diff --git a/.gitignore b/.gitignore index 77c9c744e2..93230a7478 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ Makefile .cproject .project .settings +.idea +.vscode *.user *.user.* *.o @@ -16,6 +18,9 @@ ui_*.h moc_predefs.h src/res/qrc_resources.cpp windows/ASIOSDK2 +windows/VC_redist.x64.exe +windows/vc_redist.x86.exe +windows/nsProcess.dll windows/NSIS debug/ release/ @@ -30,3 +35,4 @@ distributions/opus* distributions/jack2 distributions/claudio_piano.sf2 distributions/fluidsynth* +distributions/jamulus.desktop diff --git a/.gitmodules b/.gitmodules index 3f550c737e..279ef8e356 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,21 @@ [submodule "libs/oboe"] path = libs/oboe url = https://github.com/google/oboe.git +[submodule "tools/jamulus-php"] + path = tools/jamulus-php + url = https://github.com/softins/jamulus-php +[submodule "tools/jamulus-web"] + path = tools/jamulus-web + url = https://github.com/softins/jamulus-web +[submodule "tools/jamulus-wireshark"] + path = tools/jamulus-wireshark + url = https://github.com/softins/jamulus-wireshark +[submodule "tools/jamulus-historytool"] + path = tools/jamulus-historytool + url = https://github.com/pljones/jamulus-historytool +[submodule "tools/jamulus-jamexporter"] + path = tools/jamulus-jamexporter + url = https://github.com/pljones/jamulus-jamexporter +[submodule "tools/jamulus-docker"] + path = tools/jamulus-docker + url = https://github.com/grundic/jamulus-docker diff --git a/.travis.yml b/.travis.yml index d60f7c082e..51262e83df 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: cpp matrix: include: - os: linux - dist: xenial + dist: trusty before_install: - sudo apt-get update -qq - sudo apt-get install devscripts build-essential lintian dh-make @@ -12,6 +12,10 @@ matrix: - gcc script: - echo $TRAVIS_TAG + # headless server compilation + - qmake "CONFIG+=headless nosound" Jamulus.pro + - make + # normal compilation - qmake Jamulus.pro - make #deploy function is only available in travis-ci.com (not free) but not in travis-ci.org (free) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..bb07fad668 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,10 @@ +You want to contribute? We really appreciate that. +Please have a look at this list to facilitate your contribution: + +* Before you start coding, create a Github issue at https://github.com/corrados/jamulus/issues. In that new issue write your specification and that you are planning to do the coding. Then we can discuss the specification. Only start coding after we agreed to a specification. +* If the Github issue for your feature/bug fix already exists, then write a message in that issue that you want to work on it. +* Respect the existing code style: Tab size=4, insert spaces. +* Make sure (if possible) that your code compiles on Windows/Mac/Linux. +* Do not use diff/patch to send your code changes but create a Github fork of the Jamulus code and create a Pull Request when you are done. + +Look here for other further guidelines: https://github.com/corrados/jamulus/issues/596 \ No newline at end of file diff --git a/ChangeLog b/ChangeLog index 28fe556dc5..fb56e39629 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,17 +1,269 @@ +3.6.2git <- NOTE: the release version number will be 3.6.3 -3.5.6git +3.6.2 (2020-12-12) +- change Clear All Stored Solo Settings to clear Mute as well (#731) + +- avoid selecting IPv6 results from hostname lookup, coded by jarmar (#722) + +- added possibility to set MIDI offset for fader control to --ctrlmidich (#95) + +- detect if no audio Device is selected before trying to connect a server (#129) + +- on MacOS if an audio device is no longer available, show a warning + rather than switching to default automatically (#727) + +- bug fix: sliders move by themselves if fader groups are used on reconnect (#611) + +- bug fix: do not reset sound card channel selection on a device property change (#727) + +- bug fix: compiling Jamulus 3.6.1 is failing on Debian 9 Linode (#736) + +- bug fix: on MacOS Jamulus does not always select the previous sound card (#680) + +- bug fix: use new server icon on Mac server bundle and Windows installer (#737) + +- bug fix: ping times of servers which are further down the server list are too high (#49) + + +3.6.1 (2020-11-21) + +- added menu entry "Set All Faders to New Client Level" (#622) + +- isolate a channel from the group temporarily with shift-click-drag (#695) + +- on shift-click the pan reset to 0 L/R (#707) + +- support multiple custom central server addresses (#698) + +- the Jamulus server now has a different icon, created by geheimerEichkater (#700) + +- support two rows for the mixer panel (#720) + +- changed RYG indicator lights with colour-blind compensation, created by geheimerEichkater (#57) + +- saving and loading mixer settings is now possible during an active connection and a + mixer settings file can be loaded with drag'n'drop (#706) + +- menu entry Clear All Stored Solo Settings is now enabled during an active connection + +- bug fix: the fader group property was not correctly loaded from the ini file + + +3.6.0 (2020-10-25) + +- handle audio packets received out of order, coded by softins (#619) + +- most recently connected users appear on the RH side on the fader panel (#673) + +- improvements for the server multithreading, coded by kraney (#653) + +- removed Display Channel Levels setting and --servername (#638) + +- removed support for further server infos in --serverinfo since the preferred + way of registering a server is to do it using the protocol messages (#638) + +- removed -g, --pingservers since all Central servers must activate this function, + now it is activated by default and no command line argument is needed (#638) + +- added --mutemyown command line argument to mute my own signal in my personal mix, + only supported in headless client mode (#340) + +- added "Mountain Dulcimer" instrument icon, created by dora71 (#649) + +- added new instrument icons for "Scratching" and "Rapping" + +- replaced double types by floats for some of the signal processing, coded by hselasky (#544) + +- support permanent channel fader sorting (i.e., not only on request but always) (#666) + +- support sorting faders by channel city + +- if sorting the faders by instrument, we now sort by the name for the same instruments + +- improvements to the Android audio interface, coded by j-santander (#83) + +- bug fix: reduced server list is displayed instead of the normal list (#657) + + +3.5.12 (2020-10-03) + +- added hyperlink support for the chat window, coded by jc-Rosichini (#591) + +- added new menu entry "Clear All Stored Solo Settings" (#616) + +- fade in all clients at the server when entering a server to avoid the volume + is at 100% when joining a server (#628) + +- added a qmake CONFIG flag for disabling the automatic version check (#370) + +- avoid confusion with the Server Address field on the connection setup window by + no longer showing the server name in that field since it is only intended for + entering IP addresses or valid server URLs (#365) + +- removed the "Show Creative Commons BY-NC-SA 4.0 Licence Dialog" setting from the + server GUI and changed the -L/--licence text in the licence dialog to "Do you agree + to the text in the chat window?" so that a licence text must now be given in the + server welcome message (#587, #367, #81) + +- added a protocol message for a reduced server list to improve the situation caused + by UDP packet fragmentation (#631, #255) + +- added translation: Slovak by jose1711 (#635) + +- bug fix: crash when using the jam recorder in the server, coded by pljones (#618) + + +3.5.11 (2020-09-19) + +- support a check for updates (#370) + +- added an optional server list whitelist filter (#413) + +- added a command line argument to enable multithreading in the server (#339) + +- added support for split protocol messages (fixes bug with large number of clients + connected to a server, #547) + +- store recorder settings, coded by pljones (#313) + +- added a command line argument to disable recording on start up, coded by pljones (#574) + +- accessibility improvements, coded by chigkim (#498, #512) + +- added Jack audio latency calculation, coded by bflamig (#437) + +- show the server name in the title bar (#559) + +- bug fix: crash when using the Jack backend and quickly reconfiguring, coded by hselasky (#543) + +- bug fix: Alt+h shortcut to open the Chat dialog did not work, use Alt+c instead + +- bug fix: pan is not correctly initialized in the server on a new connection (#537) + + +3.5.10 (2020-08-16) + +- do not change the server list order if the mouse is over the table to + avoid selecting an incorrect server on a mouse double click (#293) + +- if network name/address contains spaces, they are removed now, + coded by dingodoppelt (#462) + +- improve Compact skin by using smaller font size + +- improve server audio mix processing for better clipping behavior + +- support MIDI control faders in headless build (#483) + +- option to set Mute Myself on with a command line argument (#495) + +- added a red message to indicate that Mute Myself is activated (#476) + +- manual clip LED reset by mouse click on the level meter (#421) + +- replacing internal history graph functionality by external scripts/tools (#501) + +- accessibility improvements, coded by chigkim (#499, #510, #514) + +- bug fix: fixed a stability issue in the server + +- bug fix: --showallservers ping column sort is alphabetic (#201) + + +3.5.9 (2020-07-19) + +- new app icon for Jamulus, created by geheimerEichkater (#410) + +- support up to four different groups for the channels (#202) + +- support sorting faders by channel group + +- add support to change the GUI language (#297) + +- add special server list filter for filtering occupied servers by using "#" (#397) + +- update server UI to allow setting the jam recorder directory (like -R) (#228, #405) + +- redesign of the server dialog (e.g. added welcome message setting) + +- save and restore mixer state (like fader, mute, etc.) (#377), + note that saving/loading of settings only works if not connected + +- scale channel instrument picture in Compact skin mode + +- show maximum number of clients for servers in the serverlist, coded by dingodoppelt (#451) + +- log the number of connected clients on each new connection (#277) + +- move the Mute Myself button up to prevent accidentally disconnecting + +- bug fix: grouping faders in the client should be proportional (see discussion in #202, #419) + + +3.5.8 (2020-06-30) + +- bug fix: incorrect selection of UI language (#408) + + +3.5.7 (2020-06-28) + +- add new "compact" skin, intended for large ensembles (#339) + +- support sorting faders by channel instrument, coded by Alberstein8 (#356) + +- new group switch to change several faders in sync, coded by Alberstein8 (#202, #379) + +- support a clip LED, coded by fleutot (#220) + +- add server recording indicator, coded by pljones (#295) + +- support for storing/recovering the server window positions (#357) + +- add a headless build type which does not depend on QtGui/QtWidgets, coded by marcan (#322) + +- the local pan middle position is no longer attenuated in Mono-in/Stereo-out mode (#353) + +- added translation: Brazilian Portuguese by melcon (#372) + +- add send button to chat window (#384) + +- add some protections to the code, coded by atsampson (#380, #381, #382) + +- bug fix: server window stop updating after minimized, coded by AronVietti (#355, #383) + + +3.5.6 (2020-06-09) + +- support sorting faders by channel name (#178) + +- enable/disable recording from command line, coded by pljones (#228) + +- add Audacity "list of files" writer to jam recorder, by pljones (#315) + +- make level meter LED black when off, by fleutot (#318) + +- added ukulele/bass ukulele instrument icons created by dos1 (#319) + +- avoid showing IP address if no name in the musician profile is given (#316) + +- show channel numbers if --ctrlmidich is used (#241, #95) + +- added check in acknowledge message, coded by atsampson (#302) + +- bug fix: on MacOS declare an activity to ensure the process doesn't get throttled + by OS level Nap, Sleep, and Thread Priority systems, coded by AronVietti (#23) 3.5.5 (2020-05-26) -- added vocal banjo/mandolin instrument icons created by atsampson (#283) +- added banjo/mandolin instrument icons created by atsampson (#283) - faster update of musicians list in the server list table @@ -69,7 +321,7 @@ - new command line option -d to disconnect all clients on shutdown of the server (#161) -- bug fix: for mono capture jack audio interface Jamulus complains it +- bug fix: for mono capture Jack audio interface Jamulus complains it cannot make connections (#137) - bug fix: fixed that Jamulus segfaults when jackd is restarted (#122, #127) @@ -441,7 +693,7 @@ - show number of connected clients in window title (and therefore in OS task bar) -- added "Whats this" help text to the GUI controls in the general settings +- added "What's this" help text to the GUI controls in the general settings dialog, added Tool Tips to some GUI controls - server logging history grid lines of weekend days are now plotted with diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100755 index d8999a7844..0000000000 --- a/INSTALL.md +++ /dev/null @@ -1,11 +0,0 @@ - -Installing Jamulus -============================ - -[Please see this overview](https://github.com/corrados/jamulus/wiki/Getting-Started) containing instructions for installing and using Jamulus for your platform. - - -Compiling Jamulus -============================ - -[Please see these instructions](https://github.com/corrados/jamulus/wiki/Compiling) diff --git a/Jamulus.pro b/Jamulus.pro index 60da17c283..db1e8afa0c 100755 --- a/Jamulus.pro +++ b/Jamulus.pro @@ -1,4 +1,4 @@ -VERSION = 3.5.6git +VERSION = 3.6.2git # use target name which does not use a captital letter at the beginning contains(CONFIG, "noupcasename") { @@ -10,16 +10,27 @@ CONFIG += qt \ thread \ release -QT += widgets \ - network \ - xml +QT += network \ + xml \ + concurrent + +contains(CONFIG, "headless") { + message(Headless mode activated.) + QT -= gui +} else { + QT += widgets +} 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_pt_BR.ts \ src/res/translation/translation_es_ES.ts \ src/res/translation/translation_nl_NL.ts \ - src/res/translation/translation_it_IT.ts + src/res/translation/translation_pl_PL.ts \ + src/res/translation/translation_sk_SK.ts \ + src/res/translation/translation_it_IT.ts \ + src/res/translation/translation_sv_SE.ts INCLUDEPATH += src @@ -86,12 +97,18 @@ win32 { DEFINES += SERVER_BUNDLE TARGET = $${TARGET}Server + MACOSX_BUNDLE_ICON_FILE = jamulus-server-icon-2020.icns + RC_FILE = mac/jamulus-server-icon-2020.icns + } else { + MACOSX_BUNDLE_ICON_FILE = mainicon.icns + RC_FILE = mac/mainicon.icns } QT += macextras HEADERS += mac/sound.h SOURCES += mac/sound.cpp - RC_FILE = mac/mainicon.icns + HEADERS += mac/activity.h + OBJECTIVE_SOURCES += mac/activity.mm CONFIG += x86 QMAKE_TARGET_BUNDLE_PREFIX = net.sourceforge.llcon QMAKE_APPLICATION_BUNDLE_NAME. = $$TARGET @@ -107,7 +124,8 @@ win32 { -framework CoreAudio \ -framework CoreMIDI \ -framework AudioToolbox \ - -framework AudioUnit + -framework AudioUnit \ + -framework Foundation # replace coreaudio with jack if requested contains(CONFIG, "jackonmac") { @@ -140,7 +158,9 @@ win32 { target.path = /tmp/your_executable # path on device INSTALLS += target - HEADERS += android/sound.h + HEADERS += android/sound.h \ + android/ring_buffer.h + SOURCES += android/sound.cpp \ android/androiddebug.cpp @@ -172,10 +192,12 @@ win32 { libs/oboe/src/fifo/FifoController.cpp \ libs/oboe/src/fifo/FifoControllerBase.cpp \ libs/oboe/src/fifo/FifoControllerIndirect.cpp \ + libs/oboe/src/flowgraph/ChannelCountConverter.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/MultiToMonoConverter.cpp \ libs/oboe/src/flowgraph/RampLinear.cpp \ libs/oboe/src/flowgraph/SampleRateConverter.cpp \ libs/oboe/src/flowgraph/SinkFloat.cpp \ @@ -219,10 +241,12 @@ win32 { libs/oboe/src/fifo/FifoController.h \ libs/oboe/src/fifo/FifoControllerBase.h \ libs/oboe/src/fifo/FifoControllerIndirect.h \ + libs/oboe/src/flowgraph/ChannelCountConverter.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/MultiToMonoConverter.h \ libs/oboe/src/flowgraph/RampLinear.h \ libs/oboe/src/flowgraph/SampleRateConverter.h \ libs/oboe/src/flowgraph/SinkFloat.h \ @@ -264,6 +288,9 @@ win32 { # we want to compile with C++11 CONFIG += c++11 + HEADERS += linux/sound.h + SOURCES += linux/sound.cpp + # we assume to have lrintf() one moderately modern linux distributions # would be better to have that tested, though DEFINES += HAVE_LRINTF @@ -272,8 +299,7 @@ win32 { DEFINES += HAVE_STDINT_H # only include jack support if CONFIG nosound is not set - nosoundoption = $$find(CONFIG, "nosound") - count(nosoundoption, 0) { + !contains(CONFIG, "nosound") { message(Jack Audio Interface Enabled.) contains(CONFIG, "raspijamulus") { @@ -284,45 +310,58 @@ win32 { PKGCONFIG += jack } - HEADERS += linux/sound.h - SOURCES += linux/sound.cpp DEFINES += WITH_SOUND } isEmpty(PREFIX) { PREFIX = /usr/local } + isEmpty(BINDIR) { BINDIR = bin } BINDIR = $$absolute_path($$BINDIR, $$PREFIX) - INSTALLS += target target.path = $$BINDIR + + contains(CONFIG, "headless") { + INSTALLS += target + } else { + isEmpty(APPSDIR) { + APPSDIR = share/applications + } + APPSDIR = $$absolute_path($$APPSDIR, $$PREFIX) + desktop.path = $$APPSDIR + QMAKE_SUBSTITUTES += distributions/jamulus.desktop.in + desktop.files = distributions/jamulus.desktop + + isEmpty(ICONSDIR) { + ICONSDIR = share/icons/hicolor/512x512/apps + } + ICONSDIR = $$absolute_path($$ICONSDIR, $$PREFIX) + icons.path = $$ICONSDIR + icons.files = distributions/jamulus.png + + INSTALLS += target desktop icons + } } RCC_DIR = src/res RESOURCES += src/resources.qrc -FORMS += src/clientdlgbase.ui \ +FORMS_GUI = src/clientdlgbase.ui \ src/serverdlgbase.ui \ src/clientsettingsdlgbase.ui \ src/chatdlgbase.ui \ src/connectdlgbase.ui \ src/aboutdlgbase.ui -HEADERS += src/audiomixerboard.h \ - src/buffer.h \ +HEADERS += src/buffer.h \ src/channel.h \ - src/chatdlg.h \ src/client.h \ - src/clientsettingsdlg.h \ - src/connectdlg.h \ src/global.h \ - src/clientdlg.h \ - src/serverdlg.h \ src/multicolorled.h \ - src/multicolorledbar.h \ src/protocol.h \ + src/recorder/jamcontroller.h \ src/server.h \ src/serverlist.h \ src/serverlogging.h \ @@ -331,13 +370,20 @@ HEADERS += src/audiomixerboard.h \ src/soundbase.h \ src/testbench.h \ src/util.h \ - src/analyzerconsole.h \ src/recorder/jamrecorder.h \ src/recorder/creaperproject.h \ src/recorder/cwavestream.h \ - src/historygraph.h \ src/signalhandler.h +HEADERS_GUI = src/audiomixerboard.h \ + src/chatdlg.h \ + src/clientsettingsdlg.h \ + src/connectdlg.h \ + src/clientdlg.h \ + src/serverdlg.h \ + src/levelmeter.h \ + src/analyzerconsole.h + HEADERS_OPUS = libs/opus/celt/arch.h \ libs/opus/celt/bands.h \ libs/opus/celt/celt.h \ @@ -409,19 +455,12 @@ HEADERS_OPUS_X86 = libs/opus/celt/x86/celt_lpc_sse.h \ libs/opus/celt/x86/vq_sse.h \ libs/opus/celt/x86/x86cpu.h -SOURCES += src/audiomixerboard.cpp \ - src/buffer.cpp \ +SOURCES += src/buffer.cpp \ src/channel.cpp \ - src/chatdlg.cpp \ src/client.cpp \ - src/clientsettingsdlg.cpp \ - src/connectdlg.cpp \ - src/clientdlg.cpp \ - src/serverdlg.cpp \ src/main.cpp \ - src/multicolorled.cpp \ - src/multicolorledbar.cpp \ src/protocol.cpp \ + src/recorder/jamcontroller.cpp \ src/server.cpp \ src/serverlist.cpp \ src/serverlogging.cpp \ @@ -430,11 +469,19 @@ SOURCES += src/audiomixerboard.cpp \ src/socket.cpp \ src/soundbase.cpp \ src/util.cpp \ - src/analyzerconsole.cpp \ src/recorder/jamrecorder.cpp \ src/recorder/creaperproject.cpp \ - src/recorder/cwavestream.cpp \ - src/historygraph.cpp + src/recorder/cwavestream.cpp + +SOURCES_GUI = src/audiomixerboard.cpp \ + src/chatdlg.cpp \ + src/clientsettingsdlg.cpp \ + src/connectdlg.cpp \ + src/clientdlg.cpp \ + src/serverdlg.cpp \ + src/multicolorled.cpp \ + src/levelmeter.cpp \ + src/analyzerconsole.cpp SOURCES_OPUS = libs/opus/celt/bands.c \ libs/opus/celt/celt.c \ @@ -600,14 +647,20 @@ android { DISTFILES += ChangeLog \ COPYING \ - INSTALL.md \ + CONTRIBUTING.md \ README.md \ + distributions/jamulus.desktop.in \ + distributions/jamulus.png \ src/res/translation/translation_de_DE.qm \ src/res/translation/translation_fr_FR.qm \ src/res/translation/translation_pt_PT.qm \ + src/res/translation/translation_pt_BR.qm \ src/res/translation/translation_es_ES.qm \ src/res/translation/translation_nl_NL.qm \ + src/res/translation/translation_pl_PL.qm \ src/res/translation/translation_it_IT.qm \ + src/res/translation/translation_sv_SE.qm \ + src/res/translation/translation_sk_SK.qm \ src/res/CLEDBlack.png \ src/res/CLEDBlackSmall.png \ src/res/CLEDDisabledSmall.png \ @@ -621,13 +674,16 @@ DISTFILES += ChangeLog \ src/res/CLEDRedSmall.png \ src/res/CLEDYellow.png \ src/res/CLEDYellowSmall.png \ + src/res/IndicatorGreen.png \ + src/res/IndicatorYellow.png \ + src/res/IndicatorRed.png \ src/res/faderbackground.png \ src/res/faderhandle.png \ src/res/faderhandlesmall.png \ src/res/HLEDGreen.png \ src/res/HLEDGreenSmall.png \ - src/res/HLEDGrey.png \ - src/res/HLEDGreySmall.png \ + src/res/HLEDBlack.png \ + src/res/HLEDBlackSmall.png \ src/res/HLEDRed.png \ src/res/HLEDRedSmall.png \ src/res/HLEDYellow.png \ @@ -635,29 +691,8 @@ DISTFILES += ChangeLog \ src/res/ledbuttonnotpressed.png \ src/res/ledbuttonpressed.png \ src/res/fronticon.png \ - src/res/mainicon.png \ + src/res/fronticonserver.png \ src/res/mixerboardbackground.png \ - src/res/VLEDBlack.png \ - src/res/VLEDBlackSmall.png \ - src/res/VLEDDisabledSmall.png \ - src/res/VLEDGreen.png \ - src/res/VLEDGreenSmall.png \ - src/res/VLEDGrey.png \ - src/res/VLEDGreySmall.png \ - src/res/VLEDRed.png \ - src/res/VLEDRedSmall.png \ - src/res/VLEDYellow.png \ - src/res/VLEDYellowSmall.png \ - src/res/VRLEDBlack.png \ - src/res/VRLEDBlackSmall.png \ - src/res/VRLEDGreen.png \ - src/res/VRLEDGreenSmall.png \ - src/res/VRLEDGrey.png \ - src/res/VRLEDGreySmall.png \ - src/res/VRLEDRed.png \ - src/res/VRLEDRedSmall.png \ - src/res/VRLEDYellow.png \ - src/res/VRLEDYellowSmall.png \ src/res/instruments/accordeon.png \ src/res/instruments/aguitar.png \ src/res/instruments/bassguitar.png \ @@ -674,9 +709,12 @@ DISTFILES += ChangeLog \ src/res/instruments/keyboard.png \ src/res/instruments/listener.png \ src/res/instruments/microphone.png \ + src/res/instruments/mountaindulcimer.png \ src/res/instruments/none.png \ + src/res/instruments/rapping.png \ src/res/instruments/recorder.png \ src/res/instruments/saxophone.png \ + src/res/instruments/scratching.png \ src/res/instruments/streamer.png \ src/res/instruments/synthesizer.png \ src/res/instruments/trombone.png \ @@ -699,10 +737,16 @@ DISTFILES += ChangeLog \ src/res/instruments/congas.png \ src/res/instruments/bongo.svg \ src/res/instruments/bongo.png \ + src/res/instruments/ukulele.svg \ + src/res/instruments/ukulele.png \ + src/res/instruments/bassukulele.svg \ + src/res/instruments/bassukulele.png \ src/res/instruments/vocalbass.png \ src/res/instruments/vocaltenor.png \ src/res/instruments/vocalalto.png \ src/res/instruments/vocalsoprano.png \ + src/res/instruments/vocalbaritone.png \ + src/res/instruments/vocallead.png \ src/res/instruments/banjo.png \ src/res/instruments/mandolin.png \ src/res/flags/flagnone.png \ @@ -958,6 +1002,14 @@ DISTFILES_OPUS += libs/opus/AUTHORS \ libs/opus/celt/arm/armopts.s.in \ libs/opus/celt/arm/celt_pitch_xcorr_arm.s \ +contains(CONFIG, "headless") { + DEFINES += HEADLESS +} else { + HEADERS += $$HEADERS_GUI + SOURCES += $$SOURCES_GUI + FORMS += $$FORMS_GUI +} + # use external OPUS library if requested contains(CONFIG, "opus_shared_lib") { message(OPUS codec is used from a shared library.) @@ -978,3 +1030,11 @@ contains(CONFIG, "opus_shared_lib") { SOURCES += $$SOURCES_OPUS DISTFILES += $$DISTFILES_OPUS } + +# disable version check if requested +contains(CONFIG, "disable_version_check") { + message(The version check is disabled.) + DEFINES += DISABLE_VERSION_CHECK +} + +ANDROID_ABIS = armeabi-v7a arm64-v8a x86 x86_64 diff --git a/README.md b/README.md index 43fde8b091..6b1432f286 100644 --- a/README.md +++ b/README.md @@ -16,47 +16,38 @@ __MacOS__ ([Core Audio](https://developer.apple.com/documentation/coreaudio)) an __Linux__ ([Jack](http://jackaudio.org)). It is based on the [Qt framework](https://www.qt.io) and uses the [OPUS](http://www.opus-codec.org) audio codec. -The project is hosted at [Sourceforge.net](http://sourceforge.net/projects/llcon). ![Sourceforge logo](http://sflogo.sourceforge.net/sflogo.php?group_id=158367&type=5) - -Required Hardware Setup ------------------------ - -The required minimum internet connection speed is 200 kbps (0.2Mbps) for the up and down-stream. -The ping time (i.e. round trip delay) from your computer to the server should not exceed 40 ms average. - -For the Jamulus software to run stable it is recommended to use a PC with at least 1.5 GHz CPU frequency. - -On a Windows operating system it is recommended to use a sound card with a native ASIO driver. -This ensures to get the lowest possible latencies. +The project is hosted at [Sourceforge.net](http://sourceforge.net/projects/llcon). -Download and Installation -------------------------- -Download the latest version for [Windows, Macintosh or Linux here](https://sourceforge.net/projects/llcon/files/). +Installation and Getting Started +-------------------------------- -**Windows users**: The Jamulus client software requires an ASIO sound card driver to be available in the system. -If your sound card does not have native ASIO support, you can try out [this alternative](http://www.asio4all.org/) +[Please see this overview](https://jamulus.io/wiki/Getting-Started) containing instructions for installing and using Jamulus for your platform. Help ---- -Official documentation for Jamulus is on the [Github wiki](https://github.com/corrados/jamulus/wiki) +Official documentation for Jamulus is on the [Jamulus homepage](https://jamulus.io) -See also the [discussion forums](https://sourceforge.net/p/llcon/discussion) +See also the [discussion forums](https://sourceforge.net/p/llcon/discussion). If you have issues, feel free to ask for help there. Bugs and feature requests can be [reported here](https://github.com/corrados/jamulus/issues) -Compilation and Development ---------------------------- +Compilation +----------- + +[Please see these instructions](https://jamulus.io/wiki/Compiling) + -See the [compile Instructions](INSTALL.md) +Contributing +------------ -For setting up and running a server, see [this guide](https://github.com/corrados/jamulus/wiki/Running-a-Server) +See the [contributing instructions](CONTRIBUTING.md) Acknowledgments diff --git a/android/androiddebug.cpp b/android/androiddebug.cpp index 93656a47d6..c2f08d09a5 100644 --- a/android/androiddebug.cpp +++ b/android/androiddebug.cpp @@ -1,6 +1,30 @@ -const char*const applicationName="Jamulus"; +/******************************************************************************\ + * Copyright (c) 2020 + * + * Author(s): + * Simon Tomlinson + * + ****************************************************************************** + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ -#ifdef ANDROIDDEBUG // Set in my myapp.pro file for android builds +const char*const applicationName = "Jamulus"; + +#ifdef ANDROIDDEBUG // Set in my myapp.pro file for android builds #include #include #include @@ -9,37 +33,49 @@ const char*const applicationName="Jamulus"; #include #include -void myMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) +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); + 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); + + if ( context.function && !QString ( context.function ).isEmpty() ) + { + report +=+" function "; + report += QString(context.function); } - const char*const local=report.toLocal8Bit().constData(); - switch (type) { + + const char*const local = report.toLocal8Bit().constData(); + + switch ( type ) + { case QtDebugMsg: - __android_log_write(ANDROID_LOG_DEBUG,applicationName,local); + __android_log_write ( ANDROID_LOG_DEBUG, applicationName, local) ; break; + case QtInfoMsg: - __android_log_write(ANDROID_LOG_INFO,applicationName,local); + __android_log_write ( ANDROID_LOG_INFO, applicationName, local ); break; + case QtWarningMsg: - __android_log_write(ANDROID_LOG_WARN,applicationName,local); + __android_log_write ( ANDROID_LOG_WARN, applicationName, local ); break; + case QtCriticalMsg: - __android_log_write(ANDROID_LOG_ERROR,applicationName,local); + __android_log_write ( ANDROID_LOG_ERROR, applicationName, local ); break; + case QtFatalMsg: default: - __android_log_write(ANDROID_LOG_FATAL,applicationName,local); + __android_log_write ( ANDROID_LOG_FATAL, applicationName, local ); abort(); + break; } } #endif diff --git a/android/res/drawable-hdpi/icon.png b/android/res/drawable-hdpi/icon.png index 0ccf07af81..3ba6f7222f 100644 Binary files a/android/res/drawable-hdpi/icon.png and b/android/res/drawable-hdpi/icon.png differ diff --git a/android/res/drawable-ldpi/icon.png b/android/res/drawable-ldpi/icon.png index a4feee4b65..ea9221bedf 100644 Binary files a/android/res/drawable-ldpi/icon.png and b/android/res/drawable-ldpi/icon.png differ diff --git a/android/res/drawable-mdpi/icon.png b/android/res/drawable-mdpi/icon.png index b3cb62fa77..9ea7673bd8 100644 Binary files a/android/res/drawable-mdpi/icon.png and b/android/res/drawable-mdpi/icon.png differ diff --git a/android/res/drawable-xhdpi/icon.png b/android/res/drawable-xhdpi/icon.png index 44170ddefb..33f37bdaae 100644 Binary files a/android/res/drawable-xhdpi/icon.png and b/android/res/drawable-xhdpi/icon.png differ diff --git a/android/res/drawable-xxhdpi/icon.png b/android/res/drawable-xxhdpi/icon.png index ba542bc036..873c1fc25b 100644 Binary files a/android/res/drawable-xxhdpi/icon.png and b/android/res/drawable-xxhdpi/icon.png differ diff --git a/android/res/drawable-xxxhdpi/icon.png b/android/res/drawable-xxxhdpi/icon.png index aa894729fe..43c1851e6e 100644 Binary files a/android/res/drawable-xxxhdpi/icon.png and b/android/res/drawable-xxxhdpi/icon.png differ diff --git a/android/ring_buffer.h b/android/ring_buffer.h new file mode 100644 index 0000000000..792e6812e3 --- /dev/null +++ b/android/ring_buffer.h @@ -0,0 +1,172 @@ +/******************************************************************************\ + * Copyright (c) 2004-2020 + * + * Author(s): + * Julian Santander + * + ****************************************************************************** + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#pragma once +#include + +/** + * Implementantion of a ring buffer. + * Data is contained in a vector dynamically allocated. + */ +template +class RingBuffer { +public: + /** + * @brief RingBuffer + * @param max maximum number of elements that can be contained in the ring buffer + */ + RingBuffer(std::size_t max = 0):mData(max),mRead(0),mWrite(0),mFull(false) { } + + /** + * @brief Resets the ring_buffer + * @param max maximum number of elements that can be contained in the ring buffer. + */ + void reset(std::size_t max = 0) { + mData = std::vector(max); + mRead = 0; + mWrite = 0; + mFull = false; + } + + /** + * @brief Current number of elements contained in the ring buffer + * @return + */ + std::size_t size() const { + std::size_t size = capacity(); + if(!mFull) { + if(mWrite >= mRead) { + size = mWrite - mRead; + } else { + size = capacity() + mWrite - mRead; + } + } + return size; + } + + /** + * @brief whether the ring buffer is full + * @return + */ + bool isFull() const { return mFull; } + + /** + * @brief whether the ring buffer is empty. + * @return + */ + bool isEmpty() const { return !isFull() && (mRead == mWrite); } + + /** + * @brief Maximum number of elements in the ring buffer + * @return + */ + std::size_t capacity() const { return mData.size(); } + + /** + * @brief Adds a single value + * @param v the value to add + */ + void put(const T &v) { + mData[mWrite] = v; + forward(); + } + + /** + * @brief Reads a single value + * @param v the value read + * @return true if the value was read + */ + bool get(T&v) { + if(!isEmpty()) { + v = mData[mRead]; + backward(); + return true; + } else { + return false; + } + } + + /** + * @brief Adds a multiple consecutive values + * @param v pointer to the consecutive values + * @param count number of consecutive values. + */ + void put(const T *v, std::size_t count) { + std::size_t avail = mWrite - capacity(); + std::size_t to_copy = std::min(count,avail); + memcpy(mData.data() + mWrite,v, to_copy*sizeof(T)); + forward(to_copy); + if(to_copy < count) { + put(v+to_copy,count - to_copy); + } + } + + /** + * @brief Reads multiple values + * @param v pointer to the memory wher ethe values will be written + * @param count Maximum available size in the memory area + * @return actual number of elements read. + */ + std::size_t get(T *v, std::size_t count) { + std::size_t avail = 0; + if(mRead < mWrite) { + avail = mWrite - mRead; + } else { + avail = mRead - capacity(); + } + std::size_t to_copy = std::min(count, avail); + memcpy(v, mData.data() + mRead, to_copy * sizeof(T)); + backward(to_copy); + if((size()>0)&&(count > to_copy)) { + return to_copy + get(v + to_copy, count - to_copy); + } else { + return to_copy; + } + } +private: + void forward() { + if(isFull()) { + mRead = (mRead + 1) % capacity(); + } + mWrite = (mWrite + 1) % capacity(); + mFull = (mRead == mWrite); + } + + void forward(std::size_t count) { + for(std::size_t i=0; i mData; + std::size_t mRead; /** offset to reading point */ + std::size_t mWrite; /** offset to writing point */ + bool mFull; +}; + diff --git a/android/sound.cpp b/android/sound.cpp index ae28508a4b..ffab760999 100644 --- a/android/sound.cpp +++ b/android/sound.cpp @@ -2,7 +2,7 @@ * Copyright (c) 2004-2020 * * Author(s): - * Simon Tomlinson + * Simon Tomlinson, Volker Fischer * ****************************************************************************** * @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -27,125 +27,139 @@ /* Implementation *************************************************************/ +const uint8_t CSound::RING_FACTOR = 20; + CSound::CSound ( void (*fpNewProcessCallback) ( CVector& psData, void* arg ), void* arg, - const int iCtrlMIDIChannel, + const QString& strMIDISetup, const bool , const QString& ) : - CSoundBase ( "OpenSL", true, fpNewProcessCallback, arg, iCtrlMIDIChannel ) - + CSoundBase ( "Oboe", fpNewProcessCallback, arg, strMIDISetup ) { - pSound = this; #ifdef ANDROIDDEBUG - qInstallMessageHandler(myMessageHandler); + qInstallMessageHandler(myMessageHandler); #endif } -void CSound::setupCommonStreamParams(oboe::AudioStreamBuilder *builder) +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) + builder ->setFormat(oboe::AudioFormat::Float) - ->setSharingMode(oboe::SharingMode::Shared) - ->setChannelCount(oboe::ChannelCount::Mono) - // ->setSampleRate(48000) - // ->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::Medium) - ->setPerformanceMode(oboe::PerformanceMode::None); + ->setSharingMode(oboe::SharingMode::Exclusive) + ->setChannelCount(oboe::ChannelCount::Stereo) + ->setSampleRate(SYSTEM_SAMPLE_RATE_HZ) + ->setFramesPerCallback(iOboeBufferSizeMono) + ->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::Medium) + ->setPerformanceMode(oboe::PerformanceMode::LowLatency); + return; } -void CSound::openStreams() +void CSound::closeStream ( oboe::ManagedStream& stream ) { - // 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); + 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 ) ) ); + } - warnIfNotLowLatency(mRecordingStream, "RecordStream"); - printStreamDetails(mRecordingStream); + stream.reset(); + } } -void CSound::printStreamDetails(oboe::ManagedStream &stream) + +void CSound::openStreams() { + // Setup output stream + oboe::AudioStreamBuilder inBuilder, outBuilder; - 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:: + outBuilder.setDirection ( oboe::Direction::Output ); + outBuilder.setCallback(this); + setupCommonStreamParams ( &outBuilder ); - 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 << "]"; + oboe::Result result = outBuilder.openManagedStream ( mPlayStream ); -} + if ( result != oboe::Result::OK ) + { + return; + } + mPlayStream->setBufferSizeInFrames ( iOboeBufferSizeStereo ); + + warnIfNotLowLatency ( mPlayStream, "PlayStream" ); + printStreamDetails ( mPlayStream ); + + // Setup input stream + inBuilder.setDirection ( oboe::Direction::Input ); -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." ) ); + // Only set callback for the input direction + // the output will be handled writing directly on the stream + inBuilder.setCallback(this); + setupCommonStreamParams ( &inBuilder ); + + result = inBuilder.openManagedStream ( mRecordingStream ); + + if ( result != oboe::Result::OK ) + { + closeStream ( mPlayStream ); + return; } + + mRecordingStream->setBufferSizeInFrames ( iOboeBufferSizeStereo ); + + warnIfNotLowLatency ( mRecordingStream, "RecordStream" ); + printStreamDetails ( mRecordingStream ); + printStreamDetails ( mPlayStream ); } -void CSound::closeStream(oboe::ManagedStream &stream) +void CSound::printStreamDetails ( 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(); + 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() ); + 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::warnIfNotLowLatency ( oboe::ManagedStream& stream, + QString streamName ) +{ + if ( stream->getPerformanceMode() != oboe::PerformanceMode::LowLatency ) + { + QString latencyMode = ( stream->getPerformanceMode() == oboe::PerformanceMode::None ? "None" : "Power Saving" ); } } void CSound::closeStreams() { // clean up - closeStream(mRecordingStream); - closeStream(mPlayStream); + closeStream ( mRecordingStream ); + closeStream ( mPlayStream ); } void CSound::Start() @@ -158,7 +172,6 @@ void CSound::Start() // finally start the streams so the callback begins, start with inputstream first. mRecordingStream->requestStart(); mPlayStream->requestStart(); - } void CSound::Stop() @@ -172,129 +185,191 @@ void CSound::Stop() int CSound::Init ( const int iNewPrefMonoBufferSize ) { // store buffer size - iOpenSLBufferSizeMono = 512 ; - //iNewPrefMonoBufferSize; + iOboeBufferSizeMono = iNewPrefMonoBufferSize; // 512 // init base class - CSoundBase::Init ( iOpenSLBufferSizeMono ); + CSoundBase::Init ( iOboeBufferSizeMono ); // set internal buffer size value and calculate stereo buffer size - iOpenSLBufferSizeStereo = 2 * iOpenSLBufferSizeMono; + iOboeBufferSizeStereo = 2 * iOboeBufferSizeMono; // create memory for intermediate audio buffer - vecsTmpAudioSndCrdStereo.Init ( iOpenSLBufferSizeStereo ); + vecsTmpInputAudioSndCrdStereo.Init ( iOboeBufferSizeStereo ); + mOutBuffer.reset ( iOboeBufferSizeStereo * RING_FACTOR ); -// TEST -#if ( SYSTEM_SAMPLE_RATE_HZ != 48000 ) -# error "Only a system sample rate of 48 kHz is supported by this module" -#endif -// audio interface number of channels is 1 and the sample rate -// is 16 kHz -> just copy samples and perform no filtering as a -// first simple solution -// 48 kHz / 16 kHz = factor 3 (note that the buffer size mono might -// be divisible by three, therefore we will get a lot of drop outs) -iModifiedInBufSize = iOpenSLBufferSizeMono / 3; -vecsTmpAudioInSndCrd.Init ( iModifiedInBufSize ); - - return iOpenSLBufferSizeMono; + return iOboeBufferSizeMono; } // 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) +// 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 ) { - // only process if we are running - if ( ! pSound->bRun ) - { - return oboe::DataCallbackResult::Continue; - } - - // 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) + // only process if we are running + if ( !bRun ) { - float *floatData = static_cast(audioData); + return oboe::DataCallbackResult::Continue; + } - // Zero out the incoming container array - memset(audioData, 0, sizeof(float) * numFrames * oboeStream->getChannelCount()); + if ( mStats.in_callback_calls % 1000 == 0 ) + { + mStats.log(); + } - // 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 + if ( oboeStream == mRecordingStream.get() && audioData ) + { + return onAudioInput(oboeStream, audioData, numFrames); + } + if ( oboeStream == mPlayStream.get() && audioData) + { + return onAudioOutput(oboeStream, audioData, numFrames); + } - // convert to 32 bit - const int32_t iCurSam = static_cast ( - pSound->vecsTmpAudioSndCrdStereo [frmNum * oboeStream->getChannelCount() + channelNum] ); - floatData[frmNum * oboeStream->getChannelCount() + channelNum] = (float) iCurSam/ _MAXSHORT; - } - } - } - else - { - // prime output stream buffer with silence - memset(static_cast(audioData) + numFrames * oboeStream->getChannelCount(), 0, - (numFrames) * oboeStream->getBytesPerFrame()); - } + return oboe::DataCallbackResult::Continue; +} + +oboe::DataCallbackResult CSound::onAudioInput ( oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames ) +{ + mStats.in_callback_calls++; + + // First things first, we need to discard the input queue a little for 500ms or so + if ( mCountCallbacksToDrain > 0 ) + { + // discard the input buffer + int32_t numBytes = numFrames * oboeStream->getBytesPerFrame(); + + memset ( audioData, 0 /* value */, numBytes ); + + mCountCallbacksToDrain--; } - else if (oboeStream == pSound->mRecordingStream.get() && audioData) + + // We're good to start recording now + // Take the data from the recording device output buffer and move + // it to the vector ready to send up to the server + float* floatData = static_cast ( audioData ); + + // Copy recording data to internal vector + for ( int frmNum = 0; frmNum < numFrames; ++frmNum ) { - // First things first, we need to discard the input queue a little for 500ms or so - if (pSound->mCountCallbacksToDrain > 0) + for ( int channelNum = 0; channelNum < oboeStream->getChannelCount(); channelNum++ ) { - // discard the input buffer - int32_t numBytes = numFrames * oboeStream->getBytesPerFrame(); - memset(audioData, 0 /* value */, numBytes); - pSound->mCountCallbacksToDrain--; + vecsTmpInputAudioSndCrdStereo[frmNum * oboeStream->getChannelCount() + channelNum] = + static_cast(floatData[frmNum * oboeStream->getChannelCount() + channelNum] * _MAXSHORT); } + } + + if ( numFrames != iOboeBufferSizeMono ) + { + qDebug() << "Received " << numFrames << " expecting " << iOboeBufferSizeMono; + } + + mStats.frames_in += numFrames; + + // Tell parent class that we've put some data ready to send to the server + ProcessCallback ( vecsTmpInputAudioSndCrdStereo ); + + // The callback has placed in the vector the samples to play + addOutputData(oboeStream->getChannelCount()); + + return oboe::DataCallbackResult::Continue; +} + +void CSound::addOutputData(int channel_count) +{ + QMutexLocker locker ( &MutexAudioProcessCallback ); - // 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 + // Only copy data if we have data to copy, otherwise fill with silence + if ( !vecsTmpInputAudioSndCrdStereo.empty() ) + { + for ( int frmNum = 0; frmNum < iOboeBufferSizeMono; ++frmNum ) + { + for ( int channelNum = 0; channelNum < channel_count; channelNum++ ) + { + // copy sample received from server into output buffer - float *floatData = static_cast(audioData); + // convert to 32 bit + const int32_t iCurSam = static_cast ( + vecsTmpInputAudioSndCrdStereo[frmNum * channel_count+ channelNum] ); - // Copy recording data to internal vector - for (int frmNum = 0; frmNum < numFrames; ++frmNum) + mOutBuffer.put ( ( static_cast ( iCurSam ) ) / _MAXSHORT ); + } + } + } + else + { + // prime output stream buffer with silence + for ( int frmNum = 0; frmNum < iOboeBufferSizeMono; ++frmNum ) { - for (int channelNum = 0; channelNum < oboeStream->getChannelCount(); channelNum++) + for ( int channelNum = 0; channelNum < channel_count; channelNum++ ) { - pSound->vecsTmpAudioSndCrdStereo [frmNum * oboeStream->getChannelCount() + channelNum] = - (short) floatData[frmNum * oboeStream->getChannelCount() + channelNum] * _MAXSHORT; + mOutBuffer.put ( 0 ); } } + } + + if ( mOutBuffer.isFull() ) + { + mStats.ring_overrun++; + } +} - // Tell parent class that we've put some data ready to send to the server - pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo ); +oboe::DataCallbackResult CSound::onAudioOutput ( oboe::AudioStream* oboeStream, + void* audioData, + int32_t numFrames ) +{ + mStats.frames_out += numFrames; + mStats.out_callback_calls++; + + QMutexLocker locker ( &MutexAudioProcessCallback ); + + std::size_t to_write = numFrames*oboeStream->getChannelCount(); + std::size_t count = std::min ( mOutBuffer.size(), to_write ); + + mOutBuffer.get ( (float*) audioData, count ); + + if ( to_write > count ) + { + mStats.frames_filled_out += ( to_write - count ); + memset ( ( (float*) audioData ) + count, 0, ( to_write - count ) * sizeof ( float ) ); } - // locker.unlock(); + return oboe::DataCallbackResult::Continue; } -//TODO better handling of stream closing errors -void CSound::onErrorAfterClose(oboe::AudioStream *oboeStream, oboe::Result result) +// 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) +// TODO better handling of stream closing errors +void CSound::onErrorBeforeClose ( oboe::AudioStream* oboeStream, + oboe::Result result ) { - qDebug() << "CSound::onErrorBeforeClose"; + qDebug() << "CSound::onErrorBeforeClose"; } +void CSound::Stats::reset() +{ + frames_in = 0; + frames_out = 0; + frames_filled_out = 0; + in_callback_calls = 0; + out_callback_calls = 0; + ring_overrun = 0; +} - +void CSound::Stats::log() const +{ + qDebug() << "Stats: " + << "frames_in: " << frames_in + << ",frames_out: " << frames_out + << ",frames_filled_out: " << frames_filled_out + << ",in_callback_calls: " << in_callback_calls + << ",out_callback_calls: " << out_callback_calls + << ",ring_overrun: " << ring_overrun; +} diff --git a/android/sound.h b/android/sound.h index 535694b7d8..668395c4ca 100644 --- a/android/sound.h +++ b/android/sound.h @@ -2,7 +2,7 @@ * Copyright (c) 2004-2020 * * Author(s): - * Simon Tomlinson + * Simon Tomlinson, Volker Fischer * ****************************************************************************** * @@ -18,29 +18,28 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ #pragma once -/* Deprecated, moving to OBOE - * #include - * #include */ #include -#include #include "soundbase.h" #include "global.h" #include #include +#include "ring_buffer.h" +#include /* Classes ********************************************************************/ -class CSound : public CSoundBase, public oboe::AudioStreamCallback//, public IRenderableAudio, public IRestartable +class CSound : public CSoundBase, public oboe::AudioStreamCallback { public: + static const uint8_t RING_FACTOR; CSound ( void (*fpNewProcessCallback) ( CVector& psData, void* arg ), void* arg, - const int iCtrlMIDIChannel, + const QString& strMIDISetup, const bool , const QString& ); virtual ~CSound() {} @@ -50,56 +49,48 @@ class CSound : public CSoundBase, public oboe::AudioStreamCallback//, public IRe 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); + 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 vecsTmpAudioSndCrdStereo; - - static void android_message_handler(QtMsgType type, - const QMessageLogContext &context, - const QString &message) + struct Stats { - 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)); + Stats() { reset(); } + void reset(); + void log() const; + std::size_t frames_in; + std::size_t frames_out; + std::size_t frames_filled_out; + std::size_t in_callback_calls; + std::size_t out_callback_calls; + std::size_t ring_overrun; }; -// TEST -CVector vecsTmpAudioInSndCrd; -int iModifiedInBufSize; - - int iOpenSLBufferSizeMono; - int iOpenSLBufferSizeStereo; +protected: + CVector vecsTmpInputAudioSndCrdStereo; + RingBuffer mOutBuffer; + int iOboeBufferSizeMono; + int iOboeBufferSizeStereo; private: - void setupCommonStreamParams(oboe::AudioStreamBuilder *builder); - void printStreamDetails(oboe::ManagedStream &stream); + 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 warnIfNotLowLatency ( oboe::ManagedStream& stream, QString streamName ); + void closeStream ( oboe::ManagedStream& stream ); + + oboe::DataCallbackResult onAudioInput ( oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames ); + oboe::DataCallbackResult onAudioOutput ( oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames ); - oboe::ManagedStream mRecordingStream; - oboe::ManagedStream mPlayStream; - AudioStreamCallback *mCallback; + void addOutputData(int channel_count); + + oboe::ManagedStream mRecordingStream; + oboe::ManagedStream mPlayStream; // 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; + static constexpr int32_t kNumCallbacksToDrain = 10; int32_t mCountCallbacksToDrain = kNumCallbacksToDrain; - - // Used to reference this instance of class from within the static callback - CSound *pSound; - - QMutex Mutex; - + Stats mStats; }; diff --git a/distributions/build-debian-package.sh b/distributions/build-debian-package.sh index 4bbb190138..98e6f4c363 100755 --- a/distributions/build-debian-package.sh +++ b/distributions/build-debian-package.sh @@ -1,16 +1,19 @@ -#!/bin/bash -red="\e[91m" -default="\e[39m" -echo Today \(2019-05-27\) it is maybe best to build the .deb it on Ubuntu 16.04 -echo Since there are no versions specified of the libraries it will takte current -echo so it would also run on Ubuntu 17,18,19 or Debian 9/10 -echo -e ${red}press a [KEY] to continue or [CTRL]-C to abort${default} -read -n 1 +#!/bin/sh -e -sudo apt-get install devscripts build-essential lintian dh-make -sudo apt-get install qtdeclarative5-dev qt5-default libjack-jackd2-dev +red="\033[91m" +default="\033[39m" + +echo It can be preferential to build the binary packages on a Ubuntu 16.04 +echo system since there are no specific library version dependencies.The +echo resulting packages will run on Ubuntu 17/18/19/20 or Debian 9/10. +echo +echo ${red}Press [ENTER] to continue or [CTRL]-C to abort${default} +read dummy + +sudo apt-get install devscripts build-essential \ + debhelper libjack-jackd2-dev qtbase5-dev qttools5-dev-tools mv debian .. cd .. -debuild -us -uc +debuild -b -us -uc mv debian distributions diff --git a/distributions/debian/changelog b/distributions/debian/changelog index efa48b11ab..1d240b8cb7 100644 --- a/distributions/debian/changelog +++ b/distributions/debian/changelog @@ -1,4 +1,4 @@ -jamulus (3.5.2) UNRELEASED; urgency=medium +jamulus (3.5.10~git-0) UNRELEASED; urgency=medium * Initial release. diff --git a/distributions/debian/control b/distributions/debian/control index 9f198a9c72..7f34dc3051 100644 --- a/distributions/debian/control +++ b/distributions/debian/control @@ -4,16 +4,11 @@ Priority: optional Maintainer: "Marc Landolt jr" Build-Depends: debhelper (>= 9), - dpkg-dev, - g++, - libc6-dev | libc-dev, libjack-jackd2-dev, - make, - qt5-default, - qtdeclarative5-dev, + qtbase5-dev, qttools5-dev-tools, -Standards-Version: 3.9.5 -Homepage: http://llcon.sourceforge.net/ +Standards-Version: 3.9.7 +Homepage: https://jamulus.io Vcs-Git: git://github.com/corrados/jamulus.git Vcs-Browser: https://github.com/corrados/jamulus @@ -22,7 +17,6 @@ Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, - adduser, Description: Low latency Audio Server/Client The Jamulus software enables musicians to perform real-time jam sessions over the internet. There is one server running the Jamulus server software which @@ -30,3 +24,20 @@ Description: Low latency Audio Server/Client sends the mix back to each client. . It runs on Windows / OSX / Linux. + +Package: jamulus-headless +Architecture: any +Depends: + ${shlibs:Depends}, + ${misc:Depends}, + adduser, +Description: Low latency Audio Server (headless) + The Jamulus software enables musicians to perform real-time jam sessions over + the internet. There is one server running the Jamulus server software which + collects the audio data from each Jamulus client, mixes the audio data and + sends the mix back to each client. + . + It runs on Windows / OSX / Linux. + . + This package contains a Jamulus binary built for headless operation + (without GUI library dependencies) and a jamulus-headless systemd service. diff --git a/distributions/debian/copyright b/distributions/debian/copyright index a34612a010..9e01119192 100644 --- a/distributions/debian/copyright +++ b/distributions/debian/copyright @@ -9,8 +9,9 @@ License: GPL-2 Files: debian/* Copyright: + 2020 Tormod Volden 2020 Olivier Humbert - 2019 "Marc Landolt jr" + 2019 Marc Landolt jr License: GPL-2 License: GPL-2 diff --git a/distributions/debian/jamulus-headless.install b/distributions/debian/jamulus-headless.install new file mode 100644 index 0000000000..525a2a0c1b --- /dev/null +++ b/distributions/debian/jamulus-headless.install @@ -0,0 +1,2 @@ +usr/bin/jamulus-headless +debian/jamulus-headless.service lib/systemd/system diff --git a/distributions/debian/postinst b/distributions/debian/jamulus-headless.postinst similarity index 54% rename from distributions/debian/postinst rename to distributions/debian/jamulus-headless.postinst index 3bfa902466..2f8e31b25c 100644 --- a/distributions/debian/postinst +++ b/distributions/debian/jamulus-headless.postinst @@ -4,6 +4,6 @@ set -e # dh_sysuser can be used in newer distro releases -adduser --system --quiet jamulus +adduser --system --quiet --home /nonexistent --no-create-home jamulus #DEBHELPER# diff --git a/distributions/debian/jamulus-headless.service b/distributions/debian/jamulus-headless.service new file mode 100644 index 0000000000..919be42b17 --- /dev/null +++ b/distributions/debian/jamulus-headless.service @@ -0,0 +1,14 @@ +[Unit] +Description=Jamulus headless server +After=network.target +StartLimitIntervalSec=0 + +[Service] +Type=simple +Restart=always +RestartSec=1 +User=jamulus +ExecStart=/bin/sh -c '/usr/bin/jamulus-headless -s -n --servername $(uname -n) -l /var/log/jamulus -e jamulus.fischvolk.de -g -o "$(uname -n);;"' + +[Install] +WantedBy=multi-user.target diff --git a/distributions/debian/jamulus.install b/distributions/debian/jamulus.install new file mode 100644 index 0000000000..548d05342f --- /dev/null +++ b/distributions/debian/jamulus.install @@ -0,0 +1,3 @@ +usr/bin/jamulus +usr/share/applications/jamulus.desktop +usr/share/icons/hicolor/512x512/apps/jamulus.png diff --git a/distributions/debian/rules b/distributions/debian/rules index b98816c934..16a8428706 100755 --- a/distributions/debian/rules +++ b/distributions/debian/rules @@ -1,21 +1,24 @@ #!/usr/bin/make -f + +export QT_SELECT=qt5 + %: dh $@ override_dh_auto_configure: - qmake CONFIG+=noupcasename Jamulus.pro + mkdir -p build-gui && cd build-gui && qmake TARGET=jamulus PREFIX=/usr ../Jamulus.pro + mkdir -p build-nox && cd build-nox && qmake "CONFIG+=nosound headless" TARGET=jamulus-headless PREFIX=/usr ../Jamulus.pro override_dh_auto_build: cd src/res/translation && lrelease *.ts - dh_auto_build + cd build-gui && make + cd build-nox && make + +override_dh_auto_install: + cd build-nox && make install INSTALL_ROOT=../debian/tmp + cd build-gui && make install INSTALL_ROOT=../debian/tmp -override_dh_usrlocal: - echo $$(pwd) - mkdir -p $$(pwd)/debian/jamulus/usr/bin/ - install -D -m 0755 jamulus $$(pwd)/debian/jamulus/usr/bin/ - mkdir -p $$(pwd)/debian/jamulus/usr/share/applications/ - install -D -m 0755 $$(pwd)/distributions/jamulus.desktop $$(pwd)/debian/jamulus/usr/share/applications/ - mkdir -p $$(pwd)/debian/jamulus/usr/share/icons/hicolor/512x512/apps/ - cp $$(pwd)/distributions/jamulus.png $$(pwd)/debian/jamulus/usr/share/icons/hicolor/512x512/apps/ - mkdir -p $$(pwd)/debian/jamulus/lib/systemd/system - cp $$(pwd)/distributions/jamulus-server.service $$(pwd)/debian/jamulus/lib/systemd/system/ +override_dh_auto_clean: + rm -rf build-gui + rm -rf build-nox + dh_clean diff --git a/distributions/debian/source/format b/distributions/debian/source/format new file mode 100644 index 0000000000..163aaf8d82 --- /dev/null +++ b/distributions/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/distributions/installscripts/install4debian.sh b/distributions/installscripts/install4debian.sh new file mode 100644 index 0000000000..f12ccb0d8c --- /dev/null +++ b/distributions/installscripts/install4debian.sh @@ -0,0 +1,78 @@ + +#!/bin/sh +# set DISTRO either to "Ubuntu", "Debian" or "Fedora" +DISTRO="Debian" +LINVERSION="0.0" + +# Get Jamulus Release Name with "curl" and "grep" +R=`curl -s https://api.github.com/repos/corrados/jamulus/releases/latest | grep -oP '"tag_name": "\K(.*)(?=")'` +echo "Jamulus Installation Script for $DISTRO $LINVERSION" +echo "Release: $R" +INSTALLJAMULUS="no" +while true; do + read -p "Do you wish to install Jamulus on $DISTRO $LINVERSION? (y/n) " yn + case $yn in + [Yy]* ) + echo "Start Installation $DISTRO $LINVERSION" + echo "(1) Fetch Release $R from GitHub" + wget https://github.com/corrados/jamulus/archive/$R.tar.gz + echo "(2) Extract Source Code for Jamulus Release $R from GitHub" + tar -xvf $R.tar.gz + echo "(3) Delete ${R}.tar.gz from GitHub" + rm $R.tar.gz + echo "(4) Update Repository" + sudo apt-get update + INSTALLJAMULUS="yes" + break;; + [Nn]* ) + echo "Cancelled Jamulus Installation on $DISTRO $LINVERSION" + exit;; + * ) echo "Please answer yes or no.";; + esac +done + +# echo "Check Variable: $INSTALLJAMULUS" + +if [ "$INSTALLJAMULUS" = "yes" ]; then + echo "(5) Install Build Essentials for $DISTRO" + + if [ "$DISTRO" = "Ubuntu" ] + then + echo "Installation for $DISTRO" + sudo apt-get install cmake qmake gcc g++ + sudo apt-get install build-essential qt5-qmake qtdeclarative5-dev qt5-default qttools5-dev-tools libjack-jackd2-dev + sudo apt-get install qjackctl + if [ "$LINVERSION" = "18.4" ] + then + echo "Perform Installation Specifics for $DISTRO Version $DISTRO" + fi + + elif [ "$DISTRO" = "Debian" ] + then + sudo apt-get install build-essential qtdeclarative5-dev qt5-default qttools5-dev-tools libjack-jackd2-dev + sudo apt-get install qjackctl + elif [ "$DISTRO" = "Fedora" ] + then + sudo dnf install qt5-qtdeclarative-devel jack-audio-connection-kit-dbus jack-audio-connection-kit-devel + sudo dnf install qjackctl + fi + + echo "(6) Compile Jamulus $R" + echo "Change to Directory jamulus-$R" + cd "jamulus-$R" + # ls + qmake Jamulus.pro + make clean + make + sudo make install + echo "Compilation DONE" + cd .. + echo "(6) Delete the Source Files after Installation" + rm -R "jamulus-$R" + +else + + echo "Installation cancelled" + +fi + diff --git a/distributions/installscripts/install4fedora.sh b/distributions/installscripts/install4fedora.sh new file mode 100644 index 0000000000..4f5f87a055 --- /dev/null +++ b/distributions/installscripts/install4fedora.sh @@ -0,0 +1,78 @@ + +#!/bin/sh +# set DISTRO either to "Ubuntu", "Debian" or "Fedora" +DISTRO="Fedora" +LINVERSION="18.04" + +# Get Jamulus Release Name with "curl" and "grep" +R=`curl -s https://api.github.com/repos/corrados/jamulus/releases/latest | grep -oP '"tag_name": "\K(.*)(?=")'` +echo "Jamulus Installation Script for $DISTRO $LINVERSION" +echo "Release: $R" +INSTALLJAMULUS="no" +while true; do + read -p "Do you wish to install Jamulus on $DISTRO $LINVERSION? (y/n) " yn + case $yn in + [Yy]* ) + echo "Start Installation $DISTRO $LINVERSION" + echo "(1) Fetch Release $R from GitHub" + wget https://github.com/corrados/jamulus/archive/$R.tar.gz + echo "(2) Extract Source Code for Jamulus Release $R from GitHub" + tar -xvf $R.tar.gz + echo "(3) Delete ${R}.tar.gz from GitHub" + rm $R.tar.gz + echo "(4) Update Repository" + sudo apt-get update + INSTALLJAMULUS="yes" + break;; + [Nn]* ) + echo "Cancelled Jamulus Installation on $DISTRO $LINVERSION" + exit;; + * ) echo "Please answer yes or no.";; + esac +done + +# echo "Check Variable: $INSTALLJAMULUS" + +if [ "$INSTALLJAMULUS" = "yes" ]; then + echo "(5) Install Build Essentials for $DISTRO" + + if [ "$DISTRO" = "Ubuntu" ] + then + echo "Installation for $DISTRO" + sudo apt-get install cmake qmake gcc g++ + sudo apt-get install build-essential qt5-qmake qtdeclarative5-dev qt5-default qttools5-dev-tools libjack-jackd2-dev + sudo apt-get install qjackctl + if [ "$LINVERSION" = "18.4" ] + then + echo "Perform Installation Specifics for $DISTRO Version $DISTRO" + fi + + elif [ "$DISTRO" = "Debian" ] + then + sudo apt-get install build-essential qtdeclarative5-dev qt5-default qttools5-dev-tools libjack-jackd2-dev + sudo apt-get install qjackctl + elif [ "$DISTRO" = "Fedora" ] + then + sudo dnf install qt5-qtdeclarative-devel jack-audio-connection-kit-dbus jack-audio-connection-kit-devel + sudo dnf install qjackctl + fi + + echo "(6) Compile Jamulus $R" + echo "Change to Directory jamulus-$R" + cd "jamulus-$R" + # ls + qmake Jamulus.pro + make clean + make + sudo make install + echo "Compilation DONE" + cd .. + echo "(6) Delete the Source Files after Installation" + rm -R "jamulus-$R" + +else + + echo "Installation cancelled" + +fi + diff --git a/distributions/installscripts/install4ubuntu.sh b/distributions/installscripts/install4ubuntu.sh new file mode 100644 index 0000000000..e449b78ac2 --- /dev/null +++ b/distributions/installscripts/install4ubuntu.sh @@ -0,0 +1,78 @@ + +#!/bin/sh +# set DISTRO either to "Ubuntu", "Debian" or "Fedora" +DISTRO="Ubuntu" +LINVERSION="18.04" + +# Get Jamulus Release Name with "curl" and "grep" +R=`curl -s https://api.github.com/repos/corrados/jamulus/releases/latest | grep -oP '"tag_name": "\K(.*)(?=")'` +echo "Jamulus Installation Script for $DISTRO $LINVERSION" +echo "Release: $R" +INSTALLJAMULUS="no" +while true; do + read -p "Do you wish to install Jamulus on $DISTRO $LINVERSION? (y/n) " yn + case $yn in + [Yy]* ) + echo "Start Installation $DISTRO $LINVERSION" + echo "(1) Fetch Release $R from GitHub" + wget https://github.com/corrados/jamulus/archive/$R.tar.gz + echo "(2) Extract Source Code for Jamulus Release $R from GitHub" + tar -xvf $R.tar.gz + echo "(3) Delete ${R}.tar.gz from GitHub" + rm $R.tar.gz + echo "(4) Update Repository" + sudo apt-get update + INSTALLJAMULUS="yes" + break;; + [Nn]* ) + echo "Cancelled Jamulus Installation on $DISTRO $LINVERSION" + exit;; + * ) echo "Please answer yes or no.";; + esac +done + +# echo "Check Variable: $INSTALLJAMULUS" + +if [ "$INSTALLJAMULUS" = "yes" ]; then + echo "(5) Install Build Essentials for $DISTRO" + + if [ "$DISTRO" = "Ubuntu" ] + then + echo "Installation for $DISTRO" + sudo apt-get install cmake qmake gcc g++ + sudo apt-get install build-essential qt5-qmake qtdeclarative5-dev qt5-default qttools5-dev-tools libjack-jackd2-dev + sudo apt-get install qjackctl + if [ "$LINVERSION" = "18.4" ] + then + echo "Perform Installation Specifics for $DISTRO Version $DISTRO" + fi + + elif [ "$DISTRO" = "Debian" ] + then + sudo apt-get install build-essential qtdeclarative5-dev qt5-default qttools5-dev-tools libjack-jackd2-dev + sudo apt-get install qjackctl + elif [ "$DISTRO" = "Fedora" ] + then + sudo dnf install qt5-qtdeclarative-devel jack-audio-connection-kit-dbus jack-audio-connection-kit-devel + sudo dnf install qjackctl + fi + + echo "(6) Compile Jamulus $R" + echo "Change to Directory jamulus-$R" + cd "jamulus-$R" + # ls + qmake Jamulus.pro + make clean + make + sudo make install + echo "Compilation DONE" + cd .. + echo "(6) Delete the Source Files after Installation" + rm -R "jamulus-$R" + +else + + echo "Installation cancelled" + +fi + diff --git a/distributions/jamulus-server.service b/distributions/jamulus-server.service index 211d501f20..e3b281418c 100644 --- a/distributions/jamulus-server.service +++ b/distributions/jamulus-server.service @@ -8,7 +8,7 @@ Type=simple Restart=always RestartSec=1 User=jamulus -ExecStart=/bin/bash -c '/usr/bin/jamulus -s -n --servername $(uname -n) -l /var/log/jamulus -e jamulus.fischvolk.de -g -o "$(uname -n);;"' +ExecStart=/bin/sh -c '/usr/bin/jamulus -s -n -l /var/log/jamulus -e jamulus.fischvolk.de -g -o "$(uname -n);;"' [Install] WantedBy=multi-user.target diff --git a/distributions/jamulus.desktop b/distributions/jamulus.desktop deleted file mode 100644 index e12447c6b7..0000000000 --- a/distributions/jamulus.desktop +++ /dev/null @@ -1,12 +0,0 @@ -[Desktop Entry] -Name=Jamulus -Comment=Jam Session -Comment[fr]=Séance de bœuf -GenericName=Internet Jam Session Software -GenericName[fr]=Logiciel de séance de bœuf sur Internet -Exec=jamulus -Icon=jamulus -Terminal=false -Type=Application -Categories=AudioVideo;Audio;Mixer;Qt; -Keywords=jam;live;online;music;conference; diff --git a/distributions/jamulus.desktop.in b/distributions/jamulus.desktop.in new file mode 100644 index 0000000000..fd366bdb2b --- /dev/null +++ b/distributions/jamulus.desktop.in @@ -0,0 +1,18 @@ +[Desktop Entry] +Name=Jamulus +Comment=Jam Session +Comment[fr]=Séance de bœuf +Comment[sv]=Musikaliska jamsessioner över Internet +GenericName=Internet Jam Session Software +GenericName[fr]=Logiciel de séance de bœuf sur Internet +GenericName[es]=Software para Jam Sessions por Internet +GenericName[pt]=Software para Jam Sessions pela Internet +GenericName[nl]=Software voor jamsessies over internet +GenericName[sk]=Softvér na džemovanie cez internet +GenericName[sv]=Mjukvara för Jam Sessioner över Internet +Exec=$$TARGET +Icon=jamulus +Terminal=false +Type=Application +Categories=AudioVideo;Audio;Mixer;Qt; +Keywords=jam;live;online;music;conference; diff --git a/distributions/jamulus.png b/distributions/jamulus.png index e82702bc72..8444778cb6 100644 Binary files a/distributions/jamulus.png and b/distributions/jamulus.png differ diff --git a/distributions/raspijamulus.sh b/distributions/raspijamulus.sh index 360b2f2df5..f4a1be07c0 100755 --- a/distributions/raspijamulus.sh +++ b/distributions/raspijamulus.sh @@ -1,17 +1,7 @@ #!/bin/bash # This script is intended to setup a clean Raspberry Pi system for running Jamulus - -# Regarding the old OPUS version (#252): I just tried out the following: -# * Do not use OPUS in shared library but use the version which is included in the jamulus source code: -# instead of 80 % load I get 90 % load on my Raspberry Pi Zero -# * Do not use OPUS in shared libaray but use the version which is included in the Jamulus source code -# but try to compile in fixed-point: I get compilation errors so this is not possible right now -# * I replaced the opus-1.1 with OPUS="opus-1.3.1" in the raspijamulus.sh -> OPUS version 1.3.1 has a -# known bug with the custom interface. If I use that version as a shared libaray, I get a runtime error -# on starting Jamulus. So this is also not possible. We have to wait for the next official OPUS version. -# Therefore it is the best to keep the opus-1.1 version. -OPUS="opus-1.1" +OPUS="opus-1.3.1" NCORES=$(nproc) # install required packages @@ -32,6 +22,30 @@ else tar -xzf ${OPUS}.tar.gz rm ${OPUS}.tar.gz cd ${OPUS} + if [ ${OPUS} == "opus-1.3.1" ]; then + echo "@@ -117,13 +117,19 @@ void validate_celt_decoder(CELTDecoder *st) + #ifndef CUSTOM_MODES + celt_assert(st->mode == opus_custom_mode_create(48000, 960, NULL)); + celt_assert(st->overlap == 120); ++ celt_assert(st->end <= 21); ++#else ++/* From Section 4.3 in the spec: The normal CELT layer uses 21 of those bands, ++ though Opus Custom (see Section 6.2) may use a different number of bands ++ ++ Check if it's within the maximum number of Bark frequency bands instead */ ++ celt_assert(st->end <= 25); + #endif + celt_assert(st->channels == 1 || st->channels == 2); + celt_assert(st->stream_channels == 1 || st->stream_channels == 2); + celt_assert(st->downsample > 0); + celt_assert(st->start == 0 || st->start == 17); + celt_assert(st->start < st->end); +- celt_assert(st->end <= 21); + #ifdef OPUS_ARCHMASK + celt_assert(st->arch >= 0); + celt_assert(st->arch <= OPUS_ARCHMASK);" >> opus_patch_file.diff + patch celt/celt_decoder.c opus_patch_file.diff + fi ./configure --enable-custom-modes --enable-fixed-point make -j${NCORES} mkdir include/opus @@ -63,36 +77,17 @@ else fi fi -# optional: FluidSynth synthesizer -if [ "$1" == "opt" -o "$1" == "synth" ]; then - if [ -d "fluidsynth" ]; then - echo "The Fluidsynth directory is present, we assume it is compiled and ready to use. If not, delete the fluidsynth directory and call this script again." - else -#TODO if the normal jack package is not installed, fluidsynth compiles without jack support - wget https://github.com/FluidSynth/fluidsynth/archive/v2.0.6.tar.gz -O fluidsynth.tar.gz - tar -xzf fluidsynth.tar.gz - rm fluidsynth.tar.gz - mv fluidsynth-* fluidsynth - cd fluidsynth - mkdir build - cd build - cmake .. - make -j${NCORES} - wget https://data.musical-artifacts.com/hammersound/claudio_piano.sf2 - cd ../.. - fi -fi - # compile Jamulus with external Opus library cd .. -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 +qmake "CONFIG+=opus_shared_lib raspijamulus headless" "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 ADEVICE=$(aplay -l|grep "USB Audio"|tail -1|cut -d' ' -f3) echo "Using USB audio device: ${ADEVICE}" -# write Jamulus ini file for setting the client name and buffer settings +# write Jamulus ini file for setting the client name and buffer settings, if there is +# just one CPU core, we assume that we are running on a Raspberry Pi Zero JAMULUSINIFILE="Jamulus.ini" NAME64=$(echo "Raspi $(hostname)"|cut -c -16|tr -d $'\n'|base64) if [ "$NCORES" -gt "1" ]; then @@ -102,7 +97,7 @@ if [ "$NCORES" -gt "1" ]; then else echo -e "\n ${NAME64}" > ${JAMULUSINIFILE} echo -e " 1\n 3\n 3" >> ${JAMULUSINIFILE} - echo -e " 0\n 0\n" >> ${JAMULUSINIFILE} + echo -e " 0\n 1\n" >> ${JAMULUSINIFILE} fi # taken from "Raspberry Pi and realtime, low-latency audio" homepage at wiki.linuxaudio.org @@ -112,58 +107,9 @@ fi # start Jack2 and Jamulus in headless mode export LD_LIBRARY_PATH="distributions/${OPUS}/.libs:distributions/jack2/build:distributions/jack2/build/common" +distributions/jack2/build/jackd -R -T --silent -P70 -p16 -t2000 -d alsa -dhw:${ADEVICE} -p 128 -n 3 -r 48000 -s & +./Jamulus -n -i ${JAMULUSINIFILE} -c jamulus.fischvolk.de & -if [ "$1" == "opt" ]; then - distributions/jack2/build/jackd -R -T --silent -P70 -p16 -t2000 -d alsa -dhw:${ADEVICE} -p 256 -n 3 -r 48000 -s & - ./Jamulus -n -i ${JAMULUSINIFILE} -j -c jamulus.fischvolk.de &>/dev/null & - sleep 1 - ./distributions/fluidsynth/build/src/fluidsynth -o synth.polyphony=25 -s -i -a jack -g 0.4 distributions/fluidsynth/build/claudio_piano.sf2 &>/dev/null & - sleep 3 - ./distributions/jack2/build/example-clients/jack_connect "Jamulus:output left" system:playback_1 - ./distributions/jack2/build/example-clients/jack_connect "Jamulus:output right" system:playback_2 - ./distributions/jack2/build/example-clients/jack_connect fluidsynth:left "Jamulus:input left" - ./distributions/jack2/build/example-clients/jack_connect fluidsynth:right "Jamulus:input right" - aconnect 'USB-MIDI' 128 - - # if hyperion is installed, set red color - if [ ! -z "$(command -v hyperion-remote)" ]; then - hyperion-remote -c red - fi - - # watchdog: if MIDI device is turned off, shutdown Jamulus - while [ ! -z "$(amidi -l|grep "USB-MIDI")" ]; do - sleep 1 - done - killall Jamulus - killall fluidsynth - echo "Cleaned up jackd, Jamulus and fluidsynth" - - # if hyperion is installed, reset color - if [ ! -z "$(command -v hyperion-remote)" ]; then - hyperion-remote --color black - hyperion-remote --clearall - fi - -elif [ "$1" == "synth" ]; then - distributions/jack2/build/jackd -R -T --silent -P70 -p16 -t2000 -d alsa -dhw:${ADEVICE} -p 256 -n 3 -r 48000 -s & - ./distributions/fluidsynth/build/src/fluidsynth -o synth.polyphony=25 -s -i -a jack -g 0.4 distributions/fluidsynth/build/claudio_piano.sf2 &>/dev/null & - sleep 3 - ./distributions/jack2/build/example-clients/jack_connect fluidsynth:left system:playback_1 - ./distributions/jack2/build/example-clients/jack_connect fluidsynth:right system:playback_2 - aconnect 'USB-MIDI' 128 - - # watchdog: if MIDI device is turned off, shutdown fluidsynth - while [ ! -z "$(amidi -l|grep "USB-MIDI")" ]; do - sleep 1 - done - killall fluidsynth - echo "Cleaned up jackd and fluidsynth" - -else - distributions/jack2/build/jackd -R -T --silent -P70 -p16 -t2000 -d alsa -dhw:${ADEVICE} -p 128 -n 3 -r 48000 -s & - ./Jamulus -n -i ${JAMULUSINIFILE} -c jamulus.fischvolk.de & - echo "###---------- PRESS ANY KEY TO TERMINATE THE JAMULUS SESSION ---------###" - read -n 1 -s -r -p "" - killall Jamulus -fi - +echo "###---------- PRESS ANY KEY TO TERMINATE THE JAMULUS SESSION ---------###" +read -n 1 -s -r -p "" +killall Jamulus diff --git a/libs/oboe b/libs/oboe index 55d878a4e8..10bb6fa3e3 160000 --- a/libs/oboe +++ b/libs/oboe @@ -1 +1 @@ -Subproject commit 55d878a4e85e1994f2b5883366079b991500a25f +Subproject commit 10bb6fa3e3dc5dbe225df0b6aa7d3e3794b6b2e2 diff --git a/libs/opus/celt/arm/armcpu.c b/libs/opus/celt/arm/armcpu.c index 694a63b78e..6987b69a18 100644 --- a/libs/opus/celt/arm/armcpu.c +++ b/libs/opus/celt/arm/armcpu.c @@ -148,7 +148,7 @@ opus_uint32 opus_cpu_capabilities(void) } #else /* The feature registers which can tell us what the processor supports are - * accessible in priveleged modes only, so we can't have a general user-space + * accessible in privileged modes only, so we can't have a general user-space * detection method like on x86.*/ # error "Configured to use ARM asm but no CPU detection method available for " \ "your platform. Reconfigure with --disable-rtcd (or send patches)." diff --git a/libs/opus/celt/arm/celt_pitch_xcorr_arm-gnu.S b/libs/opus/celt/arm/celt_pitch_xcorr_arm-gnu.S index 10668e54a5..e6a397652f 100644 --- a/libs/opus/celt/arm/celt_pitch_xcorr_arm-gnu.S +++ b/libs/opus/celt/arm/celt_pitch_xcorr_arm-gnu.S @@ -81,7 +81,7 @@ xcorr_kernel_neon_process8: @ @ Load x[0...7] VLD1.16 {d6, d7}, [r4]! - @ Unlike VMOV, VAND is a data processsing instruction (and doesn't get + @ Unlike VMOV, VAND is a data processing instruction (and doesn't get @ assembled to VMOV, like VORR would), so it dual-issues with the prior VLD1. VAND d3, d5, d5 SUBS r12, r12, #8 diff --git a/libs/opus/celt/arm/celt_pitch_xcorr_arm.s b/libs/opus/celt/arm/celt_pitch_xcorr_arm.s index 6e873afc37..65a4461fff 100644 --- a/libs/opus/celt/arm/celt_pitch_xcorr_arm.s +++ b/libs/opus/celt/arm/celt_pitch_xcorr_arm.s @@ -78,7 +78,7 @@ xcorr_kernel_neon_process8 ; ; Load x[0...7] VLD1.16 {d6, d7}, [r4]! - ; Unlike VMOV, VAND is a data processsing instruction (and doesn't get + ; Unlike VMOV, VAND is a data processing instruction (and doesn't get ; assembled to VMOV, like VORR would), so it dual-issues with the prior VLD1. VAND d3, d5, d5 SUBS r12, r12, #8 diff --git a/libs/opus/celt/celt_encoder.c b/libs/opus/celt/celt_encoder.c index 44cb0850ab..f49a0e8e92 100644 --- a/libs/opus/celt/celt_encoder.c +++ b/libs/opus/celt/celt_encoder.c @@ -364,7 +364,7 @@ static int transient_analysis(const opus_val32 * OPUS_RESTRICT in, int len, int unmask=0; /* We should never see NaNs here. If we find any, then something really bad happened and we better abort before it does any damage later on. If these asserts are disabled (no hardening), then the table - lookup a few lines below (id = ...) is likely to crash dur to an out-of-bounds read. DO NOT FIX + lookup a few lines below (id = ...) is likely to crash due to an out-of-bounds read. DO NOT FIX that crash on NaN since it could result in a worse issue later on. */ celt_assert(!celt_isnan(tmp[0])); celt_assert(!celt_isnan(norm)); @@ -1571,7 +1571,7 @@ int celt_encode_with_ec(CELTEncoder * OPUS_RESTRICT st, const opus_val16 * pcm, (tmp+4*mode->Fs)/(8*mode->Fs)-!!st->signalling)); effectiveBytes = nbCompressedBytes - nbFilledBytes; } - equiv_rate = ((opus_int32)nbCompressedBytes*8*50 >> (3-LM)) - (40*C+20)*((400>>LM) - 50); + equiv_rate = ((opus_int32)nbCompressedBytes*8*50 << (3-LM)) - (40*C+20)*((400>>LM) - 50); if (st->bitrate != OPUS_BITRATE_MAX) equiv_rate = IMIN(equiv_rate, st->bitrate - (40*C+20)*((400>>LM) - 50)); diff --git a/libs/opus/celt/entcode.h b/libs/opus/celt/entcode.h index 3763e3f284..de145c7193 100644 --- a/libs/opus/celt/entcode.h +++ b/libs/opus/celt/entcode.h @@ -82,7 +82,7 @@ struct ec_ctx{ In the encoder: the low end of the current range.*/ opus_uint32 val; /*In the decoder: the saved normalization factor from ec_decode(). - In the encoder: the number of oustanding carry propagating symbols.*/ + In the encoder: the number of outstanding carry propagating symbols.*/ opus_uint32 ext; /*A buffered input/output symbol, awaiting carry propagation.*/ int rem; diff --git a/libs/opus/celt/entenc.h b/libs/opus/celt/entenc.h index f502eaf662..a544f79c0f 100644 --- a/libs/opus/celt/entenc.h +++ b/libs/opus/celt/entenc.h @@ -35,7 +35,7 @@ _size: The size of the buffer, in chars.*/ void ec_enc_init(ec_enc *_this,unsigned char *_buf,opus_uint32 _size); /*Encodes a symbol given its frequency information. - The frequency information must be discernable by the decoder, assuming it + The frequency information must be discernible by the decoder, assuming it has read only the previous symbols from the stream. It is allowable to change the frequency information, or even the entire source alphabet, so long as the decoder can tell from the context of the diff --git a/libs/opus/celt/kiss_fft.c b/libs/opus/celt/kiss_fft.c index 83775165d8..a0ea27666a 100644 --- a/libs/opus/celt/kiss_fft.c +++ b/libs/opus/celt/kiss_fft.c @@ -42,7 +42,7 @@ #include "stack_alloc.h" /* The guts header contains all the multiplication and addition macros that are defined for - complex numbers. It also delares the kf_ internal functions. + complex numbers. It also declares the kf_ internal functions. */ static void kf_bfly2( diff --git a/libs/opus/include/opus.h b/libs/opus/include/opus.h index d282f21d25..eca574d540 100644 --- a/libs/opus/include/opus.h +++ b/libs/opus/include/opus.h @@ -103,7 +103,7 @@ extern "C" { * @endcode * * where opus_encoder_get_size() returns the required size for the encoder state. Note that - * future versions of this code may change the size, so no assuptions should be made about it. + * future versions of this code may change the size, so no assumptions should be made about it. * * The encoder state is always continuous in memory and only a shallow copy is sufficient * to copy it (e.g. memcpy()) @@ -357,7 +357,7 @@ OPUS_EXPORT int opus_encoder_ctl(OpusEncoder *st, int request, ...) OPUS_ARG_NON * error = opus_decoder_init(dec, Fs, channels); * @endcode * where opus_decoder_get_size() returns the required size for the decoder state. Note that - * future versions of this code may change the size, so no assuptions should be made about it. + * future versions of this code may change the size, so no assumptions should be made about it. * * The decoder state is always continuous in memory and only a shallow copy is sufficient * to copy it (e.g. memcpy()) diff --git a/libs/opus/include/opus_multistream.h b/libs/opus/include/opus_multistream.h index babcee6905..f4dcd1bb9d 100644 --- a/libs/opus/include/opus_multistream.h +++ b/libs/opus/include/opus_multistream.h @@ -143,7 +143,7 @@ extern "C" { * Vorbis * channel ordering. A decoder may wish to apply an additional permutation * to the mapping the encoder used to achieve a different output channel - * order (e.g. for outputing in WAV order). + * order (e.g. for outputting in WAV order). * * Each multistream packet contains an Opus packet for each stream, and all of * the Opus packets in a single multistream packet must have the same @@ -510,7 +510,7 @@ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusMSDecoder *opus_multistream_decoder_crea int *error ) OPUS_ARG_NONNULL(5); -/** Intialize a previously allocated decoder state object. +/** Initialize a previously allocated decoder state object. * The memory pointed to by \a st must be at least the size returned by * opus_multistream_encoder_get_size(). * This is intended for applications which use their own allocator instead of diff --git a/libs/opus/include/opus_projection.h b/libs/opus/include/opus_projection.h index 9dabf4e85c..5bdc6e66de 100644 --- a/libs/opus/include/opus_projection.h +++ b/libs/opus/include/opus_projection.h @@ -405,7 +405,7 @@ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusProjectionDecoder *opus_projection_decod ) OPUS_ARG_NONNULL(5); -/** Intialize a previously allocated projection decoder state object. +/** Initialize a previously allocated projection decoder state object. * The memory pointed to by \a st must be at least the size returned by * opus_projection_decoder_get_size(). * This is intended for applications which use their own allocator instead of diff --git a/libs/opus/opus_functions.cmake b/libs/opus/opus_functions.cmake index fe309c2c49..f69408cfe2 100644 --- a/libs/opus/opus_functions.cmake +++ b/libs/opus/opus_functions.cmake @@ -248,7 +248,7 @@ function(get_opus_sources SOURCE_GROUP MAKE_FILE SOURCES) if(${list_length} LESS 1) message( FATAL_ERROR - "No files parsed succesfully from ${SOURCE_GROUP} in ${MAKE_FILE}") + "No files parsed successfully from ${SOURCE_GROUP} in ${MAKE_FILE}") endif() # remove trailing whitespaces diff --git a/libs/opus/silk/PLC.c b/libs/opus/silk/PLC.c index f89391651c..bf808cf0c4 100644 --- a/libs/opus/silk/PLC.c +++ b/libs/opus/silk/PLC.c @@ -156,7 +156,7 @@ static OPUS_INLINE void silk_PLC_update( silk_memset( psPLC->LTPCoef_Q14, 0, LTP_ORDER * sizeof( opus_int16 )); } - /* Save LPC coeficients */ + /* Save LPC coefficients */ silk_memcpy( psPLC->prevLPC_Q12, psDecCtrl->PredCoef_Q12[ 1 ], psDec->LPC_order * sizeof( opus_int16 ) ); psPLC->prevLTP_scale_Q14 = psDecCtrl->LTP_scale_Q14; @@ -257,7 +257,7 @@ static OPUS_INLINE void silk_PLC_conceal( /* LPC concealment. Apply BWE to previous LPC */ silk_bwexpander( psPLC->prevLPC_Q12, psDec->LPC_order, SILK_FIX_CONST( BWE_COEF, 16 ) ); - /* Preload LPC coeficients to array on stack. Gives small performance gain */ + /* Preload LPC coefficients to array on stack. Gives small performance gain */ silk_memcpy( A_Q12, psPLC->prevLPC_Q12, psDec->LPC_order * sizeof( opus_int16 ) ); /* First Lost frame */ diff --git a/libs/opus/silk/control_SNR.c b/libs/opus/silk/control_SNR.c index 9a6db27543..455acad6f6 100644 --- a/libs/opus/silk/control_SNR.c +++ b/libs/opus/silk/control_SNR.c @@ -78,7 +78,7 @@ static const unsigned char silk_TargetRate_WB_21[201 - 10] = { 247,248,249,249,250,251,252,253,255 }; -/* Control SNR of redidual quantizer */ +/* Control SNR of residual quantizer */ opus_int silk_control_SNR( silk_encoder_state *psEncC, /* I/O Pointer to Silk encoder state */ opus_int32 TargetRate_bps /* I Target max bitrate (bps) */ diff --git a/libs/opus/silk/decode_core.c b/libs/opus/silk/decode_core.c index 1c352a6522..79f378034d 100644 --- a/libs/opus/silk/decode_core.c +++ b/libs/opus/silk/decode_core.c @@ -98,7 +98,7 @@ void silk_decode_core( pres_Q14 = res_Q14; A_Q12 = psDecCtrl->PredCoef_Q12[ k >> 1 ]; - /* Preload LPC coeficients to array on stack. Gives small performance gain */ + /* Preload LPC coefficients to array on stack. Gives small performance gain */ silk_memcpy( A_Q12_tmp, A_Q12, psDec->LPC_order * sizeof( opus_int16 ) ); B_Q14 = &psDecCtrl->LTPCoef_Q14[ k * LTP_ORDER ]; signalType = psDec->indices.signalType; diff --git a/libs/opus/silk/decode_indices.c b/libs/opus/silk/decode_indices.c index 0bb4a997a5..268e4276b3 100644 --- a/libs/opus/silk/decode_indices.c +++ b/libs/opus/silk/decode_indices.c @@ -120,7 +120,7 @@ void silk_decode_indices( } psDec->ec_prevLagIndex = psDec->indices.lagIndex; - /* Get countour index */ + /* Get contour index */ psDec->indices.contourIndex = (opus_int8)ec_dec_icdf( psRangeDec, psDec->pitch_contour_iCDF, 8 ); /********************/ diff --git a/libs/opus/silk/encode_indices.c b/libs/opus/silk/encode_indices.c index 4bcbc3347b..8a6db2cbe1 100644 --- a/libs/opus/silk/encode_indices.c +++ b/libs/opus/silk/encode_indices.c @@ -140,7 +140,7 @@ void silk_encode_indices( } psEncC->ec_prevLagIndex = psIndices->lagIndex; - /* Countour index */ + /* Contour index */ silk_assert( psIndices->contourIndex >= 0 ); silk_assert( ( psIndices->contourIndex < 34 && psEncC->fs_kHz > 8 && psEncC->nb_subfr == 4 ) || ( psIndices->contourIndex < 11 && psEncC->fs_kHz == 8 && psEncC->nb_subfr == 4 ) || diff --git a/libs/opus/silk/float/find_pitch_lags_FLP.c b/libs/opus/silk/float/find_pitch_lags_FLP.c index dedbcd2836..fc66ffb379 100644 --- a/libs/opus/silk/float/find_pitch_lags_FLP.c +++ b/libs/opus/silk/float/find_pitch_lags_FLP.c @@ -61,7 +61,7 @@ void silk_find_pitch_lags_FLP( x_buf = x - psEnc->sCmn.ltp_mem_length; /******************************************/ - /* Estimate LPC AR coeficients */ + /* Estimate LPC AR coefficients */ /******************************************/ /* Calculate windowed signal */ diff --git a/libs/opus/silk/main.h b/libs/opus/silk/main.h index 1a33eed549..0bed912ecb 100644 --- a/libs/opus/silk/main.h +++ b/libs/opus/silk/main.h @@ -142,7 +142,7 @@ opus_int silk_control_audio_bandwidth( silk_EncControlStruct *encControl /* I Control structure */ ); -/* Control SNR of redidual quantizer */ +/* Control SNR of residual quantizer */ opus_int silk_control_SNR( silk_encoder_state *psEncC, /* I/O Pointer to Silk encoder state */ opus_int32 TargetRate_bps /* I Target max bitrate (bps) */ diff --git a/libs/opus/silk/mips/NSQ_del_dec_mipsr1.h b/libs/opus/silk/mips/NSQ_del_dec_mipsr1.h index cd70713a8f..70222aeeb2 100644 --- a/libs/opus/silk/mips/NSQ_del_dec_mipsr1.h +++ b/libs/opus/silk/mips/NSQ_del_dec_mipsr1.h @@ -86,14 +86,14 @@ static inline void silk_noise_shape_quantizer_del_dec( /*Unused.*/ (void)arch; - //Intialize b_Q14 variables + //Initialize b_Q14 variables b_Q14_0 = b_Q14[ 0 ]; b_Q14_1 = b_Q14[ 1 ]; b_Q14_2 = b_Q14[ 2 ]; b_Q14_3 = b_Q14[ 3 ]; b_Q14_4 = b_Q14[ 4 ]; - //Intialize a_Q12 variables + //Initialize a_Q12 variables a_Q12_0 = a_Q12[0]; a_Q12_1 = a_Q12[1]; a_Q12_2 = a_Q12[2]; diff --git a/libs/opus/silk/structs.h b/libs/opus/silk/structs.h index 3380c757b2..da66fe638d 100644 --- a/libs/opus/silk/structs.h +++ b/libs/opus/silk/structs.h @@ -231,7 +231,7 @@ typedef struct { /* Struct for Packet Loss Concealment */ typedef struct { opus_int32 pitchL_Q8; /* Pitch lag to use for voiced concealment */ - opus_int16 LTPCoef_Q14[ LTP_ORDER ]; /* LTP coeficients to use for voiced concealment */ + opus_int16 LTPCoef_Q14[ LTP_ORDER ]; /* LTP coefficients to use for voiced concealment */ opus_int16 prevLPC_Q12[ MAX_LPC_ORDER ]; opus_int last_frame_lost; /* Was previous frame lost */ opus_int32 rand_seed; /* Seed for unvoiced signal generation */ diff --git a/libs/opus/src/analysis.h b/libs/opus/src/analysis.h index 0b66555f21..cb19cc0dfa 100644 --- a/libs/opus/src/analysis.h +++ b/libs/opus/src/analysis.h @@ -88,7 +88,7 @@ typedef struct { */ void tonality_analysis_init(TonalityAnalysisState *analysis, opus_int32 Fs); -/** Reset a TonalityAnalysisState stuct. +/** Reset a TonalityAnalysisState struct. * * Call this when there's a discontinuity in the data. */ diff --git a/libs/opus/src/opus_encoder.c b/libs/opus/src/opus_encoder.c index e98ac5b8d0..5c1366e193 100644 --- a/libs/opus/src/opus_encoder.c +++ b/libs/opus/src/opus_encoder.c @@ -1604,7 +1604,7 @@ opus_int32 opus_encode_native(OpusEncoder *st, const opus_val16 *pcm, int frame_ redundancy = 1; celt_to_silk = 1; st->silk_bw_switch = 0; - /* Do a prefill without reseting the sampling rate control. */ + /* Do a prefill without resetting the sampling rate control. */ prefill=2; } diff --git a/linux/sound.cpp b/linux/sound.cpp index b55b550e8d..363330f537 100755 --- a/linux/sound.cpp +++ b/linux/sound.cpp @@ -20,7 +20,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -151,6 +151,25 @@ void CSound::OpenJack ( const bool bNoAutoJackConnect, jack_free ( ports ); } + + // input latency + jack_latency_range_t latrange; + latrange.min = 0; + latrange.max = 0 ; + + jack_port_get_latency_range ( input_port_left, JackCaptureLatency, &latrange ); + int inLatency = latrange.min; // be optimistic + + // output latency + latrange.min = 0; + latrange.max = 0 ; + + jack_port_get_latency_range ( output_port_left, JackPlaybackLatency, &latrange ); + int outLatency = latrange.min; // be optimistic + + // compute latency by using the first input and first output + // ports and using the most optimistic values + fInOutLatencyMs = static_cast ( inLatency + outLatency ) * 1000 / SYSTEM_SAMPLE_RATE_HZ; } } @@ -187,7 +206,7 @@ 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 +// registered callback which calls "EmitReinitRequestSignal()". In that function // this CSound::Init() function is called... //jack_set_buffer_size ( pJackClient, iNewPrefMonoBufferSize ); @@ -221,6 +240,9 @@ int CSound::process ( jack_nframes_t nframes, void* arg ) CSound* pSound = static_cast ( arg ); int i; + // make sure we are locked during execution + QMutexLocker locker ( &pSound->MutexAudioProcessCallback ); + if ( pSound->IsRunning() && ( nframes == static_cast ( pSound->iJACKBufferSizeMono ) ) ) { // get input data pointer @@ -237,11 +259,8 @@ int CSound::process ( jack_nframes_t nframes, void* arg ) { for ( i = 0; i < pSound->iJACKBufferSizeMono; i++ ) { - pSound->vecsTmpAudioSndCrdStereo[2 * i] = - (short) ( in_left[i] * _MAXSHORT ); - - pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] = - (short) ( in_right[i] * _MAXSHORT ); + pSound->vecsTmpAudioSndCrdStereo[2 * i] = Float2Short ( in_left[i] * _MAXSHORT ); + pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] = Float2Short ( in_right[i] * _MAXSHORT ); } } diff --git a/linux/sound.h b/linux/sound.h index 23e9bf5980..05ce243a07 100755 --- a/linux/sound.h +++ b/linux/sound.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -62,11 +62,11 @@ class CSound : public CSoundBase public: CSound ( void (*fpNewProcessCallback) ( CVector& psData, void* arg ), void* arg, - const int iCtrlMIDIChannel, + const QString& strMIDISetup, const bool bNoAutoJackConnect, const QString& strJackClientName ) : - CSoundBase ( "Jack", true, fpNewProcessCallback, arg, iCtrlMIDIChannel ), - iJACKBufferSizeMono ( 0 ), bJackWasShutDown ( false ) { OpenJack ( bNoAutoJackConnect, strJackClientName.toLocal8Bit().data() ); } + CSoundBase ( "Jack", fpNewProcessCallback, arg, strMIDISetup ), + iJACKBufferSizeMono ( 0 ), bJackWasShutDown ( false ), fInOutLatencyMs ( 0.0f ) { OpenJack ( bNoAutoJackConnect, strJackClientName.toLocal8Bit().data() ); } virtual ~CSound() { CloseJack(); } @@ -74,6 +74,8 @@ class CSound : public CSoundBase virtual void Start(); virtual void Stop(); + virtual float GetInOutLatencyMs() { return fInOutLatencyMs; } + // these variables should be protected but cannot since we want // to access them from the callback function CVector vecsTmpAudioSndCrdStereo; @@ -98,18 +100,34 @@ class CSound : public CSoundBase static int bufferSizeCallback ( jack_nframes_t, void *arg ); static void shutdownCallback ( void* ); jack_client_t* pJackClient; + + float fInOutLatencyMs; }; #else // no sound -> dummy class definition +#include "server.h" class CSound : public CSoundBase { + Q_OBJECT + public: CSound ( void (*fpNewProcessCallback) ( CVector& psData, void* pParg ), void* pParg, - const int iCtrlMIDIChannel, + const QString& strMIDISetup, const bool , const QString& ) : - CSoundBase ( "nosound", false, fpNewProcessCallback, pParg, iCtrlMIDIChannel ) {} + CSoundBase ( "nosound", fpNewProcessCallback, pParg, strMIDISetup ), + HighPrecisionTimer ( true ) { HighPrecisionTimer.Start(); + QObject::connect ( &HighPrecisionTimer, &CHighPrecisionTimer::timeout, + this, &CSound::OnTimer ); } virtual ~CSound() {} + virtual int Init ( const int iNewPrefMonoBufferSize ) { CSoundBase::Init ( iNewPrefMonoBufferSize ); + vecsTemp.Init ( 2 * iNewPrefMonoBufferSize ); + return iNewPrefMonoBufferSize; } + CHighPrecisionTimer HighPrecisionTimer; + CVector vecsTemp; + +public slots: + void OnTimer() { vecsTemp.Reset ( 0 ); if ( IsRunning() ) { ProcessCallback ( vecsTemp ); } } }; #endif // WITH_SOUND diff --git a/mac/Info-make.plist b/mac/Info-make.plist index 9f750fada6..503eb9141a 100644 --- a/mac/Info-make.plist +++ b/mac/Info-make.plist @@ -17,7 +17,7 @@ CFBundleDevelopmentRegion en CFBundleIconFile - mainicon.icns + @ICON@ CFBundleGetInfoString Created by Qt/QMake diff --git a/mac/Info-xcode.plist b/mac/Info-xcode.plist index ad59ccfcc4..31b543d5ea 100644 --- a/mac/Info-xcode.plist +++ b/mac/Info-xcode.plist @@ -17,7 +17,7 @@ CFBundleDevelopmentRegion en CFBundleIconFile - mainicon.icns + ${ASSETCATALOG_COMPILER_APPICON_NAME} CFBundleGetInfoString Created by Qt/QMake diff --git a/mac/activity.h b/mac/activity.h new file mode 100644 index 0000000000..4731f36890 --- /dev/null +++ b/mac/activity.h @@ -0,0 +1,46 @@ +/******************************************************************************\ + * Copyright (c) 2004-2020 + * + * Author(s): + * AronVietti + * + ****************************************************************************** + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ +#pragma once + +// Forward Delcaration to pointer that holds the activity id +class CActivityId; + +// Reperesents an OSX specific activity. See Managing Activities +// https://developer.apple.com/documentation/foundation/nsprocessinfo?language=objc +// This essentially lets us start and stop an Activity frame where we tell the OS +// that we are Latency Critical and are not performing background tasks. +class CActivity +{ +private: + CActivityId *pActivity; + +public: + CActivity(); + + ~CActivity(); + + void BeginActivity(); + + void EndActivity(); +}; diff --git a/mac/activity.mm b/mac/activity.mm new file mode 100644 index 0000000000..a0f718cc53 --- /dev/null +++ b/mac/activity.mm @@ -0,0 +1,53 @@ +/******************************************************************************\ + * Copyright (c) 2004-2020 + * + * Author(s): + * AronVietti + * + ****************************************************************************** + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#include "activity.h" +#include + +class CActivityId +{ +public: + id activityId; +}; + +CActivity::CActivity() : pActivity(new CActivityId()) {} + +CActivity::~CActivity() +{ + delete pActivity; +} + +void CActivity::BeginActivity() +{ + NSActivityOptions options = NSActivityBackground | NSActivityIdleSystemSleepDisabled | NSActivityLatencyCritical; + + pActivity->activityId = [[NSProcessInfo processInfo] beginActivityWithOptions: options reason:@"Jamulus provides low latency audio processing and should not be inturrupted by system throttling."]; +} + +void CActivity::EndActivity() +{ + [[NSProcessInfo processInfo] endActivity: pActivity->activityId]; + + pActivity->activityId = nil; +} diff --git a/mac/jamulus-server-icon-2020.icns b/mac/jamulus-server-icon-2020.icns new file mode 100644 index 0000000000..5986210768 Binary files /dev/null and b/mac/jamulus-server-icon-2020.icns differ diff --git a/mac/mainicon.icns b/mac/mainicon.icns index a54c1c4439..375aae4946 100644 Binary files a/mac/mainicon.icns and b/mac/mainicon.icns differ diff --git a/mac/sound.cpp b/mac/sound.cpp index 18adbaf02c..b758abd759 100755 --- a/mac/sound.cpp +++ b/mac/sound.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -28,10 +28,10 @@ /* Implementation *************************************************************/ CSound::CSound ( void (*fpNewProcessCallback) ( CVector& psData, void* arg ), void* arg, - const int iCtrlMIDIChannel, + const QString& strMIDISetup, const bool , const QString& ) : - CSoundBase ( "CoreAudio", true, fpNewProcessCallback, arg, iCtrlMIDIChannel ), + CSoundBase ( "CoreAudio", fpNewProcessCallback, arg, strMIDISetup ), midiInPortRef ( static_cast ( NULL ) ) { // Apple Mailing Lists: Subject: GUI Apps should set kAudioHardwarePropertyRunLoop @@ -54,8 +54,43 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector& psData sizeof ( CFRunLoopRef ), &theRunLoop ); + // initial query for available input/output sound devices in the system + GetAvailableInOutDevices(); - // Get available input/output devices -------------------------------------- + // init device index as not initialized (invalid) + lCurDev = INVALID_INDEX; + CurrentAudioInputDeviceID = 0; + CurrentAudioOutputDeviceID = 0; + iNumInChan = 0; + iNumInChanPlusAddChan = 0; + iNumOutChan = 0; + iSelInputLeftChannel = 0; + iSelInputRightChannel = 0; + iSelOutputLeftChannel = 0; + iSelOutputRightChannel = 0; + + + // Optional MIDI initialization -------------------------------------------- + if ( iCtrlMIDIChannel != INVALID_MIDI_CH ) + { + // create client and ports + MIDIClientRef midiClient = static_cast ( NULL ); + MIDIClientCreate ( CFSTR ( APP_NAME ), NULL, NULL, &midiClient ); + MIDIInputPortCreate ( midiClient, CFSTR ( "Input port" ), callbackMIDI, this, &midiInPortRef ); + + // open connections from all sources + const int iNMIDISources = MIDIGetNumberOfSources(); + + for ( int i = 0; i < iNMIDISources; i++ ) + { + MIDIEndpointRef src = MIDIGetSource ( i ); + MIDIPortConnectSource ( midiInPortRef, src, NULL ) ; + } + } +} + +void CSound::GetAvailableInOutDevices() +{ UInt32 iPropertySize = 0; AudioObjectPropertyAddress stPropertyAddress; @@ -161,37 +196,6 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector& psData } } } - - // init device index as not initialized (invalid) - lCurDev = INVALID_INDEX; - CurrentAudioInputDeviceID = 0; - CurrentAudioOutputDeviceID = 0; - iNumInChan = 0; - iNumInChanPlusAddChan = 0; - iNumOutChan = 0; - iSelInputLeftChannel = 0; - iSelInputRightChannel = 0; - iSelOutputLeftChannel = 0; - iSelOutputRightChannel = 0; - - - // Optional MIDI initialization -------------------------------------------- - if ( iCtrlMIDIChannel != INVALID_MIDI_CH ) - { - // create client and ports - MIDIClientRef midiClient = static_cast ( NULL ); - MIDIClientCreate ( CFSTR ( APP_NAME ), NULL, NULL, &midiClient ); - MIDIInputPortCreate ( midiClient, CFSTR ( "Input port" ), callbackMIDI, this, &midiInPortRef ); - - // open connections from all sources - const int iNMIDISources = MIDIGetNumberOfSources(); - - for ( int i = 0; i < iNMIDISources; i++ ) - { - MIDIEndpointRef src = MIDIGetSource ( i ); - MIDIPortConnectSource ( midiInPortRef, src, NULL ) ; - } - } } void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID, @@ -310,25 +314,142 @@ int CSound::CountChannels ( AudioDeviceID devID, return result; } -QString CSound::LoadAndInitializeDriver ( int iDriverIdx, bool ) +QString CSound::LoadAndInitializeDriver ( QString strDriverName, bool ) { - // check device capabilities if it fullfills our requirements + // secure lNumDevs/strDriverNames access + QMutexLocker locker ( &Mutex ); + + // reload the driver list of available sound devices + GetAvailableInOutDevices(); + + // find driver index from given driver name + int iDriverIdx = INVALID_INDEX; // initialize with an invalid index + + for ( int i = 0; i < MAX_NUMBER_SOUND_CARDS; i++ ) + { + if ( strDriverName.compare ( strDriverNames[i] ) == 0 ) + { + iDriverIdx = i; + } + } + + // if the selected driver was not found, return an error message + if ( iDriverIdx == INVALID_INDEX ) + { + return tr ( "The current selected audio device is no longer present in the system." ); + } + + // check device capabilities if it fulfills our requirements const QString strStat = CheckDeviceCapabilities ( iDriverIdx ); - // check if device is capable - if ( strStat.isEmpty() ) + // check if device is capable and if not the same device is used + if ( strStat.isEmpty() && ( strCurDevName.compare ( strDriverNames[iDriverIdx] ) != 0 ) ) { + AudioObjectPropertyAddress stPropertyAddress; + + // unregister callbacks if previous device was valid + if ( lCurDev != INVALID_INDEX ) + { + stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; + stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; + + // unregister callback functions for device property changes + stPropertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + + AudioObjectRemovePropertyListener ( kAudioObjectSystemObject, + &stPropertyAddress, + deviceNotification, + this ); + + stPropertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; + + AudioObjectRemovePropertyListener ( kAudioObjectSystemObject, + &stPropertyAddress, + deviceNotification, + this ); + + stPropertyAddress.mSelector = kAudioDevicePropertyDeviceHasChanged; + + AudioObjectRemovePropertyListener ( audioOutputDevice[lCurDev], + &stPropertyAddress, + deviceNotification, + this ); + + AudioObjectRemovePropertyListener ( audioInputDevice[lCurDev], + &stPropertyAddress, + deviceNotification, + this ); + + stPropertyAddress.mSelector = kAudioDevicePropertyDeviceIsAlive; + + AudioObjectRemovePropertyListener ( audioOutputDevice[lCurDev], + &stPropertyAddress, + deviceNotification, + this ); + + AudioObjectRemovePropertyListener ( audioInputDevice[lCurDev], + &stPropertyAddress, + deviceNotification, + this ); + } + // store ID of selected driver if initialization was successful lCurDev = iDriverIdx; CurrentAudioInputDeviceID = audioInputDevice[iDriverIdx]; CurrentAudioOutputDeviceID = audioOutputDevice[iDriverIdx]; + // register callbacks + stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; + stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; + + // setup callbacks for device property changes + stPropertyAddress.mSelector = kAudioDevicePropertyDeviceHasChanged; + + AudioObjectAddPropertyListener ( audioInputDevice[lCurDev], + &stPropertyAddress, + deviceNotification, + this ); + + AudioObjectAddPropertyListener ( audioOutputDevice[lCurDev], + &stPropertyAddress, + deviceNotification, + this ); + + stPropertyAddress.mSelector = kAudioDevicePropertyDeviceIsAlive; + + AudioObjectAddPropertyListener ( audioInputDevice[lCurDev], + &stPropertyAddress, + deviceNotification, + this ); + + AudioObjectAddPropertyListener ( audioOutputDevice[lCurDev], + &stPropertyAddress, + deviceNotification, + this ); + + stPropertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + + AudioObjectAddPropertyListener ( kAudioObjectSystemObject, + &stPropertyAddress, + deviceNotification, + this ); + + stPropertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; + + AudioObjectAddPropertyListener ( kAudioObjectSystemObject, + &stPropertyAddress, + deviceNotification, + this ); + // the device has changed, per definition we reset the channel // mapping to the defaults (first two available channels) SetLeftInputChannel ( 0 ); SetRightInputChannel ( 1 ); SetLeftOutputChannel ( 0 ); SetRightOutputChannel ( 1 ); + + // store the current name of the driver + strCurDevName = strDriverNames[iDriverIdx]; } return strStat; @@ -350,12 +471,15 @@ QString CSound::CheckDeviceCapabilities ( const int iDriverIdx ) stPropertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate; iPropertySize = sizeof ( Float64 ); - AudioObjectGetPropertyData ( audioInputDevice[iDriverIdx], - &stPropertyAddress, - 0, - NULL, - &iPropertySize, - &inputSampleRate ); + if ( AudioObjectGetPropertyData ( audioInputDevice[iDriverIdx], + &stPropertyAddress, + 0, + NULL, + &iPropertySize, + &inputSampleRate ) ) + { + return QString ( tr ( "The audio input device is no longer available." ) ); + } if ( inputSampleRate != fSystemSampleRate ) { @@ -377,12 +501,15 @@ QString CSound::CheckDeviceCapabilities ( const int iDriverIdx ) // check output device sample rate iPropertySize = sizeof ( Float64 ); - AudioObjectGetPropertyData ( audioOutputDevice[iDriverIdx], - &stPropertyAddress, - 0, - NULL, - &iPropertySize, - &outputSampleRate ); + if ( AudioObjectGetPropertyData ( audioOutputDevice[iDriverIdx], + &stPropertyAddress, + 0, + NULL, + &iPropertySize, + &outputSampleRate ) ) + { + return QString ( tr ( "The audio output device is no longer available." ) ); + } if ( outputSampleRate != fSystemSampleRate ) { @@ -561,7 +688,7 @@ QString CSound::CheckDeviceCapabilities ( const int iDriverIdx ) // add the "[n]:" at the beginning as is in the Audio-Midi-Setup if ( !bConvOK || ( iPropertySize == 0 ) ) { - // use a defalut name in case there was an error or the name is empty + // use a default name in case there was an error or the name is empty sChannelNamesOutput[iCurOutCH] = QString ( "%1: Channel %1" ).arg ( iCurOutCH + 1 ); } @@ -719,32 +846,6 @@ void CSound::SetRightOutputChannel ( const int iNewChan ) void CSound::Start() { - AudioObjectPropertyAddress stPropertyAddress; - - stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; - stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; - - // setup callback for xruns (only for input is enough) - stPropertyAddress.mSelector = kAudioDeviceProcessorOverload; - - AudioObjectAddPropertyListener ( audioInputDevice[lCurDev], - &stPropertyAddress, - deviceNotification, - this ); - - // setup callbacks for device property changes - stPropertyAddress.mSelector = kAudioDevicePropertyDeviceHasChanged; - - AudioObjectAddPropertyListener ( audioInputDevice[lCurDev], - &stPropertyAddress, - deviceNotification, - this ); - - AudioObjectAddPropertyListener ( audioOutputDevice[lCurDev], - &stPropertyAddress, - deviceNotification, - this ); - // register the callback function for input and output AudioDeviceCreateIOProcID ( audioInputDevice[lCurDev], callbackIO, @@ -774,32 +875,6 @@ void CSound::Stop() AudioDeviceDestroyIOProcID ( audioInputDevice[lCurDev], audioInputProcID ); AudioDeviceDestroyIOProcID ( audioOutputDevice[lCurDev], audioOutputProcID ); - AudioObjectPropertyAddress stPropertyAddress; - - stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; - stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; - - // unregister callback functions for device property changes - stPropertyAddress.mSelector = kAudioDevicePropertyDeviceHasChanged; - - AudioObjectRemovePropertyListener( audioOutputDevice[lCurDev], - &stPropertyAddress, - deviceNotification, - this ); - - AudioObjectRemovePropertyListener( audioInputDevice[lCurDev], - &stPropertyAddress, - deviceNotification, - this ); - - // unregister the callback function for xruns - stPropertyAddress.mSelector = kAudioDeviceProcessorOverload; - - AudioObjectRemovePropertyListener( audioInputDevice[lCurDev], - &stPropertyAddress, - deviceNotification, - this ); - // call base class CSoundBase::Stop(); } @@ -901,21 +976,24 @@ OSStatus CSound::deviceNotification ( AudioDeviceID, { CSound* pSound = static_cast ( inRefCon ); - if ( inAddresses->mSelector == kAudioDevicePropertyDeviceHasChanged ) + if ( ( inAddresses->mSelector == kAudioDevicePropertyDeviceHasChanged ) || + ( inAddresses->mSelector == kAudioDevicePropertyDeviceIsAlive ) || + ( inAddresses->mSelector == kAudioHardwarePropertyDefaultOutputDevice ) || + ( inAddresses->mSelector == kAudioHardwarePropertyDefaultInputDevice ) ) { - // if any property of the device has changed, do a full reload - pSound->EmitReinitRequestSignal ( RS_RELOAD_RESTART_AND_INIT ); - } - -/* - if ( inAddresses->mSelector == kAudioDeviceProcessorOverload ) - { - // xrun handling (it is important to act on xruns under CoreAudio - // since it seems that the xrun situation stays stable for a - // while and would give you a long time bad audio) - pSound->EmitReinitRequestSignal ( RS_ONLY_RESTART ); + if ( ( inAddresses->mSelector == kAudioDevicePropertyDeviceHasChanged ) || + ( inAddresses->mSelector == kAudioDevicePropertyDeviceIsAlive ) ) + { + // if any property of the device has changed, do a full reload + pSound->EmitReinitRequestSignal ( RS_RELOAD_RESTART_AND_INIT ); + } + else + { + // for any other change in audio devices, just initiate a restart which + // triggers an update of the sound device selection combo box + pSound->EmitReinitRequestSignal ( RS_ONLY_RESTART ); + } } -*/ return noErr; } @@ -931,7 +1009,7 @@ OSStatus CSound::callbackIO ( AudioDeviceID inDevice, CSound* pSound = static_cast ( inRefCon ); // both, the input and output device use the same callback function - QMutexLocker locker ( &pSound->Mutex ); + QMutexLocker locker ( &pSound->MutexAudioProcessCallback ); const int iCoreAudioBufferSizeMono = pSound->iCoreAudioBufferSizeMono; const int iSelInBufferLeft = pSound->iSelInBufferLeft; @@ -949,7 +1027,7 @@ OSStatus CSound::callbackIO ( AudioDeviceID inDevice, const CVector& vecNumInBufChan = pSound->vecNumInBufChan; const CVector& vecNumOutBufChan = pSound->vecNumOutBufChan; - if ( ( inDevice == pSound->CurrentAudioInputDeviceID ) && inInputData ) + if ( ( inDevice == pSound->CurrentAudioInputDeviceID ) && inInputData && pSound->bRun ) { // check sizes (note that float32 has four bytes) if ( ( iSelInBufferLeft >= 0 ) && @@ -982,7 +1060,7 @@ OSStatus CSound::callbackIO ( AudioDeviceID inDevice, for ( int i = 0; i < iCoreAudioBufferSizeMono; i++ ) { - pSound->vecsTmpAudioSndCrdStereo[2 * i] = Double2Short ( + pSound->vecsTmpAudioSndCrdStereo[2 * i] = Float2Short ( pSound->vecsTmpAudioSndCrdStereo[2 * i] + pLeftData[iNumChanPerFrameLeft * i + iSelAddInInterlChLeft] * _MAXSHORT ); } } @@ -994,7 +1072,7 @@ OSStatus CSound::callbackIO ( AudioDeviceID inDevice, for ( int i = 0; i < iCoreAudioBufferSizeMono; i++ ) { - pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] = Double2Short ( + pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] = Float2Short ( pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] + pRightData[iNumChanPerFrameRight * i + iSelAddInInterlChRight] * _MAXSHORT ); } } @@ -1009,7 +1087,7 @@ OSStatus CSound::callbackIO ( AudioDeviceID inDevice, pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo ); } - if ( ( inDevice == pSound->CurrentAudioOutputDeviceID ) && outOutputData ) + if ( ( inDevice == pSound->CurrentAudioOutputDeviceID ) && outOutputData && pSound->bRun ) { // check sizes (note that float32 has four bytes) if ( ( iSelOutBufferLeft >= 0 ) && diff --git a/mac/sound.h b/mac/sound.h index 183c5377a7..b70274b8e6 100755 --- a/mac/sound.h +++ b/mac/sound.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -28,6 +28,7 @@ #include #include #include +#include #include "soundbase.h" #include "global.h" @@ -38,7 +39,7 @@ class CSound : public CSoundBase public: CSound ( void (*fpNewProcessCallback) ( CVector& psData, void* arg ), void* arg, - const int iCtrlMIDIChannel, + const QString& strMIDISetup, const bool , const QString& ); @@ -61,13 +62,14 @@ class CSound : public CSoundBase virtual int GetLeftOutputChannel() { return iSelOutputLeftChannel; } virtual int GetRightOutputChannel() { return iSelOutputRightChannel; } - // these variables should be protected but cannot since we want + // these variables/functions should be protected but cannot since we want // to access them from the callback function CVector vecsTmpAudioSndCrdStereo; int iCoreAudioBufferSizeMono; int iCoreAudioBufferSizeStereo; AudioDeviceID CurrentAudioInputDeviceID; AudioDeviceID CurrentAudioOutputDeviceID; + long lCurDev; int iNumInChan; int iNumInChanPlusAddChan; // includes additional "added" channels int iNumOutChan; @@ -91,10 +93,11 @@ class CSound : public CSoundBase CVector vecNumOutBufChan; protected: - virtual QString LoadAndInitializeDriver ( int iIdx, bool ); + virtual QString LoadAndInitializeDriver ( QString strDriverName, bool ); QString CheckDeviceCapabilities ( const int iDriverIdx ); void UpdateChSelection(); + void GetAvailableInOutDevices(); int CountChannels ( AudioDeviceID devID, bool isInput ); diff --git a/src/aboutdlgbase.ui b/src/aboutdlgbase.ui index 398f30f828..4c7f307783 100755 --- a/src/aboutdlgbase.ui +++ b/src/aboutdlgbase.ui @@ -21,7 +21,7 @@ - :/png/main/res/mainicon.png:/png/main/res/mainicon.png + :/png/main/res/fronticon.png:/png/main/res/fronticon.png true diff --git a/src/analyzerconsole.cpp b/src/analyzerconsole.cpp index d164864614..469ac9560a 100644 --- a/src/analyzerconsole.cpp +++ b/src/analyzerconsole.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -27,9 +27,8 @@ // Analyzer console implementation ********************************************* CAnalyzerConsole::CAnalyzerConsole ( CClient* pNCliP, - QWidget* parent, - Qt::WindowFlags ) : - QDialog ( parent ), + QWidget* parent ) : + QDialog ( parent, Qt::Window ), // use Qt::Window to get min/max window buttons pClient ( pNCliP ), GraphImage ( 1, 1, QImage::Format_RGB32 ), GraphErrRateCanvasRect ( 0, 0, 600, 450 ), // defines total size of graph @@ -71,8 +70,8 @@ CAnalyzerConsole::CAnalyzerConsole ( CClient* pNCliP, // Connections ------------------------------------------------------------- // timers - QObject::connect ( &TimerErrRateUpdate, SIGNAL ( timeout() ), - this, SLOT ( OnTimerErrRateUpdate() ) ); + QObject::connect ( &TimerErrRateUpdate, &QTimer::timeout, + this, &CAnalyzerConsole::OnTimerErrRateUpdate ); } void CAnalyzerConsole::showEvent ( QShowEvent* ) diff --git a/src/analyzerconsole.h b/src/analyzerconsole.h index 6fa3b59890..f5770da2a4 100644 --- a/src/analyzerconsole.h +++ b/src/analyzerconsole.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -45,9 +45,8 @@ class CAnalyzerConsole : public QDialog Q_OBJECT public: - CAnalyzerConsole ( CClient* pNCliP, - QWidget* parent = nullptr, - Qt::WindowFlags f = nullptr ); + CAnalyzerConsole ( CClient* pNCliP, + QWidget* parent = nullptr ); protected: diff --git a/src/audiomixerboard.cpp b/src/audiomixerboard.cpp old mode 100644 new mode 100755 index da65f8522c..36af8f4e72 --- a/src/audiomixerboard.cpp +++ b/src/audiomixerboard.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -28,52 +28,61 @@ /******************************************************************************\ * CChanneFader * \******************************************************************************/ -CChannelFader::CChannelFader ( QWidget* pNW, - QHBoxLayout* pParentLayout ) +CChannelFader::CChannelFader ( QWidget* pNW ) : + eDesign ( GD_STANDARD ) { // create new GUI control objects and store pointers to them (note that // QWidget takes the ownership of the pMainGrid so that this only has // to be created locally in this constructor) - pFrame = new QFrame ( pNW ); - - pLevelsBox = new QWidget ( pFrame ); - plbrChannelLevel = new CMultiColorLEDBar ( pLevelsBox ); - pFader = new QSlider ( Qt::Vertical, pLevelsBox ); - pPan = new QDial ( pLevelsBox ); - pPanLabel = new QLabel ( tr ( "Pan" ) , pLevelsBox ); - pInfoLabel = new QLabel ( "", pLevelsBox ); - - pMuteSoloBox = new QWidget ( pFrame ); - pcbMute = new QCheckBox ( tr ( "Mute" ), pMuteSoloBox ); - pcbSolo = new QCheckBox ( tr ( "Solo" ), pMuteSoloBox ); - - pLabelInstBox = new QGroupBox ( pFrame ); - plblLabel = new QLabel ( "", pFrame ); - plblInstrument = new QLabel ( pFrame ); - plblCountryFlag = new QLabel ( pFrame ); - - QVBoxLayout* pMainGrid = new QVBoxLayout ( pFrame ); - QHBoxLayout* pLevelsGrid = new QHBoxLayout ( pLevelsBox ); - QVBoxLayout* pMuteSoloGrid = new QVBoxLayout ( pMuteSoloBox ); - QHBoxLayout* pLabelGrid = new QHBoxLayout ( pLabelInstBox ); - QVBoxLayout* pLabelPictGrid = new QVBoxLayout ( ); - QVBoxLayout* pPanGrid = new QVBoxLayout ( ); - QHBoxLayout* pPanInfoGrid = new QHBoxLayout ( ); + pFrame = new QFrame ( pNW ); + + pLevelsBox = new QWidget ( pFrame ); + plbrChannelLevel = new CLevelMeter ( pLevelsBox ); + pFader = new QSlider ( Qt::Vertical, pLevelsBox ); + pPan = new QDial ( pLevelsBox ); + pPanLabel = new QLabel ( tr ( "Pan" ), pLevelsBox ); + pInfoLabel = new QLabel ( "", pLevelsBox ); + + pMuteSoloBox = new QWidget ( pFrame ); + pcbMute = new QCheckBox ( tr ( "Mute" ), pMuteSoloBox ); + pcbSolo = new QCheckBox ( tr ( "Solo" ), pMuteSoloBox ); + pcbGroup = new QCheckBox ( "", pMuteSoloBox ); + + pLabelInstBox = new QGroupBox ( pFrame ); + plblLabel = new QLabel ( "", pFrame ); + plblInstrument = new QLabel ( pFrame ); + plblCountryFlag = new QLabel ( pFrame ); + + QVBoxLayout* pMainGrid = new QVBoxLayout ( pFrame ); + QHBoxLayout* pLevelsGrid = new QHBoxLayout ( pLevelsBox ); + QVBoxLayout* pMuteSoloGrid = new QVBoxLayout ( pMuteSoloBox ); + pLabelGrid = new QHBoxLayout ( pLabelInstBox ); + pLabelPictGrid = new QVBoxLayout ( ); + QVBoxLayout* pPanGrid = new QVBoxLayout ( ); + QHBoxLayout* pPanInfoGrid = new QHBoxLayout ( ); + + // define the popup menu for the group checkbox + pGroupPopupMenu = new QMenu ( "", pcbGroup ); + pGroupPopupMenu->addAction ( tr ( "&No grouping" ), this, SLOT ( OnGroupMenuGrpNone() ) ); + pGroupPopupMenu->addAction ( tr ( "Assign to group" ) + " &1", this, SLOT ( OnGroupMenuGrp1() ) ); + pGroupPopupMenu->addAction ( tr ( "Assign to group" ) + " &2", this, SLOT ( OnGroupMenuGrp2() ) ); + pGroupPopupMenu->addAction ( tr ( "Assign to group" ) + " &3", this, SLOT ( OnGroupMenuGrp3() ) ); + pGroupPopupMenu->addAction ( tr ( "Assign to group" ) + " &4", this, SLOT ( OnGroupMenuGrp4() ) ); +#if ( MAX_NUM_FADER_GROUPS != 4 ) +# error "MAX_NUM_FADER_GROUPS must be set to 4, see implementation in CChannelFader()" +#endif // setup channel level plbrChannelLevel->setContentsMargins ( 0, 3, 2, 3 ); // setup slider - pFader->setPageStep ( 1 ); - pFader->setTickPosition ( QSlider::TicksBothSides ); - pFader->setRange ( 0, AUD_MIX_FADER_MAX ); - pFader->setTickInterval ( AUD_MIX_FADER_MAX / 9 ); - pFader->setMinimumHeight ( 85 ); // if this value is too small, the fader might not be movable with the mouse for fancy skin (#292) + pFader->setPageStep ( 1 ); + pFader->setRange ( 0, AUD_MIX_FADER_MAX ); + pFader->setTickInterval ( AUD_MIX_FADER_MAX / 9 ); // setup panning control pPan->setRange ( 0, AUD_MIX_PAN_MAX ); pPan->setValue ( AUD_MIX_PAN_MAX / 2 ); - pPan->setFixedSize ( 50, 50 ); pPan->setNotchesVisible ( true ); pPanInfoGrid->addWidget ( pPanLabel, 0, Qt::AlignLeft ); pPanInfoGrid->addWidget ( pInfoLabel ); @@ -81,12 +90,8 @@ CChannelFader::CChannelFader ( QWidget* pNW, pPanGrid->addWidget ( pPan, 0, Qt::AlignHCenter ); // setup fader tag label (black bold text which is centered) - plblLabel->setTextFormat ( Qt::PlainText ); - plblLabel->setAlignment ( Qt::AlignHCenter | Qt::AlignVCenter ); - plblLabel->setMinimumHeight ( 40 ); // maximum hight of the instrument+flag pictures - plblLabel->setStyleSheet ( - "QLabel { color: black;" - " font: bold; }" ); + plblLabel->setTextFormat ( Qt::PlainText ); + plblLabel->setAlignment ( Qt::AlignHCenter | Qt::AlignVCenter ); // set margins of the layouts to zero to get maximum space for the controls pMainGrid->setContentsMargins ( 0, 0, 0, 0 ); @@ -98,6 +103,7 @@ CChannelFader::CChannelFader ( QWidget* pNW, pLevelsGrid->setSpacing ( 0 ); // only minimal space pMuteSoloGrid->setContentsMargins ( 0, 0, 0, 0 ); + pMuteSoloGrid->setSpacing ( 0 ); // only minimal space pLabelGrid->setContentsMargins ( 0, 0, 0, 0 ); pLabelGrid->setSpacing ( 2 ); // only minimal space between picture and text @@ -107,47 +113,47 @@ CChannelFader::CChannelFader ( QWidget* pNW, pLabelPictGrid->addWidget ( plblInstrument, 0, Qt::AlignHCenter ); pLabelGrid->addLayout ( pLabelPictGrid ); - pLabelGrid->addWidget ( plblLabel, 0, Qt::AlignVCenter ); + pLabelGrid->addWidget ( plblLabel, 0, Qt::AlignVCenter ); // note: just initial add, may be changed later pLevelsGrid->addWidget ( plbrChannelLevel, 0, Qt::AlignRight ); pLevelsGrid->addWidget ( pFader, 0, Qt::AlignLeft ); - pMuteSoloGrid->addWidget ( pcbMute, 0, Qt::AlignLeft ); - pMuteSoloGrid->addWidget ( pcbSolo, 0, Qt::AlignLeft ); + pMuteSoloGrid->addWidget ( pcbGroup, 0, Qt::AlignLeft ); + pMuteSoloGrid->addWidget ( pcbMute, 0, Qt::AlignLeft ); + pMuteSoloGrid->addWidget ( pcbSolo, 0, Qt::AlignLeft ); pMainGrid->addLayout ( pPanGrid ); pMainGrid->addWidget ( pLevelsBox, 0, Qt::AlignHCenter ); pMainGrid->addWidget ( pMuteSoloBox, 0, Qt::AlignHCenter ); pMainGrid->addWidget ( pLabelInstBox ); - // add fader frame to audio mixer board layout - pParentLayout->addWidget( pFrame ); - // reset current fader + strGroupBaseText = "Grp"; // this will most probably overwritten by SetGUIDesign() + iInstrPicMaxWidth = INVALID_INDEX; // this will most probably overwritten by SetGUIDesign() Reset(); // add help text to controls plbrChannelLevel->setWhatsThis ( "" + tr ( "Channel Level" ) + ": " + - 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." ) ); + tr ( "Displays the pre-fader audio level of this channel. All clients connected to the " + "server will be assigned an audio level, the same value for every client." ) ); plbrChannelLevel->setAccessibleName ( tr ( "Input level of the current audio " "channel at the server" ) ); pFader->setWhatsThis ( "" + tr ( "Mixer Fader" ) + ": " + 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." ) ); + "Adjusts the audio level of this channel. All clients connected to the server " + "will be assigned an audio fader, displayed at each client, to adjust the local mix." ) ); pFader->setAccessibleName ( tr ( "Local mix level setting of the current audio " "channel at the server" ) ); pInfoLabel->setWhatsThis ( "" + tr ( "Status Indicator" ) + ": " + tr ( "Shows a status indication about the client which is assigned to this channel. " "Supported indicators are:" ) + "
  • " + tr ( - "Speaker with cancellation stroke: Indicates that the other client has muted you." ) + + "Speaker with cancellation stroke: Indicates that another client has muted you." ) + "
" ); pInfoLabel->setAccessibleName ( tr ( "Status indicator label" ) ); pPan->setWhatsThis ( "" + tr ( "Panning" ) + ": " + tr ( - "Sets the panning position from Left to Right of the channel. " + "Sets the pan from Left to Right of the channel. " "Works only in stereo or preferably mono in/stereo out mode." ) ); pPan->setAccessibleName ( tr ( "Local panning position of the current audio channel at the server" ) ); @@ -157,13 +163,18 @@ CChannelFader::CChannelFader ( QWidget* pNW, pcbSolo->setWhatsThis ( "" + tr ( "Solo" ) + ": " + 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 " + "except the soloed channel are muted. It is possible to set more than " "one channel to solo." ) ); pcbSolo->setAccessibleName ( tr ( "Solo button" ) ); + pcbGroup->setWhatsThis ( "" + tr ( "Group" ) + ": " + tr ( "With the Grp checkbox, a " + "group of audio channels can be defined. All channel faders in a group are moved " + "in proportional synchronization if any one of the group faders are moved." ) ); + pcbGroup->setAccessibleName ( tr ( "Group button" ) ); + QString strFaderText = "" + tr ( "Fader Tag" ) + ": " + 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." ); + "identifies the connected client. The tag name, a picture of your " + "instrument and the flag of your country can be set in the main window." ); plblInstrument->setWhatsThis ( strFaderText ); plblInstrument->setAccessibleName ( tr ( "Mixer channel instrument picture" ) ); @@ -174,26 +185,29 @@ CChannelFader::CChannelFader ( QWidget* pNW, // Connections ------------------------------------------------------------- - QObject::connect ( pFader, SIGNAL ( valueChanged ( int ) ), - this, SLOT ( OnLevelValueChanged ( int ) ) ); + QObject::connect ( pFader, &QSlider::valueChanged, + this, &CChannelFader::OnLevelValueChanged ); - QObject::connect ( pPan, SIGNAL ( valueChanged ( int ) ), - this, SLOT ( OnPanValueChanged ( int ) ) ); + QObject::connect ( pPan, &QDial::valueChanged, + this, &CChannelFader::OnPanValueChanged ); - QObject::connect ( pcbMute, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnMuteStateChanged ( int ) ) ); + QObject::connect ( pcbMute, &QCheckBox::stateChanged, + this, &CChannelFader::OnMuteStateChanged ); - QObject::connect ( pcbSolo, - SIGNAL ( stateChanged ( int ) ), - SIGNAL ( soloStateChanged ( int ) ) ); + QObject::connect ( pcbSolo, &QCheckBox::stateChanged, + this, &CChannelFader::soloStateChanged ); + + QObject::connect ( pcbGroup, &QCheckBox::stateChanged, + this, &CChannelFader::OnGroupStateChanged ); } void CChannelFader::SetGUIDesign ( const EGUIDesign eNewDesign ) { + eDesign = eNewDesign; + switch ( eNewDesign ) { case GD_ORIGINAL: - // fader pFader->setStyleSheet ( "QSlider { width: 45px;" " border-image: url(:/png/fader/res/faderbackground.png) repeat;" @@ -207,21 +221,55 @@ void CChannelFader::SetGUIDesign ( const EGUIDesign eNewDesign ) " padding-bottom: -15px; }" "QSlider::handle { image: url(:/png/fader/res/faderhandle.png); }" ); + pLabelGrid->addWidget ( plblLabel, 0, Qt::AlignVCenter ); // label next to icons + pLabelInstBox->setMinimumHeight ( 52 ); // maximum height of the instrument+flag pictures + pFader->setMinimumHeight ( 120 ); // if this value is too small, the fader might not be movable with the mouse for fancy skin (#292) + pPan->setFixedSize ( 50, 50 ); pPanLabel->setText ( tr ( "PAN" ) ); pcbMute->setText ( tr ( "MUTE" ) ); pcbSolo->setText ( tr ( "SOLO" ) ); - plbrChannelLevel->SetLevelMeterType ( CMultiColorLEDBar::MT_LED ); + strGroupBaseText = tr ( "GRP" ); + plbrChannelLevel->SetLevelMeterType ( CLevelMeter::MT_LED ); + iInstrPicMaxWidth = INVALID_INDEX; // no instrument picture scaling + break; + + case GD_SLIMFADER: + pLabelPictGrid->addWidget ( plblLabel, 0, Qt::AlignHCenter ); // label below icons + pLabelInstBox->setMinimumHeight ( 130 ); // maximum height of the instrument+flag+label + pFader->setMinimumHeight ( 85 ); + pPan->setFixedSize ( 28, 28 ); + pFader->setTickPosition ( QSlider::NoTicks ); + pFader->setStyleSheet ( "" ); + pPanLabel->setText ( tr ( "Pan" ) ); + pcbMute->setText ( tr ( "M" ) ); + pcbSolo->setText ( tr ( "S" ) ); + strGroupBaseText = tr ( "G" ); + plbrChannelLevel->SetLevelMeterType ( CLevelMeter::MT_SLIM_BAR ); + iInstrPicMaxWidth = 18; // scale instrument picture to avoid enlarging the width by the picture break; default: - // reset style sheet and set original paramters + // reset style sheet and set original parameters + pFader->setTickPosition ( QSlider::TicksBothSides ); pFader->setStyleSheet ( "" ); + pLabelGrid->addWidget ( plblLabel, 0, Qt::AlignVCenter ); // label next to icons + pLabelInstBox->setMinimumHeight ( 52 ); // maximum height of the instrument+flag pictures + pFader->setMinimumHeight ( 85 ); + pPan->setFixedSize ( 50, 50 ); pPanLabel->setText ( tr ( "Pan" ) ); pcbMute->setText ( tr ( "Mute" ) ); pcbSolo->setText ( tr ( "Solo" ) ); - plbrChannelLevel->SetLevelMeterType ( CMultiColorLEDBar::MT_BAR ); + strGroupBaseText = tr ( "Grp" ); + plbrChannelLevel->SetLevelMeterType ( CLevelMeter::MT_BAR ); + iInstrPicMaxWidth = INVALID_INDEX; // no instrument picture scaling break; } + + // we need to update since we changed the checkbox text + UpdateGroupIDDependencies(); + + // the instrument picture might need scaling after a style change + SetChannelInfos ( cReceivedChanInfo ); } void CChannelFader::SetDisplayChannelLevel ( const bool eNDCL ) @@ -243,12 +291,38 @@ void CChannelFader::SetDisplayPans ( const bool eNDP ) void CChannelFader::SetupFaderTag ( const ESkillLevel eSkillLevel ) { + // the group ID defines the border color + QString strBorderColor; + + switch ( iGroupID ) + { + case 0: + strBorderColor = "red"; + break; + + case 1: + strBorderColor = "blue"; + break; + + case 2: + strBorderColor = "green"; + break; + + case 3: + strBorderColor = "yellow"; + break; + + default: + strBorderColor = "black"; + break; + } + // setup group box for label/instrument picture: set a thick black border // with nice round edges QString strStile = - "QGroupBox { border: 2px solid black;" - " border-radius: 4px;" - " padding: 3px;"; + "QGroupBox { border: 2px solid " + strBorderColor + ";" + " border-radius: 4px;" + " padding: 3px;"; // the background color depends on the skill level switch ( eSkillLevel ) @@ -287,26 +361,31 @@ void CChannelFader::SetupFaderTag ( const ESkillLevel eSkillLevel ) void CChannelFader::Reset() { + // it is important to reset the group index first (#611) + iGroupID = INVALID_INDEX; + // general initializations SetRemoteFaderIsMute ( false ); // init gain and pan value -> maximum value as definition according to server pFader->setValue ( AUD_MIX_FADER_MAX ); + dPreviousFaderLevel = AUD_MIX_FADER_MAX; pPan->setValue ( AUD_MIX_PAN_MAX / 2 ); - // reset mute/solo check boxes and level meter + // reset mute/solo/group check boxes and level meter pcbMute->setChecked ( false ); pcbSolo->setChecked ( false ); - plbrChannelLevel->setValue ( 0 ); + plbrChannelLevel->SetValue ( 0 ); + plbrChannelLevel->ClipReset(); // clear instrument picture, country flag, tool tips and label text - plblLabel->setText ( "" ); - plblLabel->setToolTip ( "" ); - plblInstrument->setVisible ( false ); - plblInstrument->setToolTip ( "" ); + plblLabel->setText ( "" ); + plblLabel->setToolTip ( "" ); + plblInstrument->setVisible ( false ); + plblInstrument->setToolTip ( "" ); plblCountryFlag->setVisible ( false ); plblCountryFlag->setToolTip ( "" ); - strReceivedName = ""; + cReceivedChanInfo = CChannelInfo(); SetupFaderTag ( SL_NOT_SET ); // set a defined tool tip time out @@ -315,20 +394,38 @@ void CChannelFader::Reset() plblInstrument->setToolTipDuration ( iToolTipDurMs ); plblCountryFlag->setToolTipDuration ( iToolTipDurMs ); - bOtherChannelIsSolo = false; - bIsMyOwnFader = false; + bOtherChannelIsSolo = false; + bIsMyOwnFader = false; + bIsMutedAtServer = false; + iRunningNewClientCnt = 0; + + UpdateGroupIDDependencies(); } -void CChannelFader::SetFaderLevel ( const int iLevel ) +void CChannelFader::SetFaderLevel ( const double dLevel, + const bool bIsGroupUpdate ) { // first make a range check - if ( ( iLevel >= 0 ) && ( iLevel <= AUD_MIX_FADER_MAX ) ) + if ( dLevel >= 0 ) { // we set the new fader level in the GUI (slider control) and also tell the - // server about the change - pFader->setValue ( iLevel ); - SendFaderLevelToServer ( iLevel ); - } + // server about the change (block the signal of the fader since we want to + // call SendFaderLevelToServer with a special additional parameter) + pFader->blockSignals ( true ); + pFader->setValue ( min ( AUD_MIX_FADER_MAX, MathUtils::round ( dLevel ) ) ); + pFader->blockSignals ( false ); + + SendFaderLevelToServer ( min ( static_cast ( AUD_MIX_FADER_MAX ), dLevel ), bIsGroupUpdate ); + + if ( dLevel > AUD_MIX_FADER_MAX ) + { + // If the level is above the maximum, we have to store it for the purpose + // of group fader movement. If you move a fader which has lower volume than + // this one and this clips at max, we want to retain the ratio between this + // fader and the others in the group. + dPreviousFaderLevel = dLevel; + } + } } void CChannelFader::SetPanValue ( const int iPan ) @@ -336,10 +433,10 @@ void CChannelFader::SetPanValue ( const int iPan ) // first make a range check if ( ( iPan >= 0 ) && ( iPan <= AUD_MIX_PAN_MAX ) ) { - // we set the new fader level in the GUI (slider control) and also tell the - // server about the change - pPan->setValue ( iPan ); - SendPanValueToServer ( iPan ); + // we set the new fader level in the GUI (slider control) which then + // emits to signal to tell the server about the change (implicitly) + pPan->setValue ( iPan ); + pPan->setAccessibleName ( QString::number ( iPan ) ); } } @@ -361,32 +458,60 @@ void CChannelFader::SetRemoteFaderIsMute ( const bool bIsMute ) { // show orange utf8 SPEAKER WITH CANCELLATION STROKE (U+1F507) pInfoLabel->setText ( "🔇" ); -//QPixmap CancelledSpeakerPixmap ( QString::fromUtf8 ( ":/png/main/res/speakerwithcancellationstroke.png" ) ); -//pInfoLabel->setPixmap ( CancelledSpeakerPixmap.scaled ( 15, 15, Qt::KeepAspectRatio ) ); } else { pInfoLabel->setText ( "" ); -//pInfoLabel->setPixmap ( QPixmap() ); } } -void CChannelFader::SendFaderLevelToServer ( const int iLevel ) +void CChannelFader::SendFaderLevelToServer ( const double dLevel, + const bool bIsGroupUpdate ) { // if mute flag is set or other channel is on solo, do not apply the new - // fader value (exception: we are on solo, in that case we ignore the - // "other channel is on solo" flag) - if ( ( pcbMute->checkState() == Qt::Unchecked ) && - ( !bOtherChannelIsSolo || IsSolo() ) ) + // fader value to the server (exception: we are on solo, in that case we + // ignore the "other channel is on solo" flag) + const bool bSuppressServerUpdate = !( ( pcbMute->checkState() == Qt::Unchecked ) && + ( !bOtherChannelIsSolo || IsSolo() ) ); + + // emit signal for new fader gain value + emit gainValueChanged ( MathUtils::CalcFaderGain ( static_cast ( dLevel ) ), + bIsMyOwnFader, + bIsGroupUpdate, + bSuppressServerUpdate, + dLevel / dPreviousFaderLevel ); + + // update previous fader level since the level has changed, avoid to use + // the zero value not to have division by zero and also to retain the ratio + // after the fader is moved up again from the zero position + if ( dLevel > 0 ) { - // emit signal for new fader gain value - emit gainValueChanged ( CalcFaderGain ( iLevel ), bIsMyOwnFader ); + dPreviousFaderLevel = dLevel; } } void CChannelFader::SendPanValueToServer ( const int iPan ) -{ - emit panValueChanged ( static_cast ( iPan ) / AUD_MIX_PAN_MAX ); +{ + emit panValueChanged ( static_cast ( iPan ) / AUD_MIX_PAN_MAX ); +} + +void CChannelFader::OnPanValueChanged ( int value ) +{ + // on shift-click the pan shall reset to 0 L/R (#707) + if ( QGuiApplication::keyboardModifiers() == Qt::ShiftModifier ) + { + // correct the value to the center position + value = AUD_MIX_PAN_MAX / 2; + + // set the GUI control in the center position while deactivating + // the signals to avoid an infinite loop + pPan->blockSignals ( true ); + pPan->setValue ( value ); + pPan->blockSignals ( false ); + } + + pPan->setAccessibleName ( QString::number ( value ) ); + SendPanValueToServer ( value ); } void CChannelFader::OnMuteStateChanged ( int value ) @@ -395,20 +520,85 @@ void CChannelFader::OnMuteStateChanged ( int value ) SetMute ( static_cast ( value ) == Qt::Checked ); } +void CChannelFader::SetGroupID ( const int iNGroupID ) +{ + iGroupID = iNGroupID; + UpdateGroupIDDependencies(); +} + +void CChannelFader::UpdateGroupIDDependencies() +{ + // update the group checkbox according the current group ID setting + pcbGroup->blockSignals ( true ); // make sure no signals are fired + if ( iGroupID == INVALID_INDEX ) + { + pcbGroup->setCheckState ( Qt::Unchecked ); + } + else + { + pcbGroup->setCheckState ( Qt::Checked ); + } + pcbGroup->blockSignals ( false ); + + // update group checkbox text + if ( iGroupID != INVALID_INDEX ) + { + pcbGroup->setText ( strGroupBaseText + QString::number ( iGroupID + 1 ) ); + } + else + { + pcbGroup->setText ( strGroupBaseText ); + } + + // if the group is disable for this fader, reset the previous fader level + if ( iGroupID == INVALID_INDEX ) + { + // for the special case that the fader is all the way down, use a small + // value instead + if ( GetFaderLevel() > 0 ) + { + dPreviousFaderLevel = GetFaderLevel(); + } + else + { + dPreviousFaderLevel = 1; // small value + } + } + + // the fader tag border color is set according to the selected group + SetupFaderTag ( cReceivedChanInfo.eSkillLevel ); +} + +void CChannelFader::OnGroupStateChanged ( int ) +{ + // we want a popup menu shown if the user presses the group checkbox but + // we want to make sure that the checkbox state represents the current group + // setting and not the current click state since the user might not click + // on the menu but at one other place and then the popup menu disappears but + // the checkobx state would be on an invalid state + UpdateGroupIDDependencies(); + pGroupPopupMenu->popup ( QCursor::pos() ); +} + void CChannelFader::SetMute ( const bool bState ) { if ( bState ) { - // mute channel -> send gain of 0 - emit gainValueChanged ( 0, bIsMyOwnFader ); + if ( !bIsMutedAtServer ) + { + // mute channel -> send gain of 0 + emit gainValueChanged ( 0, bIsMyOwnFader, false, false, -1 ); // set level ratio to in invalid value + bIsMutedAtServer = true; + } } else { // only unmute if we are not solot but an other channel is on solo - if ( !bOtherChannelIsSolo || IsSolo() ) + if ( ( !bOtherChannelIsSolo || IsSolo() ) && bIsMutedAtServer ) { // mute was unchecked, get current fader value and apply - emit gainValueChanged ( CalcFaderGain ( GetFaderLevel() ), bIsMyOwnFader ); + emit gainValueChanged ( MathUtils::CalcFaderGain ( GetFaderLevel() ), bIsMyOwnFader, false, false, -1 ); // set level ratio to in invalid value + bIsMutedAtServer = false; } } } @@ -428,32 +618,50 @@ void CChannelFader::UpdateSoloState ( const bool bNewOtherSoloState ) void CChannelFader::SetChannelLevel ( const uint16_t iLevel ) { - plbrChannelLevel->setValue ( iLevel ); + plbrChannelLevel->SetValue ( iLevel ); } -void CChannelFader::SetText ( const CChannelInfo& ChanInfo ) +void CChannelFader::SetChannelInfos ( const CChannelInfo& cChanInfo ) { - // store original received name - strReceivedName = ChanInfo.strName; + // store received channel info + cReceivedChanInfo = cChanInfo; + + // init properties for the tool tip + int iTTInstrument = CInstPictures::GetNotUsedInstrument(); + QLocale::Country eTTCountry = QLocale::AnyCountry; - // break text at predefined position - const int iBreakPos = MAX_LEN_FADER_TAG / 2; - QString strModText = ChanInfo.GenNameForDisplay(); + // Label text -------------------------------------------------------------- - if ( strModText.length() > iBreakPos ) + QString strModText = cChanInfo.strName; + + // apply break position and font size depending on the selected design + if ( eDesign == GD_SLIMFADER ) { - strModText.insert ( iBreakPos, QString ( "\n" ) ); + // in slim mode use a non-bold font (smaller width font) + plblLabel->setStyleSheet ( "QLabel { color: black; }" ); + + // break at every 4th character + for ( int iInsPos = 4; iInsPos <= strModText.size() - 1; iInsPos += 4 + 1 ) + { + strModText.insert ( iInsPos, "\n" ); + } } + else + { + // in normal mode use bold font + plblLabel->setStyleSheet ( "QLabel { color: black; font: bold; }" ); - plblLabel->setText ( strModText ); -} + // break text at predefined position + const int iBreakPos = MAX_LEN_FADER_TAG / 2; -void CChannelFader::SetChannelInfos ( const CChannelInfo& cChanInfo ) -{ - // init properties for the tool tip - int iTTInstrument = CInstPictures::GetNotUsedInstrument(); - QLocale::Country eTTCountry = QLocale::AnyCountry; + if ( strModText.length() > iBreakPos ) + { + strModText.insert ( iBreakPos, QString ( "\n" ) ); + } + } + + plblLabel->setText ( strModText ); // Instrument picture ------------------------------------------------------ @@ -471,7 +679,17 @@ void CChannelFader::SetChannelInfos ( const CChannelInfo& cChanInfo ) else { // set correct picture - plblInstrument->setPixmap ( QPixmap ( strCurResourceRef ) ); + QPixmap pixInstr ( strCurResourceRef ); + + if ( ( iInstrPicMaxWidth != INVALID_INDEX ) && ( pixInstr.width() > iInstrPicMaxWidth ) ) + { + // scale instrument picture on request (scale to the width with correct aspect ratio) + plblInstrument->setPixmap ( pixInstr.scaledToWidth ( iInstrPicMaxWidth, Qt::SmoothTransformation ) ); + } + else + { + plblInstrument->setPixmap ( pixInstr ); + } iTTInstrument = cChanInfo.iInstrument; // enable instrument picture @@ -514,12 +732,16 @@ void CChannelFader::SetChannelInfos ( const CChannelInfo& cChanInfo ) // Tool tip ---------------------------------------------------------------- // complete musician profile in the tool tip - QString strToolTip = ""; + QString strToolTip = ""; + QString strAliasAccessible = ""; + QString strInstrumentAccessible = ""; + QString strLocationAccessible = ""; // alias/name - if ( !strReceivedName.isEmpty() ) + if ( !cChanInfo.strName.isEmpty() ) { - strToolTip += "

" + tr ( "Alias/Name" ) + "

" + strReceivedName; + strToolTip += "

" + tr ( "Alias/Name" ) + "

" + cChanInfo.strName; + strAliasAccessible += cChanInfo.strName; } // instrument @@ -527,6 +749,8 @@ void CChannelFader::SetChannelInfos ( const CChannelInfo& cChanInfo ) { strToolTip += "

" + tr ( "Instrument" ) + "

" + CInstPictures::GetName ( iTTInstrument ); + + strInstrumentAccessible += CInstPictures::GetName ( iTTInstrument ); } // location @@ -537,33 +761,44 @@ void CChannelFader::SetChannelInfos ( const CChannelInfo& cChanInfo ) if ( !cChanInfo.strCity.isEmpty() ) { - strToolTip += cChanInfo.strCity; + strToolTip += cChanInfo.strCity; + strLocationAccessible += cChanInfo.strCity; if ( eTTCountry != QLocale::AnyCountry ) { - strToolTip += ", "; + strToolTip += ", "; + strLocationAccessible += ", "; } } if ( eTTCountry != QLocale::AnyCountry ) { - strToolTip += QLocale::countryToString ( eTTCountry ); + strToolTip += QLocale::countryToString ( eTTCountry ); + strLocationAccessible += QLocale::countryToString ( eTTCountry ); } } // skill level + QString strSkillLevel; + switch ( cChanInfo.eSkillLevel ) { case SL_BEGINNER: - strToolTip += "

" + tr ( "Skill Level" ) + "

" + tr ( "Beginner" ); + strSkillLevel = tr ( "Beginner" ); + strToolTip += "

" + tr ( "Skill Level" ) + "

" + strSkillLevel; + strInstrumentAccessible += ", " + strSkillLevel; break; case SL_INTERMEDIATE: - strToolTip += "

" + tr ( "Skill Level" ) + "

" + tr ( "Intermediate" ); + strSkillLevel = tr ( "Intermediate" ); + strToolTip += "

" + tr ( "Skill Level" ) + "

" + strSkillLevel; + strInstrumentAccessible += ", " + strSkillLevel; break; case SL_PROFESSIONAL: - strToolTip += "

" + tr ( "Skill Level" ) + "

" + tr ( "Expert" ); + strSkillLevel = tr ( "Expert" ); + strToolTip += "

" + tr ( "Skill Level" ) + "

" + strSkillLevel; + strInstrumentAccessible += ", " + strSkillLevel; break; case SL_NOT_SET: @@ -577,51 +812,43 @@ void CChannelFader::SetChannelInfos ( const CChannelInfo& cChanInfo ) strToolTip.prepend ( "

" + tr ( "Musician Profile" ) + "

" ); } - plblCountryFlag->setToolTip ( strToolTip ); - plblInstrument->setToolTip ( strToolTip ); - plblLabel->setToolTip ( strToolTip ); -} - -double CChannelFader::CalcFaderGain ( const int value ) -{ - // convert actual slider range in gain values - // and normalize so that maximum gain is 1 - const double dInValueRange0_1 = static_cast ( value ) / AUD_MIX_FADER_MAX; - - // map range from 0..1 to range -35..0 dB and calculate linear gain - if ( value == 0 ) - { - return 0; // -infinity - } - else - { - return pow ( 10, ( dInValueRange0_1 * 35 - 35 ) / 20 ); - } + plblCountryFlag->setToolTip ( strToolTip ); + plblCountryFlag->setAccessibleDescription ( strLocationAccessible ); + plblInstrument->setToolTip ( strToolTip ); + plblInstrument->setAccessibleDescription ( strInstrumentAccessible ); + plblLabel->setToolTip ( strToolTip ); + plblLabel->setAccessibleName ( strAliasAccessible ); + plblLabel->setAccessibleDescription ( tr ( "Alias" ) ); } /******************************************************************************\ * CAudioMixerBoard * \******************************************************************************/ -CAudioMixerBoard::CAudioMixerBoard ( QWidget* parent, Qt::WindowFlags ) : +CAudioMixerBoard::CAudioMixerBoard ( QWidget* parent ) : QGroupBox ( parent ), - vecStoredFaderTags ( MAX_NUM_STORED_FADER_SETTINGS, "" ), - vecStoredFaderLevels ( MAX_NUM_STORED_FADER_SETTINGS, AUD_MIX_FADER_MAX ), - vecStoredPanValues ( MAX_NUM_STORED_FADER_SETTINGS, AUD_MIX_PAN_MAX / 2 ), - vecStoredFaderIsSolo ( MAX_NUM_STORED_FADER_SETTINGS, false ), - vecStoredFaderIsMute ( MAX_NUM_STORED_FADER_SETTINGS, false ), - iNewClientFaderLevel ( 100 ), + pSettings ( nullptr ), bDisplayPans ( false ), bIsPanSupported ( false ), bNoFaderVisible ( true ), iMyChannelID ( INVALID_INDEX ), - strServerName ( "" ) + iRunningNewClientCnt ( 0 ), + iNumMixerPanelRows ( 1 ), + strServerName ( "" ), + eRecorderState ( RS_UNDEFINED ), + eChSortType ( ST_NO_SORT ) { // add group box and hboxlayout QHBoxLayout* pGroupBoxLayout = new QHBoxLayout ( this ); QWidget* pMixerWidget = new QWidget(); // will be added to the scroll area which is then the parent pScrollArea = new CMixerBoardScrollArea ( this ); - pMainLayout = new QHBoxLayout ( pMixerWidget ); + pMainLayout = new QGridLayout ( pMixerWidget ); + + setAccessibleName ( "Personal Mix at the Server groupbox" ); + setWhatsThis ( "" + tr ( "Personal Mix at the Server" ) + ": " + tr ( + "When connected to a server, the controls here allow you to set your " + "local mix without affecting what others hear from you. The title shows " + "the server name and, when known, whether it is actively recording." ) ); // set title text (default: no server given) SetServerName ( "" ); @@ -631,53 +858,61 @@ CAudioMixerBoard::CAudioMixerBoard ( QWidget* parent, Qt::WindowFlags ) : for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) { - vecpChanFader[i] = new CChannelFader ( this, pMainLayout ); + vecpChanFader[i] = new CChannelFader ( this ); vecpChanFader[i]->Hide(); } - // insert horizontal spacer - pMainLayout->addItem ( new QSpacerItem ( 0, 0, QSizePolicy::Expanding ) ); + // insert horizontal spacer (at position MAX_NUM_CHANNELS+1 which is index MAX_NUM_CHANNELS) + pMainLayout->addItem ( new QSpacerItem ( 0, 0, QSizePolicy::Expanding ), 0, MAX_NUM_CHANNELS ); // set margins of the layout to zero to get maximum space for the controls - pGroupBoxLayout->setContentsMargins ( 0, 0, 0, 1 ); // note: to avoid problems at the botton, use a small margin for that + pGroupBoxLayout->setContentsMargins ( 0, 0, 0, 1 ); // note: to avoid problems at the bottom, use a small margin for that // add the group box to the scroll area - pScrollArea->setMinimumWidth ( 200 ); // at least two faders shall be visible - pScrollArea->setWidget ( pMixerWidget ); + pScrollArea->setMinimumWidth ( 200 ); // at least two faders shall be visible + pScrollArea->setWidget ( pMixerWidget ); pScrollArea->setWidgetResizable ( true ); // make sure it fills the entire scroll area - pScrollArea->setFrameShape ( QFrame::NoFrame ); - pGroupBoxLayout->addWidget ( pScrollArea ); + pScrollArea->setFrameShape ( QFrame::NoFrame ); + pGroupBoxLayout->addWidget ( pScrollArea ); // Connections ------------------------------------------------------------- connectFaderSignalsToMixerBoardSlots(); } +CAudioMixerBoard::~CAudioMixerBoard() +{ + for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) + { + delete vecpChanFader[i]; + } +} + template inline void CAudioMixerBoard::connectFaderSignalsToMixerBoardSlots() { int iCurChanID = slotId - 1; - void ( CAudioMixerBoard::* pGainValueChanged )( double, bool ) = + void ( CAudioMixerBoard::* pGainValueChanged )( float, bool, bool, bool, double ) = &CAudioMixerBoardSlots::OnChGainValueChanged; - void ( CAudioMixerBoard::* pPanValueChanged )( double ) = + void ( CAudioMixerBoard::* pPanValueChanged )( float ) = &CAudioMixerBoardSlots::OnChPanValueChanged; QObject::connect ( vecpChanFader[iCurChanID], &CChannelFader::soloStateChanged, - this, &CAudioMixerBoard::UpdateSoloStates ); + this, &CAudioMixerBoard::UpdateSoloStates ); QObject::connect ( vecpChanFader[iCurChanID], &CChannelFader::gainValueChanged, - this, pGainValueChanged ); + this, pGainValueChanged ); QObject::connect ( vecpChanFader[iCurChanID], &CChannelFader::panValueChanged, - this, pPanValueChanged ); + this, pPanValueChanged ); connectFaderSignalsToMixerBoardSlots(); -}; +} template<> -inline void CAudioMixerBoard::connectFaderSignalsToMixerBoardSlots<0>() {}; +inline void CAudioMixerBoard::connectFaderSignalsToMixerBoardSlots<0>() {} void CAudioMixerBoard::SetServerName ( const QString& strNewServerName ) { @@ -702,26 +937,20 @@ void CAudioMixerBoard::SetServerName ( const QString& strNewServerName ) void CAudioMixerBoard::SetGUIDesign ( const EGUIDesign eNewDesign ) { - // apply GUI design to child GUI controls - for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) + // move the channels tighter together in slim fader mode + if ( eNewDesign == GD_SLIMFADER ) { - vecpChanFader[i]->SetGUIDesign ( eNewDesign ); + pMainLayout->setSpacing ( 2 ); + } + else + { + pMainLayout->setSpacing ( 6 ); // Qt default spacing value } -} - -void CAudioMixerBoard::SetDisplayChannelLevels ( const bool eNDCL ) -{ - bDisplayChannelLevels = eNDCL; - // only update hiding the levels immediately, showing the levels - // is only applied if the server actually transmits levels - if ( !bDisplayChannelLevels ) + // apply GUI design to child GUI controls + for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) { - // hide all level meters - for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) - { - vecpChanFader[i]->SetDisplayChannelLevel ( false ); - } + vecpChanFader[i]->SetGUIDesign ( eNewDesign ); } } @@ -743,131 +972,261 @@ void CAudioMixerBoard::SetPanIsSupported() void CAudioMixerBoard::HideAll() { + // before hiding the faders, store their settings + StoreAllFaderSettings(); + // make all controls invisible for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) { - // before hiding the fader, store its level (if some conditions are fullfilled) - StoreFaderSettings ( vecpChanFader[i] ); - vecpChanFader[i]->SetChannelLevel ( 0 ); vecpChanFader[i]->SetDisplayChannelLevel ( false ); vecpChanFader[i]->SetDisplayPans ( false ); vecpChanFader[i]->Hide(); } - // set flags - bIsPanSupported = false; - bNoFaderVisible = true; - iMyChannelID = INVALID_INDEX; + // initialize flags and other parameters + bIsPanSupported = false; + bNoFaderVisible = true; + eRecorderState = RS_UNDEFINED; + iMyChannelID = INVALID_INDEX; + iRunningNewClientCnt = 0; // reset running counter on new server connection + + // use original order of channel (by server ID) + ChangeFaderOrder ( ST_NO_SORT ); // emit status of connected clients emit NumClientsChanged ( 0 ); // -> no clients connected } -void CAudioMixerBoard::ApplyNewConClientList ( CVector& vecChanInfo ) +void CAudioMixerBoard::SetNumMixerPanelRows ( const int iNNumMixerPanelRows ) { - // we want to set the server name only if the very first faders appear - // in the audio mixer board to show a "try to connect" before - if ( bNoFaderVisible ) + // store new value and immediately initiate the sorting + iNumMixerPanelRows = iNNumMixerPanelRows; + ChangeFaderOrder ( eChSortType ); +} + +void CAudioMixerBoard::SetFaderSorting ( const EChSortType eNChSortType ) +{ + // store new sort type and update the fader order + eChSortType = eNChSortType; + ChangeFaderOrder ( eNChSortType ); +} + +void CAudioMixerBoard::ChangeFaderOrder ( const EChSortType eChSortType ) +{ + QMutexLocker locker ( &Mutex ); + + // create a pair list of lower strings and fader ID for each channel + QList > PairList; + int iNumVisibleFaders = 0; + + for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) { - setTitle ( tr ( "Personal Mix at the Server: " ) + strServerName ); + if ( eChSortType == ST_BY_NAME ) + { + PairList << QPair ( vecpChanFader[i]->GetReceivedName().toLower(), i ); + } + else if ( eChSortType == ST_BY_CITY ) + { + PairList << QPair ( vecpChanFader[i]->GetReceivedCity().toLower(), i ); + } + else if ( eChSortType == ST_BY_INSTRUMENT ) + { + // sort first "by instrument" and second "by name" by adding the name after the instrument + PairList << QPair ( CInstPictures::GetName ( vecpChanFader[i]->GetReceivedInstrument() ) + + vecpChanFader[i]->GetReceivedName().toLower(), i ); + } + else if ( eChSortType == ST_BY_GROUPID ) + { + if ( vecpChanFader[i]->GetGroupID() == INVALID_INDEX ) + { + // put channels without a group at the end + PairList << QPair ( "z", i ); // group IDs are numbers, use letter to put it at the end + } + else + { + PairList << QPair ( QString::number ( vecpChanFader[i]->GetGroupID() ), i ); + } + } + else // ST_NO_SORT + { + // per definition for no sort: faders are sorted in the order they appeared (note that we + // pad to a total of 11 characters with zeros to make sure the sorting is done correctly) + PairList << QPair ( QString ( "%1" ).arg ( + vecpChanFader[i]->GetRunningNewClientCnt(), 11, 10, QLatin1Char ( '0' ) ), i ); + } + + // count the number of visible faders + if ( vecpChanFader[i]->IsVisible() ) + { + iNumVisibleFaders++; + } + } + + // sort the channels according to the first of the pair + std::stable_sort ( PairList.begin(), PairList.end() ); + + // calculate the number of the faders in the first row by distribute + // the faders equally in the available number of rows + const int iNumFadersFirstRow = ( iNumVisibleFaders + 1 ) / iNumMixerPanelRows; + + // add channels to the layout in the new order, note that it is not required to remove + // the widget from the layout first but it is moved to the new position automatically + int iVisibleFaderCnt = 0; + + for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) + { + const int iCurFaderID = PairList[i].second; + + if ( vecpChanFader[iCurFaderID]->IsVisible() ) + { + // per definition: the fader order is colum-first/row-second (note that + // the value in iNumFadersFirstRow defines how many rows we will get) + pMainLayout->addWidget ( vecpChanFader[iCurFaderID]->GetMainWidget(), + iVisibleFaderCnt / iNumFadersFirstRow, + iVisibleFaderCnt % iNumFadersFirstRow ); + + iVisibleFaderCnt++; + } } +} + +void CAudioMixerBoard::UpdateTitle() +{ + QString strTitlePrefix = ""; + + if ( eRecorderState == RS_RECORDING ) + { + strTitlePrefix = "[" + tr ( "RECORDING ACTIVE" ) + "] "; + } + + setTitle ( strTitlePrefix + tr ( "Personal Mix at: " ) + strServerName ); + setAccessibleName ( title() ); +} + +void CAudioMixerBoard::SetRecorderState ( const ERecorderState newRecorderState ) +{ + // store the new recorder state and update the title + eRecorderState = newRecorderState; + UpdateTitle(); +} +void CAudioMixerBoard::ApplyNewConClientList ( CVector& vecChanInfo ) +{ // get number of connected clients const int iNumConnectedClients = vecChanInfo.Size(); - // search for channels with are already present and preserve their gain - // setting, for all other channels reset gain - for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) + Mutex.lock(); { - bool bFaderIsUsed = false; + // we want to set the server name only if the very first faders appear + // in the audio mixer board to show a "try to connect" before + if ( bNoFaderVisible ) + { + UpdateTitle(); + } - for ( int j = 0; j < iNumConnectedClients; j++ ) + // search for channels with are already present and preserve their gain + // setting, for all other channels reset gain + for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) { - // check if current fader is used - if ( vecChanInfo[j].iChanID == i ) + bool bFaderIsUsed = false; + + for ( int j = 0; j < iNumConnectedClients; j++ ) { - // check if fader was already in use -> preserve gain value - if ( !vecpChanFader[i]->IsVisible() ) + // check if current fader is used + if ( vecChanInfo[j].iChanID == i ) { - // the fader was not in use, reset everything for new client - vecpChanFader[i]->Reset(); - - // check if this is my own fader and set fader property - if ( i == iMyChannelID ) + // check if fader was already in use -> preserve gain value + if ( !vecpChanFader[i]->IsVisible() ) { - vecpChanFader[i]->SetIsMyOwnFader(); + // the fader was not in use, reset everything for new client + vecpChanFader[i]->Reset(); + + // check if this is my own fader and set fader property + if ( i == iMyChannelID ) + { + vecpChanFader[i]->SetIsMyOwnFader(); + } + + // set and increment the running new client counter needed for sorting (per definition: + // a fader for a new client shall always be inserted at the right-hand-side if no other + // sorting type is selected (i.e. "no sorting" is active) (#673) + vecpChanFader[i]->SetRunningNewClientCnt ( iRunningNewClientCnt++ ); + + // show fader + vecpChanFader[i]->Show(); + + // Set the default initial fader level. Check first that + // this is not the initialization (i.e. previously there + // were no faders visible) to avoid that our own level is + // adjusted. If we have received our own channel ID, then + // we can adjust the level even if no fader was visible. + // The fader level of 100 % is the default in the + // server, in that case we do not have to do anything here. + if ( ( !bNoFaderVisible || + ( ( iMyChannelID != INVALID_INDEX ) && ( iMyChannelID != i ) ) ) && + ( pSettings->iNewClientFaderLevel != 100 ) ) + { + // the value is in percent -> convert range + vecpChanFader[i]->SetFaderLevel ( + pSettings->iNewClientFaderLevel / 100.0 * AUD_MIX_FADER_MAX ); + } } - // show fader - vecpChanFader[i]->Show(); - - // Set the default initial fader level. Check first that - // this is not the initialization (i.e. previously there - // were no faders visible) to avoid that our own level is - // adjusted. If we have received our own channel ID, then - // we can adjust the level even if no fader was visible. - // The fader level of 100 % is the default in the - // server, in that case we do not have to do anything here. - if ( ( !bNoFaderVisible || - ( ( iMyChannelID != INVALID_INDEX ) && ( iMyChannelID != i ) ) ) && - ( iNewClientFaderLevel != 100 ) ) + // restore gain (if new name is different from the current one) + if ( vecpChanFader[i]->GetReceivedName().compare ( vecChanInfo[j].strName ) ) { - // the value is in percent -> convert range - vecpChanFader[i]->SetFaderLevel ( static_cast ( - iNewClientFaderLevel / 100.0 * AUD_MIX_FADER_MAX ) ); + // the text has actually changed, search in the list of + // stored settings if we have a matching entry + int iStoredFaderLevel; + int iStoredPanValue; + bool bStoredFaderIsSolo; + bool bStoredFaderIsMute; + int iGroupID; + + if ( GetStoredFaderSettings ( vecChanInfo[j].strName, + iStoredFaderLevel, + iStoredPanValue, + bStoredFaderIsSolo, + bStoredFaderIsMute, + iGroupID ) ) + { + vecpChanFader[i]->SetFaderLevel ( iStoredFaderLevel, true ); // suppress group update + vecpChanFader[i]->SetPanValue ( iStoredPanValue ); + vecpChanFader[i]->SetFaderIsSolo ( bStoredFaderIsSolo ); + vecpChanFader[i]->SetFaderIsMute ( bStoredFaderIsMute ); + vecpChanFader[i]->SetGroupID ( iGroupID ); // Must be the last to be set in the fader! + } } - } - // restore gain (if new name is different from the current one) - if ( vecpChanFader[i]->GetReceivedName().compare ( vecChanInfo[j].strName ) ) - { - // the text has actually changed, search in the list of - // stored settings if we have a matching entry - int iStoredFaderLevel; - int iStoredPanValue; - bool bStoredFaderIsSolo; - bool bStoredFaderIsMute; - - if ( GetStoredFaderSettings ( vecChanInfo[j], - iStoredFaderLevel, - iStoredPanValue, - bStoredFaderIsSolo, - bStoredFaderIsMute ) ) - { - vecpChanFader[i]->SetFaderLevel ( iStoredFaderLevel ); - vecpChanFader[i]->SetPanValue ( iStoredPanValue ); - vecpChanFader[i]->SetFaderIsSolo ( bStoredFaderIsSolo ); - vecpChanFader[i]->SetFaderIsMute ( bStoredFaderIsMute ); - } - } + // set the channel infos + vecpChanFader[i]->SetChannelInfos ( vecChanInfo[j] ); - // set the text in the fader - vecpChanFader[i]->SetText ( vecChanInfo[j] ); + bFaderIsUsed = true; + } + } - // update other channel infos - vecpChanFader[i]->SetChannelInfos ( vecChanInfo[j] ); + // if current fader is not used, hide it + if ( !bFaderIsUsed ) + { + // before hiding the fader, store its level (if some conditions are fulfilled) + StoreFaderSettings ( vecpChanFader[i] ); - bFaderIsUsed = true; + vecpChanFader[i]->Hide(); } } - // if current fader is not used, hide it - if ( !bFaderIsUsed ) - { - // before hiding the fader, store its level (if some conditions are fullfilled) - StoreFaderSettings ( vecpChanFader[i] ); + // update the solo states since if any channel was on solo and a new client + // has just connected, the new channel must be muted + UpdateSoloStates(); - vecpChanFader[i]->Hide(); - } + // update flag for "all faders are invisible" + bNoFaderVisible = ( iNumConnectedClients == 0 ); } + Mutex.unlock(); // release mutex - // update the solo states since if any channel was on solo and a new client - // has just connected, the new channel must be muted - UpdateSoloStates(); - - // update flag for "all faders are invisible" - bNoFaderVisible = ( iNumConnectedClients == 0 ); + // sort the channels according to the selected sorting type + ChangeFaderOrder ( eChSortType ); // emit status of connected clients emit NumClientsChanged ( iNumConnectedClients ); @@ -886,6 +1245,62 @@ void CAudioMixerBoard::SetFaderLevel ( const int iChannelIdx, } } +void CAudioMixerBoard::SetAllFaderLevelsToNewClientLevel() +{ + QMutexLocker locker ( &Mutex ); + + for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) + { + // only apply to visible faders and not to my own channel fader + if ( vecpChanFader[i]->IsVisible() && ( i != iMyChannelID ) ) + { + // the value is in percent -> convert range, also use the group + // update flag to make sure the group values are all set to the + // same fader level now + vecpChanFader[i]->SetFaderLevel ( + pSettings->iNewClientFaderLevel / 100.0 * AUD_MIX_FADER_MAX, true ); + } + } +} + +void CAudioMixerBoard::StoreAllFaderSettings() +{ + QMutexLocker locker ( &Mutex ); + + for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) + { + StoreFaderSettings ( vecpChanFader[i] ); + } +} + +void CAudioMixerBoard::LoadAllFaderSettings() +{ + QMutexLocker locker ( &Mutex ); + + int iStoredFaderLevel; + int iStoredPanValue; + bool bStoredFaderIsSolo; + bool bStoredFaderIsMute; + int iGroupID; + + for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) + { + if ( GetStoredFaderSettings ( vecpChanFader[i]->GetReceivedName(), + iStoredFaderLevel, + iStoredPanValue, + bStoredFaderIsSolo, + bStoredFaderIsMute, + iGroupID ) ) + { + vecpChanFader[i]->SetFaderLevel ( iStoredFaderLevel, true ); // suppress group update + vecpChanFader[i]->SetPanValue ( iStoredPanValue ); + vecpChanFader[i]->SetFaderIsSolo ( bStoredFaderIsSolo ); + vecpChanFader[i]->SetFaderIsMute ( bStoredFaderIsMute ); + vecpChanFader[i]->SetGroupID ( iGroupID ); // Must be the last to be set in the fader! + } + } +} + void CAudioMixerBoard::SetRemoteFaderIsMute ( const int iChannelIdx, const bool bIsMute ) { @@ -925,16 +1340,43 @@ void CAudioMixerBoard::UpdateSoloStates() } void CAudioMixerBoard::UpdateGainValue ( const int iChannelIdx, - const double dValue, - const bool bIsMyOwnFader ) + const float fValue, + const bool bIsMyOwnFader, + const bool bIsGroupUpdate, + const bool bSuppressServerUpdate, + const double dLevelRatio ) { - emit ChangeChanGain ( iChannelIdx, dValue, bIsMyOwnFader ); + // update current gain + if ( !bSuppressServerUpdate ) + { + emit ChangeChanGain ( iChannelIdx, fValue, bIsMyOwnFader ); + } + + // if this fader is selected, all other in the group must be updated as + // well (note that we do not have to update if this is already a group update + // to avoid an infinite loop) + if ( ( vecpChanFader[iChannelIdx]->GetGroupID() != INVALID_INDEX ) && !bIsGroupUpdate ) + { + for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) + { + // update rest of faders selected + if ( vecpChanFader[i]->IsVisible() && + ( vecpChanFader[i]->GetGroupID() == vecpChanFader[iChannelIdx]->GetGroupID() ) && + ( i != iChannelIdx ) && + ( dLevelRatio >= 0 ) ) + { + // synchronize faders with moving fader level (it is important + // to set the group flag to avoid infinite looping) + vecpChanFader[i]->SetFaderLevel ( vecpChanFader[i]->GetPreviousFaderLevel() * dLevelRatio, true ); + } + } + } } -void CAudioMixerBoard::UpdatePanValue ( const int iChannelIdx, - const double dValue ) +void CAudioMixerBoard::UpdatePanValue ( const int iChannelIdx, + const float fValue ) { - emit ChangeChanPan ( iChannelIdx, dValue ); + emit ChangeChanPan ( iChannelIdx, fValue ); } void CAudioMixerBoard::StoreFaderSettings ( CChannelFader* pChanFader ) @@ -943,25 +1385,22 @@ void CAudioMixerBoard::StoreFaderSettings ( CChannelFader* pChanFader ) if ( pChanFader->IsVisible() && !pChanFader->GetReceivedName().isEmpty() ) { - CVector viOldStoredFaderLevels ( vecStoredFaderLevels ); - CVector viOldStoredPanValues ( vecStoredPanValues ); - CVector vbOldStoredFaderIsSolo ( vecStoredFaderIsSolo ); - CVector vbOldStoredFaderIsMute ( vecStoredFaderIsMute ); - - // init temporary list count (may be overwritten later on) - int iTempListCnt = 0; + CVector viOldStoredFaderLevels ( pSettings->vecStoredFaderLevels ); + CVector viOldStoredPanValues ( pSettings->vecStoredPanValues ); + CVector vbOldStoredFaderIsSolo ( pSettings->vecStoredFaderIsSolo ); + CVector vbOldStoredFaderIsMute ( pSettings->vecStoredFaderIsMute ); + CVector vbOldStoredFaderGroupID ( pSettings->vecStoredFaderGroupID ); // put new value on the top of the list - const int iOldIdx = - vecStoredFaderTags.StringFiFoWithCompare ( pChanFader->GetReceivedName(), - true ); + const int iOldIdx = pSettings->vecStoredFaderTags.StringFiFoWithCompare ( pChanFader->GetReceivedName() ); // current fader level and solo state is at the top of the list - vecStoredFaderLevels[0] = pChanFader->GetFaderLevel(); - vecStoredPanValues[0] = pChanFader->GetPanValue(); - vecStoredFaderIsSolo[0] = pChanFader->IsSolo(); - vecStoredFaderIsMute[0] = pChanFader->IsMute(); - iTempListCnt = 1; + pSettings->vecStoredFaderLevels[0] = pChanFader->GetFaderLevel(); + pSettings->vecStoredPanValues[0] = pChanFader->GetPanValue(); + pSettings->vecStoredFaderIsSolo[0] = pChanFader->IsSolo(); + pSettings->vecStoredFaderIsMute[0] = pChanFader->IsMute(); + pSettings->vecStoredFaderGroupID[0] = pChanFader->GetGroupID(); + int iTempListCnt = 1; // current fader is on top, other faders index start at 1 for ( int iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ ) { @@ -973,10 +1412,11 @@ void CAudioMixerBoard::StoreFaderSettings ( CChannelFader* pChanFader ) // index in case the entry was not present in the vector before if ( iIdx != iOldIdx ) { - vecStoredFaderLevels[iTempListCnt] = viOldStoredFaderLevels[iIdx]; - vecStoredPanValues[iTempListCnt] = viOldStoredPanValues[iIdx]; - vecStoredFaderIsSolo[iTempListCnt] = vbOldStoredFaderIsSolo[iIdx]; - vecStoredFaderIsMute[iTempListCnt] = vbOldStoredFaderIsMute[iIdx]; + pSettings->vecStoredFaderLevels[iTempListCnt] = viOldStoredFaderLevels[iIdx]; + pSettings->vecStoredPanValues[iTempListCnt] = viOldStoredPanValues[iIdx]; + pSettings->vecStoredFaderIsSolo[iTempListCnt] = vbOldStoredFaderIsSolo[iIdx]; + pSettings->vecStoredFaderIsMute[iTempListCnt] = vbOldStoredFaderIsMute[iIdx]; + pSettings->vecStoredFaderGroupID[iTempListCnt] = vbOldStoredFaderGroupID[iIdx]; iTempListCnt++; } @@ -985,25 +1425,27 @@ void CAudioMixerBoard::StoreFaderSettings ( CChannelFader* pChanFader ) } } -bool CAudioMixerBoard::GetStoredFaderSettings ( const CChannelInfo& ChanInfo, - int& iStoredFaderLevel, - int& iStoredPanValue, - bool& bStoredFaderIsSolo, - bool& bStoredFaderIsMute) +bool CAudioMixerBoard::GetStoredFaderSettings ( const QString& strName, + int& iStoredFaderLevel, + int& iStoredPanValue, + bool& bStoredFaderIsSolo, + bool& bStoredFaderIsMute, + int& iGroupID ) { // only do the check if the name string is not empty - if ( !ChanInfo.strName.isEmpty() ) + if ( !strName.isEmpty() ) { for ( int iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ ) { // check if fader text is already known in the list - if ( !vecStoredFaderTags[iIdx].compare ( ChanInfo.strName ) ) + if ( !pSettings->vecStoredFaderTags[iIdx].compare ( strName ) ) { // copy stored settings values - iStoredFaderLevel = vecStoredFaderLevels[iIdx]; - iStoredPanValue = vecStoredPanValues[iIdx]; - bStoredFaderIsSolo = vecStoredFaderIsSolo[iIdx] != 0; - bStoredFaderIsMute = vecStoredFaderIsMute[iIdx] != 0; + iStoredFaderLevel = pSettings->vecStoredFaderLevels[iIdx]; + iStoredPanValue = pSettings->vecStoredPanValues[iIdx]; + bStoredFaderIsSolo = pSettings->vecStoredFaderIsSolo[iIdx] != 0; + bStoredFaderIsMute = pSettings->vecStoredFaderIsMute[iIdx] != 0; + iGroupID = pSettings->vecStoredFaderGroupID[iIdx]; // values found and copied, return OK return true; @@ -1022,13 +1464,13 @@ void CAudioMixerBoard::SetChannelLevels ( const CVector& vecChannelLev for ( int iChId = 0; iChId < MAX_NUM_CHANNELS; iChId++ ) { - if ( vecpChanFader[iChId]->IsVisible() && i < iNumChannelLevels ) + if ( vecpChanFader[iChId]->IsVisible() && ( i < iNumChannelLevels ) ) { vecpChanFader[iChId]->SetChannelLevel ( vecChannelLevel[i++] ); // show level only if we successfully received levels from the // server (if server does not support levels, do not show levels) - if ( bDisplayChannelLevels && !vecpChanFader[iChId]->GetDisplayChannelLevel() ) + if ( !vecpChanFader[iChId]->GetDisplayChannelLevel() ) { vecpChanFader[iChId]->SetDisplayChannelLevel ( true ); } diff --git a/src/audiomixerboard.h b/src/audiomixerboard.h old mode 100644 new mode 100755 index 2a18c704dd..b67a11d5ab --- a/src/audiomixerboard.h +++ b/src/audiomixerboard.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -35,9 +35,13 @@ #include #include #include +#include +#include +#include #include "global.h" #include "util.h" -#include "multicolorledbar.h" +#include "levelmeter.h" +#include "settings.h" /* Classes ********************************************************************/ @@ -46,71 +50,105 @@ class CChannelFader : public QObject Q_OBJECT public: - CChannelFader ( QWidget* pNW, QHBoxLayout* pParentLayout ); - - void SetText ( const CChannelInfo& ChanInfo ); - QString GetReceivedName() { return strReceivedName; } - void SetChannelInfos ( const CChannelInfo& cChanInfo ); - void Show() { pFrame->show(); } - void Hide() { pFrame->hide(); } - bool IsVisible() { return !pFrame->isHidden(); } - bool IsSolo() { return pcbSolo->isChecked(); } - bool IsMute() { return pcbMute->isChecked(); } - void SetGUIDesign ( const EGUIDesign eNewDesign ); - void SetDisplayChannelLevel ( const bool eNDCL ); - bool GetDisplayChannelLevel(); - void SetDisplayPans ( const bool eNDP ); - - void UpdateSoloState ( const bool bNewOtherSoloState ); - void SetFaderLevel ( const int iLevel ); + CChannelFader ( QWidget* pNW ); + + QString GetReceivedName() { return cReceivedChanInfo.strName; } + int GetReceivedInstrument() { return cReceivedChanInfo.iInstrument; } + QString GetReceivedCity() { return cReceivedChanInfo.strCity; } + void SetChannelInfos ( const CChannelInfo& cChanInfo ); + void Show() { pFrame->show(); } + void Hide() { pFrame->hide(); } + bool IsVisible() { return !pFrame->isHidden(); } + bool IsSolo() { return pcbSolo->isChecked(); } + bool IsMute() { return pcbMute->isChecked(); } + int GetGroupID() { return iGroupID; } + void SetGUIDesign ( const EGUIDesign eNewDesign ); + void SetDisplayChannelLevel ( const bool eNDCL ); + bool GetDisplayChannelLevel(); + void SetDisplayPans ( const bool eNDP ); + QFrame* GetMainWidget() { return pFrame; } + void SetPanValue ( const int iPan ); void SetFaderIsSolo ( const bool bIsSolo ); void SetFaderIsMute ( const bool bIsMute ); + void SetGroupID ( const int iNGroupID ); void SetRemoteFaderIsMute ( const bool bIsMute ); - int GetFaderLevel() { return pFader->value(); } - int GetPanValue() { return pPan->value(); } - void Reset(); - void SetChannelLevel ( const uint16_t iLevel ); - void SetIsMyOwnFader() { bIsMyOwnFader = true; } + void SetFaderLevel ( const double dLevel, + const bool bIsGroupUpdate = false ); + + int GetFaderLevel() { return pFader->value(); } + double GetPreviousFaderLevel() { return dPreviousFaderLevel; } + int GetPanValue() { return pPan->value(); } + void Reset(); + void SetRunningNewClientCnt ( const int iNRunningNewClientCnt ) { iRunningNewClientCnt = iNRunningNewClientCnt; } + int GetRunningNewClientCnt() { return iRunningNewClientCnt; } + void SetChannelLevel ( const uint16_t iLevel ); + void SetIsMyOwnFader() { bIsMyOwnFader = true; } + void UpdateSoloState ( const bool bNewOtherSoloState ); protected: - double CalcFaderGain ( const int value ); + void UpdateGroupIDDependencies(); void SetMute ( const bool bState ); - void SendFaderLevelToServer ( const int iLevel ); - void SendPanValueToServer ( const int iPan ); void SetupFaderTag ( const ESkillLevel eSkillLevel ); - - QFrame* pFrame; - - QWidget* pLevelsBox; - QWidget* pMuteSoloBox; - CMultiColorLEDBar* plbrChannelLevel; - QSlider* pFader; - QDial* pPan; - QLabel* pPanLabel; - QLabel* pInfoLabel; - - QCheckBox* pcbMute; - QCheckBox* pcbSolo; - - QGroupBox* pLabelInstBox; - QLabel* plblLabel; - QLabel* plblInstrument; - QLabel* plblCountryFlag; - - QString strReceivedName; - - bool bOtherChannelIsSolo; - bool bIsMyOwnFader; + void SendPanValueToServer ( const int iPan ); + void SendFaderLevelToServer ( const double dLevel, + const bool bIsGroupUpdate ); + + QFrame* pFrame; + + QWidget* pLevelsBox; + QWidget* pMuteSoloBox; + CLevelMeter* plbrChannelLevel; + QSlider* pFader; + QDial* pPan; + QLabel* pPanLabel; + QLabel* pInfoLabel; + QHBoxLayout* pLabelGrid; + QVBoxLayout* pLabelPictGrid; + + QCheckBox* pcbMute; + QCheckBox* pcbSolo; + QCheckBox* pcbGroup; + QMenu* pGroupPopupMenu; + + QGroupBox* pLabelInstBox; + QLabel* plblLabel; + QLabel* plblInstrument; + QLabel* plblCountryFlag; + + CChannelInfo cReceivedChanInfo; + + bool bOtherChannelIsSolo; + bool bIsMyOwnFader; + bool bIsMutedAtServer; + double dPreviousFaderLevel; + int iGroupID; + QString strGroupBaseText; + int iRunningNewClientCnt; + int iInstrPicMaxWidth; + EGUIDesign eDesign; public slots: - void OnLevelValueChanged ( int value ) { SendFaderLevelToServer ( value ); } - void OnPanValueChanged ( int value ) { SendPanValueToServer ( value ); } + void OnLevelValueChanged ( int value ) { SendFaderLevelToServer ( value, QGuiApplication::keyboardModifiers() == Qt::ShiftModifier ); /* isolate a channel from the group temporarily with shift-click-drag (#695) */ } + + void OnPanValueChanged ( int value ); void OnMuteStateChanged ( int value ); + void OnGroupStateChanged ( int ); + + void OnGroupMenuGrpNone() { SetGroupID ( INVALID_INDEX ); } + void OnGroupMenuGrp1() { SetGroupID ( 0 ); } + void OnGroupMenuGrp2() { SetGroupID ( 1 ); } + void OnGroupMenuGrp3() { SetGroupID ( 2 ); } + void OnGroupMenuGrp4() { SetGroupID ( 3 ); } signals: - void gainValueChanged ( double value, bool bIsMyOwnFader ); - void panValueChanged ( double value ); + void gainValueChanged ( float value, + bool bIsMyOwnFader, + bool bIsGroupUpdate, + bool bSuppressServerUpdate, + double dLevelRatio ); + + void panValueChanged ( float value ); void soloStateChanged ( int value ); }; @@ -118,15 +156,29 @@ template class CAudioMixerBoardSlots : public CAudioMixerBoardSlots { public: - void OnChGainValueChanged ( double dValue, bool bIsMyOwnFader ) { UpdateGainValue ( slotId - 1, dValue, bIsMyOwnFader ); } - void OnChPanValueChanged ( double dValue ) { UpdatePanValue ( slotId - 1, dValue ); } + void OnChGainValueChanged ( float fValue, + bool bIsMyOwnFader, + bool bIsGroupUpdate, + bool bSuppressServerUpdate, + double dLevelRatio ) { UpdateGainValue ( slotId - 1, + fValue, + bIsMyOwnFader, + bIsGroupUpdate, + bSuppressServerUpdate, + dLevelRatio ); } + + void OnChPanValueChanged ( float fValue ) { UpdatePanValue ( slotId - 1, fValue ); } protected: virtual void UpdateGainValue ( const int iChannelIdx, - const double dValue, - const bool bIsMyOwnFader ) = 0; - virtual void UpdatePanValue ( const int iChannelIdx, - const double dValue ) = 0; + const float fValue, + const bool bIsMyOwnFader, + const bool bIsGroupUpdate, + const bool bSuppressServerUpdate, + const double dLevelRatio ) = 0; + + virtual void UpdatePanValue ( const int iChannelIdx, + const float fValue ) = 0; }; template<> @@ -140,30 +192,36 @@ class CAudioMixerBoard : Q_OBJECT public: - CAudioMixerBoard ( QWidget* parent = nullptr, Qt::WindowFlags f = nullptr ); - - void HideAll(); - void ApplyNewConClientList ( CVector& vecChanInfo ); - void SetServerName ( const QString& strNewServerName ); - void SetGUIDesign ( const EGUIDesign eNewDesign ); - void SetDisplayChannelLevels ( const bool eNDCL ); - void SetDisplayPans ( const bool eNDP ); - void SetPanIsSupported(); - void SetRemoteFaderIsMute ( const int iChannelIdx, const bool bIsMute ); - void SetMyChannelID ( const int iChannelIdx ) { iMyChannelID = iChannelIdx; } - - void SetFaderLevel ( const int iChannelIdx, - const int iValue ); - - void SetChannelLevels ( const CVector& vecChannelLevel ); - - // settings - CVector vecStoredFaderTags; - CVector vecStoredFaderLevels; - CVector vecStoredPanValues; - CVector vecStoredFaderIsSolo; - CVector vecStoredFaderIsMute; - int iNewClientFaderLevel; + CAudioMixerBoard ( QWidget* parent = nullptr ); + + virtual ~CAudioMixerBoard(); + + void SetSettingsPointer ( CClientSettings* pNSet ) { pSettings = pNSet; } + void HideAll(); + void ApplyNewConClientList ( CVector& vecChanInfo ); + void SetServerName ( const QString& strNewServerName ); + QString GetServerName() { return strServerName; } + void SetGUIDesign ( const EGUIDesign eNewDesign ); + void SetDisplayPans ( const bool eNDP ); + void SetPanIsSupported(); + void SetRemoteFaderIsMute ( const int iChannelIdx, const bool bIsMute ); + void SetMyChannelID ( const int iChannelIdx ) { iMyChannelID = iChannelIdx; } + + void SetFaderLevel ( const int iChannelIdx, + const int iValue ); + + void SetNumMixerPanelRows ( const int iNNumMixerPanelRows ); + int GetNumMixerPanelRows() { return iNumMixerPanelRows; } + + void SetFaderSorting ( const EChSortType eNChSortType ); + EChSortType GetFaderSorting() { return eChSortType; } + + void SetChannelLevels ( const CVector& vecChannelLevel ); + + void SetRecorderState ( const ERecorderState newRecorderState ); + void SetAllFaderLevelsToNewClientLevel(); + void StoreAllFaderSettings(); + void LoadAllFaderSettings(); protected: class CMixerBoardScrollArea : public QScrollArea @@ -181,39 +239,49 @@ class CAudioMixerBoard : } }; - bool GetStoredFaderSettings ( const CChannelInfo& ChanInfo, - int& iStoredFaderLevel, - int& iStoredPanValue, - bool& bStoredFaderIsSolo, - bool& bStoredFaderIsMute ); + void ChangeFaderOrder ( const EChSortType eChSortType ); + + bool GetStoredFaderSettings ( const QString& strName, + int& iStoredFaderLevel, + int& iStoredPanValue, + bool& bStoredFaderIsSolo, + bool& bStoredFaderIsMute, + int& iGroupID ); void StoreFaderSettings ( CChannelFader* pChanFader ); void UpdateSoloStates(); + void UpdateTitle(); - void OnGainValueChanged ( const int iChannelIdx, - const double dValue ); - + CClientSettings* pSettings; CVector vecpChanFader; CMixerBoardScrollArea* pScrollArea; - QHBoxLayout* pMainLayout; - bool bDisplayChannelLevels; + QGridLayout * pMainLayout; bool bDisplayPans; bool bIsPanSupported; bool bNoFaderVisible; int iMyChannelID; + int iRunningNewClientCnt; // integer type is sufficient, will never overrun for its purpose + int iNumMixerPanelRows; QString strServerName; + ERecorderState eRecorderState; + QMutex Mutex; + EChSortType eChSortType; virtual void UpdateGainValue ( const int iChannelIdx, - const double dValue, - const bool bIsMyOwnFader ); - virtual void UpdatePanValue ( const int iChannelIdx, - const double dValue ); + const float fValue, + const bool bIsMyOwnFader, + const bool bIsGroupUpdate, + const bool bSuppressServerUpdate, + const double dLevelRatio ); + + virtual void UpdatePanValue ( const int iChannelIdx, + const float fValue ); template inline void connectFaderSignalsToMixerBoardSlots(); signals: - void ChangeChanGain ( int iId, double dGain, bool bIsMyOwnFader ); - void ChangeChanPan ( int iId, double dPan ); + void ChangeChanGain ( int iId, float fGain, bool bIsMyOwnFader ); + void ChangeChanPan ( int iId, float fPan ); void NumClientsChanged ( int iNewNumClients ); }; diff --git a/src/buffer.cpp b/src/buffer.cpp index 578d5f1f58..fc87714c52 100755 --- a/src/buffer.cpp +++ b/src/buffer.cpp @@ -21,7 +21,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -31,61 +31,383 @@ /* Network buffer implementation **********************************************/ void CNetBuf::Init ( const int iNewBlockSize, const int iNewNumBlocks, + const bool bNUseSequenceNumber, const bool bPreserve ) { - // store block size value - iBlockSize = iNewBlockSize; + // store the sequence number activation flag + bUseSequenceNumber = bNUseSequenceNumber; + + // in simulation mode the size is not changed during operation -> we do + // not have to implement special code for this case + // only enter the "preserve" branch, if object was already initialized + // and the block sizes are the same + if ( bPreserve && ( !bIsSimulation ) && bIsInitialized && ( iBlockSize == iNewBlockSize ) ) + { + // extract all data from buffer in temporary storage + CVector > vecvecTempMemory = vecvecMemory; // allocate worst case memory by copying - // total size -> size of one block times the number of blocks - CBufferBase::Init ( iNewBlockSize * iNewNumBlocks, - bPreserve ); + if ( !bNUseSequenceNumber ) + { + int iPreviousDataCnt = 0; - // clear buffer if not preserved - if ( !bPreserve ) + while ( Get ( vecvecTempMemory[iPreviousDataCnt], iBlockSize ) ) + { + iPreviousDataCnt++; + } + + // now resize the buffer to the new size (buffer is empty after this operation) + Resize ( iNewNumBlocks, iNewBlockSize ); + + // copy the previous data back in the buffer (make sure we only copy as much + // data back as the new buffer size can hold) + int iDataCnt = 0; + + while ( ( iDataCnt < iPreviousDataCnt ) && + Put ( vecvecTempMemory[iDataCnt], iBlockSize ) ) + { + iDataCnt++; + } + } + else + { + // store current complete buffer state in temporary memory + CVector veciTempBlockValid ( iNumBlocksMemory ); + const uint8_t iOldSequenceNumberAtGetPos = iSequenceNumberAtGetPos; + const int iOldNumBlocksMemory = iNumBlocksMemory; + const int iOldBlockGetPos = iBlockGetPos; + int iCurBlockPos = 0; + + while ( iBlockGetPos < iNumBlocksMemory ) + { + veciTempBlockValid[iCurBlockPos] = veciBlockValid[iBlockGetPos]; + vecvecTempMemory[iCurBlockPos++] = vecvecMemory[iBlockGetPos++]; + } + + for ( iBlockGetPos = 0; iBlockGetPos < iOldBlockGetPos; iBlockGetPos++ ) + { + veciTempBlockValid[iCurBlockPos] = veciBlockValid[iBlockGetPos]; + vecvecTempMemory[iCurBlockPos++] = vecvecMemory[iBlockGetPos]; + } + + // now resize the buffer to the new size + Resize ( iNewNumBlocks, iNewBlockSize ); + + // write back the temporary data in new memory + iSequenceNumberAtGetPos = iOldSequenceNumberAtGetPos; + iBlockGetPos = 0; // per definition + + for ( int iCurPos = 0; iCurPos < std::min ( iNewNumBlocks, iOldNumBlocksMemory ); iCurPos++ ) + { + veciBlockValid[iCurPos] = veciTempBlockValid[iCurPos]; + vecvecMemory[iCurPos] = vecvecTempMemory[iCurPos]; + } + } + } + else { - Clear(); + Resize ( iNewNumBlocks, iNewBlockSize ); } + + // set initialized flag + bIsInitialized = true; } -bool CNetBuf::Put ( const CVector& vecbyData, - const int iInSize ) +void CNetBuf::Resize ( const int iNewNumBlocks, + const int iNewBlockSize ) { - bool bPutOK = true; + // allocate memory for actual data buffer + vecvecMemory.Init ( iNewNumBlocks ); + veciBlockValid.Init ( iNewNumBlocks, 0 ); // initialize with zeros = invalid - // check if there is not enough space available - if ( GetAvailSpace() < iInSize ) + if ( !bIsSimulation ) { - return false; + for ( int iBlock = 0; iBlock < iNewNumBlocks; iBlock++ ) + { + vecvecMemory[iBlock].Init ( iNewBlockSize ); + } } - // copy new data in internal buffer (implemented in base class) - CBufferBase::Put ( vecbyData, iInSize ); + // init buffer pointers and buffer state (empty buffer) and store buffer properties + iBlockGetPos = 0; + iBlockPutPos = 0; + eBufState = BS_EMPTY; + iBlockSize = iNewBlockSize; + iNumBlocksMemory = iNewNumBlocks; +} - return bPutOK; +bool CNetBuf::Put ( const CVector& vecbyData, + int iInSize ) +{ + // if the sequence number is used, we need a complete different way of applying + // the new network packet + if ( bUseSequenceNumber ) + { + // check that the input size is a multiple of the block size + if ( ( iInSize % ( iBlockSize + iNumBytesSeqNum ) ) != 0 ) + { + return false; + } + + // to get the number of input blocks we assume that the number of bytes for + // the sequence number is much smaller than the number of coded audio bytes + const int iNumBlocks = /* floor */ ( iInSize / iBlockSize ); + + // copy new data in internal buffer + for ( int iBlock = 0; iBlock < iNumBlocks; iBlock++ ) + { + // extract sequence number of current received block (per definition + // the sequence number is appended after the coded audio data) + const int iCurrentSequenceNumber = vecbyData[iBlock * ( iBlockSize + iNumBytesSeqNum ) + iBlockSize]; + + // calculate the sequence number difference and take care of wrap + int iSeqNumDiff = iCurrentSequenceNumber - static_cast ( iSequenceNumberAtGetPos ); + + if ( iSeqNumDiff < -128 ) + { + iSeqNumDiff += 256; + } + else if ( iSeqNumDiff >= 128 ) + { + iSeqNumDiff -= 256; + } + + // The 1-byte sequence number wraps around at a count of 256. So, if a packet is delayed + // further than this we cannot detect it. But it does not matter since such a packet is + // more than 100 ms delayed so we have a bad network situation anyway. Therefore we + // assume that the sequence number difference between the received and local counter is + // correct. The idea of the following code is that we always move our "buffer window" so + // that the received packet fits into the buffer. By doing this we are robust against + // sample rate offsets between client/server or buffer glitches in the audio driver since + // we adjust the window. The downside is that we never throw away single packets which arrive + // too late so we throw away valid packets when we move the "buffer window" to the delayed + // packet and then back to the correct place when the next normal packet is received. But + // tests showed that the new buffer strategy does not perform worse than the old jitter + // buffer which did not use any sequence number at all. + if ( iSeqNumDiff < 0 ) + { + // the received packet comes too late so we shift the "buffer window" to the past + // until the received packet is the very first packet in the buffer + for ( int i = iSeqNumDiff; i < 0; i++ ) + { + // insert an invalid block at the shifted position + veciBlockValid[iBlockGetPos] = 0; // invalidate + + // we decrease the local sequence number and get position and take care of wrap + iSequenceNumberAtGetPos--; + iBlockGetPos--; + + if ( iBlockGetPos < 0 ) + { + iBlockGetPos += iNumBlocksMemory; + } + } + + // insert the new packet at the beginning of the buffer since it was delayed + iBlockPutPos = iBlockGetPos; + } + else if ( iSeqNumDiff >= iNumBlocksMemory ) + { + // the received packet comes too early so we move the "buffer window" in the + // future until the received packet is the last packet in the buffer + for ( int i = 0; i < iSeqNumDiff - iNumBlocksMemory + 1; i++ ) + { + // insert an invalid block at the shifted position + veciBlockValid[iBlockGetPos] = 0; // invalidate + + // we increase the local sequence number and get position and take care of wrap + iSequenceNumberAtGetPos++; + iBlockGetPos++; + + if ( iBlockGetPos >= iNumBlocksMemory ) + { + iBlockGetPos -= iNumBlocksMemory; + } + } + + // insert the new packet at the end of the buffer since it is too early (since + // we add an offset to the get position, we have to take care of wrapping) + iBlockPutPos = iBlockGetPos + iNumBlocksMemory - 1; + + if ( iBlockPutPos >= iNumBlocksMemory ) + { + iBlockPutPos -= iNumBlocksMemory; + } + } + else + { + // this is the regular case: the received packet fits into the buffer so + // we will write it at the correct position based on the sequence number + iBlockPutPos = iBlockGetPos + iSeqNumDiff; + + if ( iBlockPutPos >= iNumBlocksMemory ) + { + iBlockPutPos -= iNumBlocksMemory; + } + } + + // for simulation buffer only update pointer, no data copying + if ( !bIsSimulation ) + { + // copy one block of data in buffer + std::copy ( vecbyData.begin() + iBlock * ( iBlockSize + iNumBytesSeqNum ), + vecbyData.begin() + iBlock * ( iBlockSize + iNumBytesSeqNum ) + iBlockSize, + vecvecMemory[iBlockPutPos].begin() ); + } + + // valid packet added, set flag + veciBlockValid[iBlockPutPos] = 1; + } + } + else + { + // check if there is not enough space available and that the input size is a + // multiple of the block size + if ( ( GetAvailSpace() < iInSize ) || + ( ( iInSize % iBlockSize ) != 0 ) ) + { + return false; + } + + // copy new data in internal buffer + const int iNumBlocks = iInSize / iBlockSize; + + for ( int iBlock = 0; iBlock < iNumBlocks; iBlock++ ) + { + // for simultion buffer only update pointer, no data copying + if ( !bIsSimulation ) + { + // copy one block of data in buffer + std::copy ( vecbyData.begin() + iBlock * iBlockSize, + vecbyData.begin() + iBlock * iBlockSize + iBlockSize, + vecvecMemory[iBlockPutPos].begin() ); + } + + // set the put position one block further + iBlockPutPos++; + + // take care about wrap around of put pointer + if ( iBlockPutPos == iNumBlocksMemory ) + { + iBlockPutPos = 0; + } + } + + // set buffer state flag + if ( iBlockPutPos == iBlockGetPos ) + { + eBufState = BS_FULL; + } + else + { + eBufState = BS_OK; + } + } + + return true; } bool CNetBuf::Get ( CVector& vecbyData, const int iOutSize ) { - bool bGetOK = true; // init return value + bool bReturn = true; - // check size - if ( ( iOutSize == 0 ) || ( iOutSize != iBlockSize ) ) + // check requested output size and available buffer data + if ( ( iOutSize == 0 ) || + ( iOutSize != iBlockSize ) || + ( GetAvailData() < iOutSize ) ) { return false; } - // check if there is not enough data available - if ( GetAvailData() < iOutSize ) + // if using sequence numbers, we do not use the block put position + // at all but only determine the state from the "valid block" indicator + if ( bUseSequenceNumber ) { - return false; + bReturn = ( veciBlockValid[iBlockGetPos] > 0 ); + + // invalidate the block we are now taking from the buffer + veciBlockValid[iBlockGetPos] = 0; // zero means invalid } - // copy data from internal buffer in output buffer (implemented in base - // class) - CBufferBase::Get ( vecbyData, iOutSize ); + // for simultion buffer or invalid block only update pointer, no data copying + if ( !bIsSimulation && bReturn ) + { + // copy data from internal buffer in output buffer + std::copy ( vecvecMemory[iBlockGetPos].begin(), + vecvecMemory[iBlockGetPos].begin() + iBlockSize, + vecbyData.begin() ); + } - return bGetOK; + // set the get position and sequence number one block further + iBlockGetPos++; + iSequenceNumberAtGetPos++; // wraps around automatically + + // take care about wrap around of get pointer + if ( iBlockGetPos == iNumBlocksMemory ) + { + iBlockGetPos = 0; + } + + // set buffer state flag + if ( iBlockPutPos == iBlockGetPos ) + { + eBufState = BS_EMPTY; + } + else + { + eBufState = BS_OK; + } + + return bReturn; +} + +int CNetBuf::GetAvailSpace() const +{ + // calculate available space in buffer + int iAvBlocks = iBlockGetPos - iBlockPutPos; + + // check for special case and wrap around + if ( iAvBlocks < 0 ) + { + iAvBlocks += iNumBlocksMemory; // wrap around + } + else + { + if ( ( iAvBlocks == 0 ) && ( eBufState == BS_EMPTY ) ) + { + iAvBlocks = iNumBlocksMemory; + } + } + + return iAvBlocks * iBlockSize; +} + +int CNetBuf::GetAvailData() const +{ + // in case of using sequence numbers, we always return data from the + // buffer per definition + int iAvBlocks = iNumBlocksMemory; + + if ( !bUseSequenceNumber ) + { + // calculate available data in buffer + iAvBlocks = iBlockPutPos - iBlockGetPos; + + // check for special case and wrap around + if ( iAvBlocks < 0 ) + { + iAvBlocks += iNumBlocksMemory; // wrap around + } + else + { + if ( ( iAvBlocks == 0 ) && ( eBufState == BS_FULL ) ) + { + iAvBlocks = iNumBlocksMemory; + } + } + } + + return iAvBlocks * iBlockSize; } @@ -143,10 +465,11 @@ void CNetBufWithStats::GetErrorRates ( CVector& vecErrRates, void CNetBufWithStats::Init ( const int iNewBlockSize, const int iNewNumBlocks, + const bool bNUseSequenceNumber, const bool bPreserve ) { // call base class Init - CNetBuf::Init ( iNewBlockSize, iNewNumBlocks, bPreserve ); + CNetBuf::Init ( iNewBlockSize, iNewNumBlocks, bNUseSequenceNumber, bPreserve ); // inits for statistics calculation if ( !bPreserve ) @@ -176,7 +499,7 @@ void CNetBufWithStats::Init ( const int iNewBlockSize, for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS; i++ ) { // init simulation buffers with the correct size - SimulationBuffer[i].Init ( iNewBlockSize, viBufSizesForSim[i] ); + SimulationBuffer[i].Init ( iNewBlockSize, viBufSizesForSim[i], bNUseSequenceNumber ); // init statistics ErrorRateStatistic[i].Init ( iMaxStatisticCount, true ); @@ -291,14 +614,14 @@ void CNetBufWithStats::UpdateAutoSetting() iCurMaxUpDecision = viBufSizesForSim[NUM_STAT_SIMULATION_BUFFERS - 1]; // This is a worst case, something very bad had happened. Hopefully - // this was just temporary so that we initiate a new initialzation + // this was just temporary so that we initiate a new initialization // phase to get quickly back to normal buffer sizes (hopefully). ResetInitCounter(); } // Post calculation (filtering) -------------------------------------------- - // Define different weigths for up and down direction. Up direction + // Define different weights for up and down direction. Up direction // filtering shall be slower than for down direction since we assume // that the lower value is the actual value which can be used for // the current network condition. If the current error rate estimation diff --git a/src/buffer.h b/src/buffer.h index a57a056319..a54d4365a7 100755 --- a/src/buffer.h +++ b/src/buffer.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -37,7 +37,7 @@ #define FILTER_DECISION_HYSTERESIS 0.1 // definition of the upper error bound of the jitter buffers -#define ERROR_RATE_BOUND_DOUBLE_FRAME_SIZE 0.001 +#define ERROR_RATE_BOUND_DOUBLE_FRAME_SIZE 0.0005 #define ERROR_RATE_BOUND ( ERROR_RATE_BOUND_DOUBLE_FRAME_SIZE / 2 ) // definition of the upper jitter buffer error bound, if that one is reached we @@ -72,176 +72,57 @@ /* Classes ********************************************************************/ // Buffer base class ----------------------------------------------------------- -template class CBufferBase +template class CBuffer { public: - CBufferBase ( const bool bNIsSim = false ) : - bIsSimulation ( bNIsSim ), bIsInitialized ( false ) {} + CBuffer() {} - void SetIsSimulation ( const bool bNIsSim ) { bIsSimulation = bNIsSim; } - - void Init ( const int iNewMemSize, - const bool bPreserve = false ) + void Init ( const int iNewMemSize ) { - // in simulation mode the size is not changed during operation -> we do - // not have to implement special code for this case - // only enter the "preserve" branch, if object was already initialized - if ( bPreserve && ( !bIsSimulation ) && bIsInitialized ) - { - // copy old data in new vector using get pointer as zero per - // definition - int iCurPos; - - // copy current data in temporary vector - CVector vecTempMemory ( vecMemory ); - - // resize actual buffer memory - vecMemory.Init ( iNewMemSize ); - - // get maximum number of data to be copied - int iCopyLen = GetAvailData(); - - if ( iCopyLen > iNewMemSize ) - { - iCopyLen = iNewMemSize; - } - - // set correct buffer state - if ( iCopyLen == iNewMemSize ) - { - eBufState = CBufferBase::BS_FULL; - } - else - { - if ( iCopyLen == 0 ) - { - eBufState = CBufferBase::BS_EMPTY; - } - else - { - eBufState = CBufferBase::BS_OK; - } - } - - if ( iGetPos < iPutPos ) - { - // "get" position is before "put" position -> no wrap around - for ( iCurPos = 0; iCurPos < iCopyLen; iCurPos++ ) - { - vecMemory[iCurPos] = vecTempMemory[iGetPos + iCurPos]; - } - } - else - { - // "put" position is before "get" position -> wrap around - bool bEnoughSpaceForSecondPart = true; - int iFirstPartLen = iMemSize - iGetPos; - - // check that first copy length is not larger then new memory - if ( iFirstPartLen >= iCopyLen ) - { - iFirstPartLen = iCopyLen; - bEnoughSpaceForSecondPart = false; - } - - for ( iCurPos = 0; iCurPos < iFirstPartLen; iCurPos++ ) - { - vecMemory[iCurPos] = vecTempMemory[iGetPos + iCurPos]; - } - - if ( bEnoughSpaceForSecondPart ) - { - // calculate remaining copy length - const int iRemainingCopyLen = iCopyLen - iFirstPartLen; - - // perform copying of second part - for ( iCurPos = 0; iCurPos < iRemainingCopyLen; iCurPos++ ) - { - vecMemory[iCurPos + iFirstPartLen] = vecTempMemory[iCurPos]; - } - } - } - - // update put pointer - if ( eBufState == CBufferBase::BS_FULL ) - { - iPutPos = 0; - } - else - { - iPutPos = iCopyLen; - } - - // update get position -> zero per definition - iGetPos = 0; - } - else - { - // allocate memory for actual data buffer - if ( !bIsSimulation ) - { - vecMemory.Init ( iNewMemSize ); - } + // allocate memory for actual data buffer + vecMemory.Init ( iNewMemSize ); - // init buffer pointers and buffer state (empty buffer) - iGetPos = 0; - iPutPos = 0; - eBufState = CBufferBase::BS_EMPTY; - } + // init buffer pointers and buffer state (empty buffer) + iGetPos = 0; + iPutPos = 0; + eBufState = BS_EMPTY; // store total memory size value iMemSize = iNewMemSize; - - // set initialized flag - bIsInitialized = true; } virtual bool Put ( const CVector& vecData, const int iInSize ) { - if ( bIsSimulation ) + // copy new data in internal buffer + int iCurPos = 0; + + if ( iPutPos + iInSize > iMemSize ) { - // in this simulation only the buffer pointers and the buffer state - // is updated, no actual data is transferred - iPutPos += iInSize; + // remaining space size for second block + const int iRemSpace = iPutPos + iInSize - iMemSize; + + // data must be written in two steps because of wrap around + while ( iPutPos < iMemSize ) + { + vecMemory[iPutPos++] = vecData[iCurPos++]; + } - if ( iPutPos >= iMemSize ) + for ( iPutPos = 0; iPutPos < iRemSpace; iPutPos++ ) { - iPutPos -= iMemSize; + vecMemory[iPutPos] = vecData[iCurPos++]; } } else { - // copy new data in internal buffer - int iCurPos = 0; + // data can be written in one step + std::copy ( vecData.begin(), + vecData.begin() + iInSize, + vecMemory.begin() + iPutPos ); - if ( iPutPos + iInSize > iMemSize ) - { - // remaining space size for second block - const int iRemSpace = iPutPos + iInSize - iMemSize; - - // data must be written in two steps because of wrap around - while ( iPutPos < iMemSize ) - { - vecMemory[iPutPos++] = vecData[iCurPos++]; - } - - for ( iPutPos = 0; iPutPos < iRemSpace; iPutPos++ ) - { - vecMemory[iPutPos] = vecData[iCurPos++]; - } - } - else - { - // data can be written in one step - std::copy ( vecData.begin(), - vecData.begin() + iInSize, - vecMemory.begin() + iPutPos ); - - // set the put position one block further (no wrap around needs - // to be considered here) - iPutPos += iInSize; - } + // set the put position one block further (no wrap around needs + // to be considered here) + iPutPos += iInSize; } // take care about wrap around of put pointer @@ -253,62 +134,48 @@ template class CBufferBase // set buffer state flag if ( iPutPos == iGetPos ) { - eBufState = CBufferBase::BS_FULL; + eBufState = BS_FULL; } else { - eBufState = CBufferBase::BS_OK; + eBufState = BS_OK; } - return true; // no error check in base class, alyways return ok + return true; // no error check in base class, always return ok } virtual bool Get ( CVector& vecData, const int iOutSize ) { - if ( bIsSimulation ) + // copy data from internal buffer in output buffer + int iCurPos = 0; + + if ( iGetPos + iOutSize > iMemSize ) { - // in this simulation only the buffer pointers and the buffer state - // is updated, no actual data is transferred - iGetPos += iOutSize; + // remaining data size for second block + const int iRemData = iGetPos + iOutSize - iMemSize; + + // data must be read in two steps because of wrap around + while ( iGetPos < iMemSize ) + { + vecData[iCurPos++] = vecMemory[iGetPos++]; + } - if ( iGetPos >= iMemSize ) + for ( iGetPos = 0; iGetPos < iRemData; iGetPos++ ) { - iGetPos -= iMemSize; + vecData[iCurPos++] = vecMemory[iGetPos]; } } else { - // copy data from internal buffer in output buffer - int iCurPos = 0; + // data can be read in one step + std::copy ( vecMemory.begin() + iGetPos, + vecMemory.begin() + iGetPos + iOutSize, + vecData.begin() ); - if ( iGetPos + iOutSize > iMemSize ) - { - // remaining data size for second block - const int iRemData = iGetPos + iOutSize - iMemSize; - - // data must be read in two steps because of wrap around - while ( iGetPos < iMemSize ) - { - vecData[iCurPos++] = vecMemory[iGetPos++]; - } - - for ( iGetPos = 0; iGetPos < iRemData; iGetPos++ ) - { - vecData[iCurPos++] = vecMemory[iGetPos]; - } - } - else - { - // data can be read in one step - std::copy ( vecMemory.begin() + iGetPos, - vecMemory.begin() + iGetPos + iOutSize, - vecData.begin() ); - - // set the get position one block further (no wrap around needs - // to be considered here) - iGetPos += iOutSize; - } + // set the get position one block further (no wrap around needs + // to be considered here) + iGetPos += iOutSize; } // take care about wrap around of get pointer @@ -320,35 +187,14 @@ template class CBufferBase // set buffer state flag if ( iPutPos == iGetPos ) { - eBufState = CBufferBase::BS_EMPTY; + eBufState = BS_EMPTY; } else { - eBufState = CBufferBase::BS_OK; + eBufState = BS_OK; } - return true; // no error check in base class, alyways return ok - } - - virtual int GetAvailSpace() const - { - // calculate available space in buffer - int iAvSpace = iGetPos - iPutPos; - - // check for special case and wrap around - if ( iAvSpace < 0 ) - { - iAvSpace += iMemSize; // wrap around - } - else - { - if ( ( iAvSpace == 0 ) && ( eBufState == BS_EMPTY ) ) - { - iAvSpace = iMemSize; - } - } - - return iAvSpace; + return true; // no error check in base class, always return ok } virtual int GetAvailData() const @@ -375,48 +221,51 @@ template class CBufferBase protected: enum EBufState { BS_OK, BS_FULL, BS_EMPTY }; - virtual void Clear() - { - // clear memory - if ( !bIsSimulation ) - { - vecMemory.Reset ( 0 ); - } - - // init buffer pointers and buffer state (empty buffer) - iGetPos = 0; - iPutPos = 0; - eBufState = CBufferBase::BS_EMPTY; - } - CVector vecMemory; int iMemSize; int iGetPos; int iPutPos; EBufState eBufState; - bool bIsSimulation; - bool bIsInitialized; }; // Network buffer (jitter buffer) ---------------------------------------------- -class CNetBuf : public CBufferBase +class CNetBuf { public: - CNetBuf ( const bool bNewIsSim = false ) : - CBufferBase ( bNewIsSim ) {} + CNetBuf ( const bool bNIsSim = false ) : + iSequenceNumberAtGetPos ( 0 ), bIsSimulation ( bNIsSim ), bIsInitialized ( false ) {} void Init ( const int iNewBlockSize, const int iNewNumBlocks, + const bool bNUseSequenceNumber, const bool bPreserve = false ); - int GetSize() { return iMemSize / iBlockSize; } + void SetIsSimulation ( const bool bNIsSim ) { bIsSimulation = bNIsSim; } - virtual bool Put ( const CVector& vecbyData, const int iInSize ); + virtual bool Put ( const CVector& vecbyData, int iInSize ); virtual bool Get ( CVector& vecbyData, const int iOutSize ); protected: - int iBlockSize; + enum EBufState { BS_OK, BS_FULL, BS_EMPTY }; + + int GetAvailSpace() const; + int GetAvailData() const; + void Resize ( const int iNewNumBlocks, const int iNewBlockSize ); + + CVector > vecvecMemory; + CVector veciBlockValid; + int iNumBlocksMemory; + int iBlockGetPos; + int iBlockPutPos; + int iBlockSize; + uint8_t iSequenceNumberAtGetPos; // uint8_t so that it wraps automatically + EBufState eBufState; + bool bUseSequenceNumber; + bool bIsSimulation; + bool bIsInitialized; + + static constexpr int iNumBytesSeqNum = 1; // per definition 1 byte sequence counter }; @@ -428,6 +277,7 @@ class CNetBufWithStats : public CNetBuf void Init ( const int iNewBlockSize, const int iNewNumBlocks, + const bool bNUseSequenceNumber, const bool bPreserve = false ); void SetUseDoubleSystemFrameSize ( const bool bNDSFSize ) { bUseDoubleSystemFrameSize = bNDSFSize; } @@ -475,12 +325,14 @@ template class CConvBuf public: CConvBuf() { Init ( 0 ); } - void Init ( const int iNewMemSize ) + void Init ( const int iNewMemSize, + const bool bNUseSequenceNumber = false ) { // allocate internal memory and reset read/write positions vecMemory.Init ( iNewMemSize ); - iMemSize = iNewMemSize; - iBufferSize = iNewMemSize; + iMemSize = iNewMemSize; + iBufferSize = iNewMemSize; + bUseSequenceNumber = bNUseSequenceNumber; Reset(); } @@ -509,20 +361,35 @@ template class CConvBuf vecMemory.begin() ); } - bool Put ( const CVector& vecsData, - const int iVecSize ) + bool Put ( const CVector& vecData, + const int iVecSize, + const TData SequenceNumber = 0 ) { - // calculate the input size and the end position after copying - const int iEnd = iPutPos + iVecSize; + // calculate the end position after copying + int iEnd = iPutPos + iVecSize; + + // consider optional sequence number + if ( bUseSequenceNumber ) + { + iEnd++; + } // first check for buffer overrun if ( iEnd <= iBufferSize ) { // copy new data in internal buffer - std::copy ( vecsData.begin(), - vecsData.begin() + iVecSize, + std::copy ( vecData.begin(), + vecData.begin() + iVecSize, vecMemory.begin() + iPutPos ); + // add optional sequence number (NOTE that we currently + // only support a single sequence number per packet) + if ( bUseSequenceNumber ) + { + // append the sequence number at the end + vecMemory[iPutPos + iVecSize] = SequenceNumber; + } + // set buffer pointer one block further iPutPos = iEnd; @@ -580,5 +447,6 @@ template class CConvBuf CVector vecMemory; int iMemSize; int iBufferSize; + bool bUseSequenceNumber; int iPutPos, iGetPos; }; diff --git a/src/channel.cpp b/src/channel.cpp index 4e6aa13621..969c7aa4ef 100755 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -27,14 +27,18 @@ // CChannel implementation ***************************************************** CChannel::CChannel ( const bool bNIsServer ) : - vecdGains ( MAX_NUM_CHANNELS, 1.0 ), - vecdPannings ( MAX_NUM_CHANNELS, 0.5 ), + vecfGains ( MAX_NUM_CHANNELS, 1.0f ), + vecfPannings ( MAX_NUM_CHANNELS, 0.5f ), + iCurSockBufNumFrames ( INVALID_INDEX ), bDoAutoSockBufSize ( true ), + bUseSequenceNumber ( false ), // this is important since in the client we reset on Channel.SetEnable ( false ) + iSendSequenceNumber ( 0 ), iFadeInCnt ( 0 ), iFadeInCntMax ( FADE_IN_NUM_FRAMES_DBLE_FRAMESIZE ), bIsEnabled ( false ), bIsServer ( bNIsServer ), - iAudioFrameSizeSamples ( DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ) + iAudioFrameSizeSamples ( DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ), + SignalLevelMeter ( false, 0.5 ) // server mode with mono out and faster smoothing { // reset network transport properties ResetNetworkTransportProperties(); @@ -60,70 +64,62 @@ CChannel::CChannel ( const bool bNIsServer ) : qRegisterMetaType > ( "CVector" ); qRegisterMetaType ( "CHostAddress" ); - QObject::connect ( &Protocol, - SIGNAL ( MessReadyForSending ( CVector ) ), - this, SLOT ( OnSendProtMessage ( CVector ) ) ); + QObject::connect ( &Protocol, &CProtocol::MessReadyForSending, + this, &CChannel::OnSendProtMessage ); - QObject::connect ( &Protocol, - SIGNAL ( ChangeJittBufSize ( int ) ), - this, SLOT ( OnJittBufSizeChange ( int ) ) ); + QObject::connect ( &Protocol, &CProtocol::ChangeJittBufSize, + this, &CChannel::OnJittBufSizeChange ); - QObject::connect ( &Protocol, - SIGNAL ( ReqJittBufSize() ), - SIGNAL ( ReqJittBufSize() ) ); + QObject::connect ( &Protocol, &CProtocol::ReqJittBufSize, + this, &CChannel::ReqJittBufSize ); - QObject::connect ( &Protocol, - SIGNAL ( ReqChanInfo() ), - SIGNAL ( ReqChanInfo() ) ); + QObject::connect ( &Protocol, &CProtocol::ReqChanInfo, + this, &CChannel::ReqChanInfo ); - QObject::connect ( &Protocol, - SIGNAL ( ReqConnClientsList() ), - SIGNAL ( ReqConnClientsList() ) ); + QObject::connect ( &Protocol, &CProtocol::ReqConnClientsList, + this, &CChannel::ReqConnClientsList ); - QObject::connect ( &Protocol, - SIGNAL ( ConClientListMesReceived ( CVector ) ), - SIGNAL ( ConClientListMesReceived ( CVector ) ) ); + QObject::connect ( &Protocol, &CProtocol::ConClientListMesReceived, + this, &CChannel::ConClientListMesReceived ); - QObject::connect ( &Protocol, SIGNAL ( ChangeChanGain ( int, double ) ), - this, SLOT ( OnChangeChanGain ( int, double ) ) ); + QObject::connect ( &Protocol, &CProtocol::ChangeChanGain, + this, &CChannel::OnChangeChanGain ); - QObject::connect ( &Protocol, SIGNAL ( ChangeChanPan ( int, double ) ), - this, SLOT ( OnChangeChanPan ( int, double ) ) ); + QObject::connect ( &Protocol, &CProtocol::ChangeChanPan, + this, &CChannel::OnChangeChanPan ); - QObject::connect ( &Protocol, - SIGNAL ( ClientIDReceived ( int ) ), - SIGNAL ( ClientIDReceived ( int ) ) ); + QObject::connect ( &Protocol, &CProtocol::ClientIDReceived, + this, &CChannel::ClientIDReceived ); - QObject::connect ( &Protocol, - SIGNAL ( MuteStateHasChangedReceived ( int, bool ) ), - SIGNAL ( MuteStateHasChangedReceived ( int, bool ) ) ); + QObject::connect ( &Protocol, &CProtocol::MuteStateHasChangedReceived, + this, &CChannel::MuteStateHasChangedReceived ); - QObject::connect ( &Protocol, SIGNAL ( ChangeChanInfo ( CChannelCoreInfo ) ), - this, SLOT ( OnChangeChanInfo ( CChannelCoreInfo ) ) ); + QObject::connect ( &Protocol, &CProtocol::ChangeChanInfo, + this, &CChannel::OnChangeChanInfo ); - QObject::connect ( &Protocol, - SIGNAL ( ChatTextReceived ( QString ) ), - SIGNAL ( ChatTextReceived ( QString ) ) ); + QObject::connect ( &Protocol, &CProtocol::ChatTextReceived, + this, &CChannel::ChatTextReceived ); - QObject::connect ( &Protocol, - SIGNAL ( NetTranspPropsReceived ( CNetworkTransportProps ) ), - this, SLOT ( OnNetTranspPropsReceived ( CNetworkTransportProps ) ) ); + QObject::connect ( &Protocol, &CProtocol::NetTranspPropsReceived, + this, &CChannel::OnNetTranspPropsReceived ); - QObject::connect ( &Protocol, - SIGNAL ( ReqNetTranspProps() ), - this, SLOT ( OnReqNetTranspProps() ) ); + QObject::connect ( &Protocol, &CProtocol::ReqNetTranspProps, + this, &CChannel::OnReqNetTranspProps ); - QObject::connect ( &Protocol, - SIGNAL ( LicenceRequired ( ELicenceType ) ), - SIGNAL ( LicenceRequired ( ELicenceType ) ) ); + QObject::connect ( &Protocol, &CProtocol::ReqSplitMessSupport, + this, &CChannel::OnReqSplitMessSupport ); - QObject::connect ( &Protocol, - SIGNAL ( VersionAndOSReceived ( COSUtil::EOpSystemType, QString ) ), - SIGNAL ( VersionAndOSReceived ( COSUtil::EOpSystemType, QString ) ) ); + QObject::connect ( &Protocol, &CProtocol::SplitMessSupported, + this, &CChannel::OnSplitMessSupported ); - QObject::connect ( &Protocol, - SIGNAL ( ReqChannelLevelList ( bool ) ), - this, SLOT ( OnReqChannelLevelList ( bool ) ) ); + QObject::connect ( &Protocol, &CProtocol::LicenceRequired, + this, &CChannel::LicenceRequired ); + + QObject::connect ( &Protocol, &CProtocol::VersionAndOSReceived, + this, &CChannel::OnVersionAndOSReceived ); + + QObject::connect ( &Protocol, &CProtocol::RecorderStateReceived, + this, &CChannel::RecorderStateReceived ); } bool CChannel::ProtocolIsEnabled() @@ -149,6 +145,15 @@ void CChannel::SetEnable ( const bool bNEnStat ) // set internal parameter bIsEnabled = bNEnStat; + // The support for the packet sequence number must be reset if the client + // disconnects from a server since we do not yet know if the next server we + // connect to will support the sequence number. We use the SetEnable call in + // the client for this task since at every start/stop it will call this + // function. NOTE that it is important to reset this paramter on SetEnable(false) + // since the SetEnable(true) is set AFTER the Init() in the client -> we + // simply set it regardless of the state which does not hurt. + bUseSequenceNumber = false; + // if channel is not enabled, reset time out count and protocol if ( !bNEnStat ) { @@ -157,8 +162,29 @@ void CChannel::SetEnable ( const bool bNEnStat ) } } +void CChannel::OnVersionAndOSReceived ( COSUtil::EOpSystemType eOSType, + QString strVersion ) +{ + // check if audio packet counter is supported by the server (minimum version is 3.6.0) +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) + if ( QVersionNumber::compare ( QVersionNumber::fromString ( strVersion ), QVersionNumber ( 3, 6, 0 ) ) >= 0 ) + { + // activate sequence counter and update the audio stream properties (which + // does all the initialization and tells the server about the change) + bUseSequenceNumber = true; + + SetAudioStreamProperties ( eAudioCompressionType, + iCeltNumCodedBytes, + iNetwFrameSizeFact, + iNumAudioChannels ); + } +#endif + + emit VersionAndOSReceived ( eOSType, strVersion ); +} + void CChannel::SetAudioStreamProperties ( const EAudComprType eNewAudComprType, - const int iNewNetwFrameSize, + const int iNewCeltNumCodedBytes, const int iNewNetwFrameSizeFact, const int iNewNumAudioChannels ) { @@ -172,9 +198,19 @@ void CChannel::SetAudioStreamProperties ( const EAudComprType eNewAudComprType, // store new values eAudioCompressionType = eNewAudComprType; iNumAudioChannels = iNewNumAudioChannels; - iNetwFrameSize = iNewNetwFrameSize; + iCeltNumCodedBytes = iNewCeltNumCodedBytes; iNetwFrameSizeFact = iNewNetwFrameSizeFact; + // add the size of the optional packet counter + if ( bUseSequenceNumber ) + { + iNetwFrameSize = iCeltNumCodedBytes + 1; // per definition 1 byte counter + } + else + { + iNetwFrameSize = iCeltNumCodedBytes; + } + // update audio frame size if ( eAudioCompressionType == CT_OPUS ) { @@ -189,14 +225,14 @@ void CChannel::SetAudioStreamProperties ( const EAudComprType eNewAudComprType, { // init socket buffer SockBuf.SetUseDoubleSystemFrameSize ( eAudioCompressionType == CT_OPUS ); // NOTE must be set BEFORE the init() - SockBuf.Init ( iNetwFrameSize, iCurSockBufNumFrames ); + SockBuf.Init ( iCeltNumCodedBytes, iCurSockBufNumFrames, bUseSequenceNumber ); } MutexSocketBuf.unlock(); MutexConvBuf.lock(); { // init conversion buffer - ConvBuf.Init ( iNetwFrameSize * iNetwFrameSizeFact ); + ConvBuf.Init ( iNetwFrameSize * iNetwFrameSizeFact, bUseSequenceNumber ); } MutexConvBuf.unlock(); @@ -229,7 +265,7 @@ bool CChannel::SetSockBufNumFrames ( const int iNewNumFrames, // the network block size is a multiple of the minimum network // block size - SockBuf.Init ( iNetwFrameSize, iNewNumFrames, bPreserve ); + SockBuf.Init ( iCeltNumCodedBytes, iNewNumFrames, bUseSequenceNumber, bPreserve ); // store current auto socket buffer size setting in the mutex // region since if we use the current parameter below in the @@ -258,8 +294,8 @@ bool CChannel::SetSockBufNumFrames ( const int iNewNumFrames, return ReturnValue; // set error flag } -void CChannel::SetGain ( const int iChanID, - const double dNewGain ) +void CChannel::SetGain ( const int iChanID, + const float fNewGain ) { QMutexLocker locker ( &Mutex ); @@ -267,27 +303,27 @@ void CChannel::SetGain ( const int iChanID, if ( ( iChanID >= 0 ) && ( iChanID < MAX_NUM_CHANNELS ) ) { // signal mute change - if ( ( vecdGains[iChanID] == 0 ) && ( dNewGain > 0 ) ) + if ( ( vecfGains[iChanID] == 0 ) && ( fNewGain > 0 ) ) { emit MuteStateHasChanged ( iChanID, false ); } - if ( ( vecdGains[iChanID] > 0 ) && ( dNewGain == 0 ) ) + if ( ( vecfGains[iChanID] > 0 ) && ( fNewGain == 0 ) ) { emit MuteStateHasChanged ( iChanID, true ); } - vecdGains[iChanID] = dNewGain; + vecfGains[iChanID] = fNewGain; } } -double CChannel::GetGain ( const int iChanID ) +float CChannel::GetGain ( const int iChanID ) { QMutexLocker locker ( &Mutex ); // get value (make sure channel ID is in range) if ( ( iChanID >= 0 ) && ( iChanID < MAX_NUM_CHANNELS ) ) { - return vecdGains[iChanID]; + return vecfGains[iChanID]; } else { @@ -295,26 +331,26 @@ double CChannel::GetGain ( const int iChanID ) } } -void CChannel::SetPan ( const int iChanID, - const double dNewPan ) +void CChannel::SetPan ( const int iChanID, + const float fNewPan ) { QMutexLocker locker ( &Mutex ); // set value (make sure channel ID is in range) if ( ( iChanID >= 0 ) && ( iChanID < MAX_NUM_CHANNELS ) ) { - vecdPannings[iChanID] = dNewPan; + vecfPannings[iChanID] = fNewPan; } } -double CChannel::GetPan ( const int iChanID ) +float CChannel::GetPan ( const int iChanID ) { QMutexLocker locker ( &Mutex ); // get value (make sure channel ID is in range) if ( ( iChanID >= 0 ) && ( iChanID < MAX_NUM_CHANNELS ) ) { - return vecdPannings[iChanID]; + return vecfPannings[iChanID]; } else { @@ -382,16 +418,16 @@ void CChannel::OnJittBufSizeChange ( int iNewJitBufSize ) } } -void CChannel::OnChangeChanGain ( int iChanID, - double dNewGain ) +void CChannel::OnChangeChanGain ( int iChanID, + float fNewGain ) { - SetGain ( iChanID, dNewGain ); + SetGain ( iChanID, fNewGain ); } -void CChannel::OnChangeChanPan ( int iChanID, - double dNewPan ) +void CChannel::OnChangeChanPan ( int iChanID, + float fNewPan ) { - SetPan ( iChanID, dNewPan ); + SetPan ( iChanID, fNewPan ); } void CChannel::OnChangeChanInfo ( CChannelCoreInfo ChanInfo ) @@ -435,6 +471,16 @@ void CChannel::OnNetTranspPropsReceived ( CNetworkTransportProps NetworkTranspor iNumAudioChannels = static_cast ( NetworkTransportProps.iNumAudioChannels ); iNetwFrameSizeFact = NetworkTransportProps.iBlockSizeFact; iNetwFrameSize = static_cast ( NetworkTransportProps.iBaseNetworkPacketSize ); + bUseSequenceNumber = ( NetworkTransportProps.eFlags == NF_WITH_COUNTER ); + + if ( bUseSequenceNumber ) + { + iCeltNumCodedBytes = iNetwFrameSize - 1; // per definition 1 byte counter + } + else + { + iCeltNumCodedBytes = iNetwFrameSize; + } // update maximum number of frames for fade in counter (only needed for server) // and audio frame size @@ -458,14 +504,14 @@ void CChannel::OnNetTranspPropsReceived ( CNetworkTransportProps NetworkTranspor // update socket buffer (the network block size is a multiple of the // minimum network frame size) SockBuf.SetUseDoubleSystemFrameSize ( eAudioCompressionType == CT_OPUS ); // NOTE must be set BEFORE the init() - SockBuf.Init ( iNetwFrameSize, iCurSockBufNumFrames ); + SockBuf.Init ( iCeltNumCodedBytes, iCurSockBufNumFrames, bUseSequenceNumber ); } MutexSocketBuf.unlock(); MutexConvBuf.lock(); { // init conversion buffer - ConvBuf.Init ( iNetwFrameSize * iNetwFrameSizeFact ); + ConvBuf.Init ( iNetwFrameSize * iNetwFrameSizeFact, bUseSequenceNumber ); } MutexConvBuf.unlock(); } @@ -479,8 +525,23 @@ void CChannel::OnReqNetTranspProps() Protocol.CreateNetwTranspPropsMes ( GetNetworkTransportPropsFromCurrentSettings() ); } +void CChannel::OnReqSplitMessSupport() +{ + // activate split messages in our protocol (client) and return answer message to the server + Protocol.SetSplitMessageSupported ( true ); + Protocol.CreateSplitMessSupportedMes(); +} + CNetworkTransportProps CChannel::GetNetworkTransportPropsFromCurrentSettings() { + // set network flags + ENetwFlags eFlags = NF_NONE; + + if ( bUseSequenceNumber ) + { + eFlags = NF_WITH_COUNTER; + } + // use current stored settings of the channel to fill the network transport // properties structure return CNetworkTransportProps ( static_cast ( iNetwFrameSize ), @@ -488,7 +549,7 @@ CNetworkTransportProps CChannel::GetNetworkTransportPropsFromCurrentSettings() static_cast ( iNumAudioChannels ), SYSTEM_SAMPLE_RATE_HZ, eAudioCompressionType, - 0, // version of the codec + eFlags, 0 ); } @@ -559,7 +620,7 @@ EPutDataStat CChannel::PutAudioData ( const CVector& vecbyData, else { // the protocol parsing failed and this was no audio block, - // we treat this as protocol error (unkown packet) + // we treat this as protocol error (unknown packet) eRet = PS_PROT_ERR; } @@ -578,6 +639,9 @@ EPutDataStat CChannel::PutAudioData ( const CVector& vecbyData, // init audio fade-in counter iFadeInCnt = 0; + + // init level meter + SignalLevelMeter.Reset(); } // reset time-out counter (note that this must be done after the @@ -647,6 +711,9 @@ EGetDataStat CChannel::GetData ( CVector& vecbyData, // in case we are just disconnected, we have to fire a message if ( eGetStatus == GS_CHAN_NOW_DISCONNECTED ) { + // reset the protocol + Protocol.Reset(); + // emit message emit Disconnected(); } @@ -661,13 +728,26 @@ void CChannel::PrepAndSendPacket ( CHighPrioSocket* pSocket, QMutexLocker locker ( &MutexConvBuf ); // use conversion buffer to convert sound card block size in network - // block size - if ( ConvBuf.Put ( vecbyNPacket, iNPacketLen ) ) + // block size and take care of optional sequence number (note that + // the sequence number wraps automatically) + if ( ConvBuf.Put ( vecbyNPacket, iNPacketLen, iSendSequenceNumber++ ) ) { pSocket->SendPacket ( ConvBuf.GetAll(), GetAddress() ); } } +double CChannel::UpdateAndGetLevelForMeterdB ( const CVector& vecsAudio, + const int iInSize, + const bool bIsStereoIn ) +{ + // update the signal level meter and immediately return the current value + SignalLevelMeter.Update ( vecsAudio, + iInSize, + bIsStereoIn ); + + return SignalLevelMeter.GetLevelForMeterdBLeftOrMono(); +} + int CChannel::GetUploadRateKbps() { const int iAudioSizeOut = iNetwFrameSizeFact * iAudioFrameSizeSamples; diff --git a/src/channel.h b/src/channel.h index ec62e84a46..454f6f6ec4 100755 --- a/src/channel.h +++ b/src/channel.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -28,6 +28,9 @@ #include #include #include +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) +# include +#endif #include "global.h" #include "buffer.h" #include "util.h" @@ -108,18 +111,18 @@ class CChannel : public QObject void CreateVersionAndOSMes() { Protocol.CreateVersionAndOSMes(); } void CreateMuteStateHasChangedMes ( const int iChanID, const bool bIsMuted ) { Protocol.CreateMuteStateHasChangedMes ( iChanID, bIsMuted ); } - void SetGain ( const int iChanID, const double dNewGain ); - double GetGain ( const int iChanID ); - double GetFadeInGain() { return static_cast ( iFadeInCnt ) / iFadeInCntMax; } + void SetGain ( const int iChanID, const float fNewGain ); + float GetGain ( const int iChanID ); + float GetFadeInGain() { return static_cast ( iFadeInCnt ) / iFadeInCntMax; } - void SetPan ( const int iChanID, const double dNewPan ); - double GetPan ( const int iChanID ); + void SetPan ( const int iChanID, const float fNewPan ); + float GetPan ( const int iChanID ); - void SetRemoteChanGain ( const int iId, const double dGain ) - { Protocol.CreateChanGainMes ( iId, dGain ); } + void SetRemoteChanGain ( const int iId, const float fGain ) + { Protocol.CreateChanGainMes ( iId, fGain ); } - void SetRemoteChanPan ( const int iId, const double dPan ) - { Protocol.CreateChanPanMes ( iId, dPan ); } + void SetRemoteChanPan ( const int iId, const float fPan ) + { Protocol.CreateChanPanMes ( iId, fPan ); } bool SetSockBufNumFrames ( const int iNewNumFrames, const bool bPreserve = false ); @@ -141,7 +144,7 @@ class CChannel : public QObject bool GetDoAutoSockBufSize() const { return bDoAutoSockBufSize; } int GetNetwFrameSizeFact() const { return iNetwFrameSizeFact; } - int GetNetwFrameSize() const { return iNetwFrameSize; } + int GetCeltNumCodedBytes() const { return iCeltNumCodedBytes; } void GetBufErrorRates ( CVector& vecErrRates, double& dLimit, double& dMaxUpLimit ) { SockBuf.GetErrorRates ( vecErrRates, dLimit, dMaxUpLimit ); } @@ -159,21 +162,26 @@ class CChannel : public QObject } void CreateClientIDMes ( const int iChanID ) { Protocol.CreateClientIDMes ( iChanID ); } void CreateReqNetwTranspPropsMes() { Protocol.CreateReqNetwTranspPropsMes(); } + void CreateReqSplitMessSupportMes() { Protocol.CreateReqSplitMessSupportMes(); } void CreateReqJitBufMes() { Protocol.CreateReqJitBufMes(); } void CreateReqConnClientsList() { Protocol.CreateReqConnClientsList(); } void CreateChatTextMes ( const QString& strChatText ) { Protocol.CreateChatTextMes ( strChatText ); } void CreateLicReqMes ( const ELicenceType eLicenceType ) { Protocol.CreateLicenceRequiredMes ( eLicenceType ); } - void CreateReqChannelLevelListMes ( bool bOptIn ) { Protocol.CreateReqChannelLevelListMes ( bOptIn ); } + +// TODO needed for compatibility to old servers >= 3.4.6 and <= 3.5.12 +void CreateReqChannelLevelListMes() { Protocol.CreateReqChannelLevelListMes(); } void CreateConClientListMes ( const CVector& vecChanInfo ) { Protocol.CreateConClientListMes ( vecChanInfo ); } - CNetworkTransportProps GetNetworkTransportPropsFromCurrentSettings(); + void CreateRecorderStateMes ( const ERecorderState eRecorderState ) + { Protocol.CreateRecorderStateMes ( eRecorderState ); } - bool ChannelLevelsRequired() const { return bChannelLevelsRequired; } + CNetworkTransportProps GetNetworkTransportPropsFromCurrentSettings(); - double GetPrevLevel() const { return dPrevLevel; } - void SetPrevLevel ( const double nPL ) { dPrevLevel = nPL; } + double UpdateAndGetLevelForMeterdB ( const CVector& vecsAudio, + const int iInSize, + const bool bIsStereoIn ); protected: bool ProtocolIsEnabled(); @@ -186,62 +194,69 @@ class CChannel : public QObject eAudioCompressionType = CT_NONE; iNetwFrameSizeFact = FRAME_SIZE_FACTOR_PREFERRED; iNetwFrameSize = CELT_MINIMUM_NUM_BYTES; + iCeltNumCodedBytes = CELT_MINIMUM_NUM_BYTES; iNumAudioChannels = 1; // mono - - dPrevLevel = 0.0; + bUseSequenceNumber = false; } // connection parameters - CHostAddress InetAddr; + CHostAddress InetAddr; // channel info - CChannelCoreInfo ChannelInfo; + CChannelCoreInfo ChannelInfo; // mixer and effect settings - CVector vecdGains; - CVector vecdPannings; + CVector vecfGains; + CVector vecfPannings; // network jitter-buffer - CNetBufWithStats SockBuf; - int iCurSockBufNumFrames; - bool bDoAutoSockBufSize; + CNetBufWithStats SockBuf; + int iCurSockBufNumFrames; + bool bDoAutoSockBufSize; + bool bUseSequenceNumber; + uint8_t iSendSequenceNumber; // network output conversion buffer - CConvBuf ConvBuf; + CConvBuf ConvBuf; // network protocol - CProtocol Protocol; + CProtocol Protocol; - int iConTimeOut; - int iConTimeOutStartVal; - int iFadeInCnt; - int iFadeInCntMax; + int iConTimeOut; + int iConTimeOutStartVal; + int iFadeInCnt; + int iFadeInCntMax; - bool bIsEnabled; - bool bIsServer; + bool bIsEnabled; + bool bIsServer; - int iNetwFrameSizeFact; - int iNetwFrameSize; - int iAudioFrameSizeSamples; + int iNetwFrameSizeFact; + int iNetwFrameSize; + int iCeltNumCodedBytes; + int iAudioFrameSizeSamples; - EAudComprType eAudioCompressionType; - int iNumAudioChannels; + EAudComprType eAudioCompressionType; + int iNumAudioChannels; - QMutex Mutex; - QMutex MutexSocketBuf; - QMutex MutexConvBuf; + QMutex Mutex; + QMutex MutexSocketBuf; + QMutex MutexConvBuf; - bool bChannelLevelsRequired; - double dPrevLevel; + CStereoSignalLevelMeter SignalLevelMeter; public slots: void OnSendProtMessage ( CVector vecMessage ); void OnJittBufSizeChange ( int iNewJitBufSize ); - void OnChangeChanGain ( int iChanID, double dNewGain ); - void OnChangeChanPan ( int iChanID, double dNewPan ); + void OnChangeChanGain ( int iChanID, float fNewGain ); + void OnChangeChanPan ( int iChanID, float fNewPan ); void OnChangeChanInfo ( CChannelCoreInfo ChanInfo ); void OnNetTranspPropsReceived ( CNetworkTransportProps NetworkTransportProps ); void OnReqNetTranspProps(); + void OnReqSplitMessSupport(); + void OnSplitMessSupported() { Protocol.SetSplitMessageSupported ( true ); } + + void OnVersionAndOSReceived ( COSUtil::EOpSystemType eOSType, + QString strVersion ); void OnParseMessageBody ( CVector vecbyMesBodyData, int iRecCounter, @@ -268,8 +283,6 @@ public slots: void OnNewConnection() { emit NewConnection(); } - void OnReqChannelLevelList ( bool bOptIn ) { bChannelLevelsRequired = bOptIn; } - signals: void MessReadyForSending ( CVector vecMessage ); void NewConnection(); @@ -287,6 +300,7 @@ public slots: void ReqNetTranspProps(); void LicenceRequired ( ELicenceType eLicenceType ); void VersionAndOSReceived ( COSUtil::EOpSystemType eOSType, QString strVersion ); + void RecorderStateReceived ( ERecorderState eRecorderState ); void Disconnected(); void DetectedCLMessage ( CVector vecbyMesBodyData, diff --git a/src/chatdlg.cpp b/src/chatdlg.cpp index 0a1554f339..9664794bbc 100755 --- a/src/chatdlg.cpp +++ b/src/chatdlg.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -26,8 +26,8 @@ /* Implementation *************************************************************/ -CChatDlg::CChatDlg ( QWidget* parent, Qt::WindowFlags f ) : - QDialog ( parent, f ) +CChatDlg::CChatDlg ( QWidget* parent ) : + QDialog ( parent, Qt::Window ) // use Qt::Window to get min/max window buttons { setupUi ( this ); @@ -52,17 +52,35 @@ CChatDlg::CChatDlg ( QWidget* parent, Qt::WindowFlags f ) : txvChatWindow->clear(); edtLocalInputText->clear(); + // we do not want to show a cursor in the chat history + txvChatWindow->setCursorWidth ( 0 ); + + // set a placeholder text to make sure where to type the message in (#384) + edtLocalInputText->setPlaceholderText ( tr ( "Type a message here" ) ); + + + // Menu ------------------------------------------------------------------- + QMenuBar* pMenu = new QMenuBar ( this ); + QMenu* pEditMenu = new QMenu ( tr ( "&Edit" ), this ); + + pEditMenu->addAction ( tr ( "Cl&ear Chat History" ), this, + SLOT ( OnClearChatHistory() ), QKeySequence ( Qt::CTRL + Qt::Key_E ) ); + + pMenu->addMenu ( pEditMenu ); + + // Now tell the layout about the menu + layout()->setMenuBar ( pMenu ); + // Connections ------------------------------------------------------------- - QObject::connect ( edtLocalInputText, - SIGNAL ( textChanged ( const QString& ) ), - this, SLOT ( OnLocalInputTextTextChanged ( const QString& ) ) ); + QObject::connect ( edtLocalInputText, &QLineEdit::textChanged, + this, &CChatDlg::OnLocalInputTextTextChanged ); - QObject::connect ( edtLocalInputText, SIGNAL ( returnPressed() ), - this, SLOT ( OnLocalInputTextReturnPressed() ) ); + QObject::connect ( butSend, &QPushButton::clicked, + this, &CChatDlg::OnSendText ); - QObject::connect ( butClear, SIGNAL ( pressed() ), - this, SLOT ( OnClearPressed() ) ); + QObject::connect ( txvChatWindow, &QTextBrowser::anchorClicked, + this, &CChatDlg::OnAnchorClicked ); } void CChatDlg::OnLocalInputTextTextChanged ( const QString& strNewText ) @@ -70,19 +88,22 @@ void CChatDlg::OnLocalInputTextTextChanged ( const QString& strNewText ) // check and correct length if ( strNewText.length() > MAX_LEN_CHAT_TEXT ) { - // text is too long, update control with shortend text + // text is too long, update control with shortened text edtLocalInputText->setText ( strNewText.left ( MAX_LEN_CHAT_TEXT ) ); } } -void CChatDlg::OnLocalInputTextReturnPressed() +void CChatDlg::OnSendText() { - // send new text and clear line afterwards - emit NewLocalInputText ( edtLocalInputText->text() ); - edtLocalInputText->clear(); + // send new text and clear line afterwards, do not send an empty message + if ( !edtLocalInputText->text().isEmpty() ) + { + emit NewLocalInputText ( edtLocalInputText->text() ); + edtLocalInputText->clear(); + } } -void CChatDlg::OnClearPressed() +void CChatDlg::OnClearChatHistory() { // clear chat window txvChatWindow->clear(); @@ -90,9 +111,32 @@ void CChatDlg::OnClearPressed() void CChatDlg::AddChatText ( QString strChatText ) { + // notify accessibility plugin that text has changed + QAccessible::updateAccessibility ( new QAccessibleValueChangeEvent ( txvChatWindow, strChatText ) ); + + // analyze strChatText to check if hyperlink (limit ourselves to https://) but do not + // replace the hyperlinks if any HTML code for a hyperlink was found (the user has done the HTML + // coding hisself and we should not mess with that) + if ( !strChatText.contains ( QRegExp ( "href\\s*=|src\\s*=" ) ) ) + { + // searches for all occurrences of https and cuts until a space (\S matches any non-white-space + // character and the + means that matches the previous element one or more times.) + strChatText.replace ( QRegExp ( "(https://\\S+)" ), "\\1" ); + } + // add new text in chat window txvChatWindow->append ( strChatText ); +} - // notify accessibility plugin that text has changed - QAccessible::updateAccessibility ( new QAccessibleValueChangeEvent ( txvChatWindow, strChatText ) ); +void CChatDlg::OnAnchorClicked ( const QUrl& Url ) +{ + // only allow https URLs to be opened in an external browser + if ( Url.scheme() == QLatin1String ( "https" ) ) + { + if ( QMessageBox::question ( this, APP_NAME, tr ( "Do you want to open the link" ) + " " + Url.toString() + + " " + tr ( "in an external browser?" ), QMessageBox::Yes | QMessageBox::No ) == QMessageBox::Yes ) + { + QDesktopServices::openUrl ( Url ); + } + } } diff --git a/src/chatdlg.h b/src/chatdlg.h index f9e7dc2cb9..8ce46ff4e3 100755 --- a/src/chatdlg.h +++ b/src/chatdlg.h @@ -18,17 +18,22 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ +#pragma once + #include #include #include #include +#include #include #include #include +#include +#include #include "global.h" #include "ui_chatdlgbase.h" @@ -39,14 +44,15 @@ class CChatDlg : public QDialog, private Ui_CChatDlgBase Q_OBJECT public: - CChatDlg ( QWidget* parent = nullptr, Qt::WindowFlags f = nullptr ); + CChatDlg ( QWidget* parent = nullptr ); void AddChatText ( QString strChatText ); public slots: - void OnLocalInputTextReturnPressed(); + void OnSendText(); void OnLocalInputTextTextChanged ( const QString& strNewText ); - void OnClearPressed(); + void OnClearChatHistory(); + void OnAnchorClicked ( const QUrl& Url ); void keyPressEvent ( QKeyEvent *e ) // block escape key { if ( e->key() != Qt::Key_Escape ) QDialog::keyPressEvent ( e ); } diff --git a/src/chatdlgbase.ui b/src/chatdlgbase.ui index 0d9addecab..7d59db9661 100755 --- a/src/chatdlgbase.ui +++ b/src/chatdlgbase.ui @@ -1,7 +1,8 @@ - + + CChatDlgBase - - + + 0 0 @@ -9,70 +10,54 @@ 405 - - + + 0 0 - + Chat - - :/png/main/res/fronticon.png + + + :/png/main/res/fronticon.png:/png/main/res/fronticon.png - + true - + - - - Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::NoTextInteraction|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + false + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + false + + + false - - - - + - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 181 - 27 - - - - - - - - Cl&ear - - - false - - + - - - &Close + + + &Send - - false + + true - - false + + true @@ -81,30 +66,10 @@ - edtLocalInputText txvChatWindow - butClear - buttonClose - + - - - buttonClose - clicked() - CChatDlgBase - accept() - - - 20 - 20 - - - 20 - 20 - - - - + diff --git a/src/client.cpp b/src/client.cpp index a6dd8e7aab..e6d3da0e1f 100755 --- a/src/client.cpp +++ b/src/client.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -28,28 +28,12 @@ /* Implementation *************************************************************/ CClient::CClient ( const quint16 iPortNumber, const QString& strConnOnStartupAddress, - const int iCtrlMIDIChannel, + const QString& strMIDISetup, const bool bNoAutoJackConnect, - const QString& strNClientName ) : - vstrIPAddress ( MAX_NUM_SERVER_ADDR_ITEMS, "" ), - ChannelInfo (), - vecStoredFaderTags ( MAX_NUM_STORED_FADER_SETTINGS, "" ), - vecStoredFaderLevels ( MAX_NUM_STORED_FADER_SETTINGS, AUD_MIX_FADER_MAX ), - vecStoredPanValues ( MAX_NUM_STORED_FADER_SETTINGS, AUD_MIX_PAN_MAX / 2 ), - vecStoredFaderIsSolo ( MAX_NUM_STORED_FADER_SETTINGS, false ), - vecStoredFaderIsMute ( MAX_NUM_STORED_FADER_SETTINGS, false ), - iNewClientFaderLevel ( 100 ), - bConnectDlgShowAllMusicians ( true ), + const QString& strNClientName, + const bool bNMuteMeInPersonalMix ) : + ChannelInfo ( ), strClientName ( strNClientName ), - vecWindowPosMain (), // empty array - vecWindowPosSettings (), // empty array - vecWindowPosChat (), // empty array - vecWindowPosProfile (), // empty array - vecWindowPosConnect (), // empty array - bWindowWasShownSettings ( false ), - bWindowWasShownChat ( false ), - bWindowWasShownProfile ( false ), - bWindowWasShownConnect ( false ), Channel ( false ), /* we need a client channel -> "false" */ CurOpusEncoder ( nullptr ), CurOpusDecoder ( nullptr ), @@ -61,9 +45,9 @@ CClient::CClient ( const quint16 iPortNumber, iNumAudioChannels ( 1 ), bIsInitializationPhase ( true ), bMuteOutStream ( false ), - dMuteOutStreamGain ( 1.0 ), + fMuteOutStreamGain ( 1.0f ), Socket ( &Channel, iPortNumber ), - Sound ( AudioCallback, this, iCtrlMIDIChannel, bNoAutoJackConnect, strNClientName ), + Sound ( AudioCallback, this, strMIDISetup, bNoAutoJackConnect, strNClientName ), iAudioInFader ( AUD_FADER_IN_MIDDLE ), bReverbOnLeftChan ( false ), iReverbLevel ( 0 ), @@ -75,11 +59,9 @@ CClient::CClient ( const quint16 iPortNumber, bFraSiFactDefSupported ( false ), bFraSiFactSafeSupported ( false ), eGUIDesign ( GD_ORIGINAL ), - bDisplayChannelLevels ( true ), bEnableOPUS64 ( false ), bJitterBufferOK ( true ), - strCentralServerAddress ( "" ), - eCentralServerAddressType ( AT_DEFAULT ), + bNuteMeInPersonalMix ( bNMuteMeInPersonalMix ), iServerSockBufNumFrames ( DEF_NET_BUF_SIZE_NUM_BL ), pSignalHandler ( CSignalHandler::getSingletonP() ) { @@ -126,103 +108,90 @@ CClient::CClient ( const quint16 iPortNumber, // Connections ------------------------------------------------------------- // connections for the protocol mechanism - QObject::connect ( &Channel, - SIGNAL ( MessReadyForSending ( CVector ) ), - this, SLOT ( OnSendProtMessage ( CVector ) ) ); + QObject::connect ( &Channel, &CChannel::MessReadyForSending, + this, &CClient::OnSendProtMessage ); - QObject::connect ( &Channel, - SIGNAL ( DetectedCLMessage ( CVector, int, CHostAddress ) ), - this, SLOT ( OnDetectedCLMessage ( CVector, int, CHostAddress ) ) ); + QObject::connect ( &Channel, &CChannel::DetectedCLMessage, + this, &CClient::OnDetectedCLMessage ); - QObject::connect ( &Channel, SIGNAL ( ReqJittBufSize() ), - this, SLOT ( OnReqJittBufSize() ) ); + QObject::connect ( &Channel, &CChannel::ReqJittBufSize, + this, &CClient::OnReqJittBufSize ); - QObject::connect ( &Channel, SIGNAL ( JittBufSizeChanged ( int ) ), - this, SLOT ( OnJittBufSizeChanged ( int ) ) ); + QObject::connect ( &Channel, &CChannel::JittBufSizeChanged, + this, &CClient::OnJittBufSizeChanged ); - QObject::connect ( &Channel, SIGNAL ( ReqChanInfo() ), - this, SLOT ( OnReqChanInfo() ) ); + QObject::connect ( &Channel, &CChannel::ReqChanInfo, + this, &CClient::OnReqChanInfo ); - QObject::connect ( &Channel, - SIGNAL ( ConClientListMesReceived ( CVector ) ), - SIGNAL ( ConClientListMesReceived ( CVector ) ) ); + QObject::connect ( &Channel, &CChannel::ConClientListMesReceived, + this, &CClient::ConClientListMesReceived ); - QObject::connect ( &Channel, - SIGNAL ( Disconnected() ), - SIGNAL ( Disconnected() ) ); + QObject::connect ( &Channel, &CChannel::Disconnected, + this, &CClient::Disconnected ); - QObject::connect ( &Channel, SIGNAL ( NewConnection() ), - this, SLOT ( OnNewConnection() ) ); + QObject::connect ( &Channel, &CChannel::NewConnection, + this, &CClient::OnNewConnection ); - QObject::connect ( &Channel, - SIGNAL ( ChatTextReceived ( QString ) ), - SIGNAL ( ChatTextReceived ( QString ) ) ); + QObject::connect ( &Channel, &CChannel::ChatTextReceived, + this, &CClient::ChatTextReceived ); - QObject::connect ( &Channel, - SIGNAL ( ClientIDReceived ( int ) ), - SIGNAL ( ClientIDReceived ( int ) ) ); + QObject::connect ( &Channel, &CChannel::ClientIDReceived, + this, &CClient::OnClientIDReceived ); - QObject::connect ( &Channel, - SIGNAL ( MuteStateHasChangedReceived ( int, bool ) ), - SIGNAL ( MuteStateHasChangedReceived ( int, bool ) ) ); + QObject::connect ( &Channel, &CChannel::MuteStateHasChangedReceived, + this, &CClient::MuteStateHasChangedReceived ); - QObject::connect ( &Channel, - SIGNAL ( LicenceRequired ( ELicenceType ) ), - SIGNAL ( LicenceRequired ( ELicenceType ) ) ); + QObject::connect ( &Channel, &CChannel::LicenceRequired, + this, &CClient::LicenceRequired ); - QObject::connect ( &Channel, - SIGNAL ( VersionAndOSReceived ( COSUtil::EOpSystemType, QString ) ), - SIGNAL ( VersionAndOSReceived ( COSUtil::EOpSystemType, QString ) ) ); + QObject::connect ( &Channel, &CChannel::VersionAndOSReceived, + this, &CClient::VersionAndOSReceived ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLMessReadyForSending ( CHostAddress, CVector ) ), - this, SLOT ( OnSendCLProtMessage ( CHostAddress, CVector ) ) ); + QObject::connect ( &Channel, &CChannel::RecorderStateReceived, + this, &CClient::RecorderStateReceived ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLServerListReceived ( CHostAddress, CVector ) ), - SIGNAL ( CLServerListReceived ( CHostAddress, CVector ) ) ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLMessReadyForSending, + this, &CClient::OnSendCLProtMessage ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLConnClientsListMesReceived ( CHostAddress, CVector ) ), - SIGNAL ( CLConnClientsListMesReceived ( CHostAddress, CVector ) ) ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLServerListReceived, + this, &CClient::CLServerListReceived ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLPingReceived ( CHostAddress, int ) ), - this, SLOT ( OnCLPingReceived ( CHostAddress, int ) ) ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLRedServerListReceived, + this, &CClient::CLRedServerListReceived ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLPingWithNumClientsReceived ( CHostAddress, int, int ) ), - this, SLOT ( OnCLPingWithNumClientsReceived ( CHostAddress, int, int ) ) ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLConnClientsListMesReceived, + this, &CClient::CLConnClientsListMesReceived ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLDisconnection ( CHostAddress ) ), - this, SLOT ( OnCLDisconnection ( CHostAddress ) ) ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLPingReceived, + this, &CClient::OnCLPingReceived ); -#ifdef ENABLE_CLIENT_VERSION_AND_OS_DEBUGGING - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLVersionAndOSReceived ( CHostAddress, COSUtil::EOpSystemType, QString ) ), - SIGNAL ( CLVersionAndOSReceived ( CHostAddress, COSUtil::EOpSystemType, QString ) ) ); -#endif + QObject::connect ( &ConnLessProtocol, &CProtocol::CLPingWithNumClientsReceived, + this, &CClient::OnCLPingWithNumClientsReceived ); + + QObject::connect ( &ConnLessProtocol, &CProtocol::CLDisconnection , + this, &CClient::OnCLDisconnection ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLChannelLevelListReceived ( CHostAddress, CVector ) ), - this, SLOT ( OnCLChannelLevelListReceived ( CHostAddress, CVector ) ) ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLVersionAndOSReceived, + this, &CClient::CLVersionAndOSReceived ); + + QObject::connect ( &ConnLessProtocol, &CProtocol::CLChannelLevelListReceived, + this, &CClient::CLChannelLevelListReceived ); // other - QObject::connect ( &Sound, SIGNAL ( ReinitRequest ( int ) ), - this, SLOT ( OnSndCrdReinitRequest ( int ) ) ); + QObject::connect ( &Sound, &CSound::ReinitRequest, + this, &CClient::OnSndCrdReinitRequest ); - QObject::connect ( &Sound, - SIGNAL ( ControllerInFaderLevel ( int, int ) ), - SIGNAL ( ControllerInFaderLevel ( int, int ) ) ); + QObject::connect ( &Sound, &CSound::ControllerInFaderLevel, + this, &CClient::OnControllerInFaderLevel ); - QObject::connect ( &Socket, SIGNAL ( InvalidPacketReceived ( CHostAddress ) ), - this, SLOT ( OnInvalidPacketReceived ( CHostAddress ) ) ); + QObject::connect ( &Socket, &CHighPrioSocket::InvalidPacketReceived, + this, &CClient::OnInvalidPacketReceived ); - QObject::connect ( pSignalHandler, - SIGNAL ( HandledSignal ( int ) ), - this, SLOT ( OnHandledSignal ( int ) ) ); + QObject::connect ( pSignalHandler, &CSignalHandler::HandledSignal, + this, &CClient::OnHandledSignal ); + // start timer so that elapsed time works + PreciseTime.start(); // start the socket (it is important to start the socket after all // initializations and connections) @@ -236,6 +205,29 @@ CClient::CClient ( const quint16 iPortNumber, } } +CClient::~CClient() +{ + // if we were running, stop sound device + if ( Sound.IsRunning() ) + { + Sound.Stop(); + } + + // free audio encoders and decoders + opus_custom_encoder_destroy ( OpusEncoderMono ); + opus_custom_decoder_destroy ( OpusDecoderMono ); + opus_custom_encoder_destroy ( OpusEncoderStereo ); + opus_custom_decoder_destroy ( OpusDecoderStereo ); + opus_custom_encoder_destroy ( Opus64EncoderMono ); + opus_custom_decoder_destroy ( Opus64DecoderMono ); + opus_custom_encoder_destroy ( Opus64EncoderStereo ); + opus_custom_decoder_destroy ( Opus64DecoderStereo ); + + // free audio modes + opus_custom_mode_destroy ( OpusMode ); + opus_custom_mode_destroy ( Opus64Mode ); +} + void CClient::OnSendProtMessage ( CVector vecMessage ) { // the protocol queries me to call the function to send the message @@ -253,7 +245,7 @@ void CClient::OnSendCLProtMessage ( CHostAddress InetAddr, void CClient::OnInvalidPacketReceived ( CHostAddress RecHostAddr ) { - // message coult not be parsed, check if the packet comes + // message could not be parsed, check if the packet comes // from the server we just connected -> if yes, send // disconnect message since the server may not know that we // are not connected anymore @@ -301,8 +293,8 @@ void CClient::OnNewConnection() Channel.CreateReqConnClientsList(); CreateServerJitterBufferMessage(); - // send opt-in / out for Channel Level updates - Channel.CreateReqChannelLevelListMes ( bDisplayChannelLevels ); +// TODO needed for compatibility to old servers >= 3.4.6 and <= 3.5.12 +Channel.CreateReqChannelLevelListMes(); } void CClient::CreateServerJitterBufferMessage() @@ -362,16 +354,6 @@ int CClient::EvaluatePingMessage ( const int iMs ) return PreciseTime.elapsed() - iMs; } -void CClient::SetCentralServerAddressType ( const ECSAddType eNCSAT ) -{ - if ( eCentralServerAddressType != eNCSAT ) - { - // update type and emit message to update the server list, too - eCentralServerAddressType = eNCSAT; - emit CentralServerAddressTypeChanged(); - } -} - void CClient::SetDoAutoSockBufSize ( const bool bValue ) { // first, set new value in the channel object @@ -381,17 +363,17 @@ void CClient::SetDoAutoSockBufSize ( const bool bValue ) CreateServerJitterBufferMessage(); } -void CClient::SetRemoteChanGain ( const int iId, - const double dGain, - const bool bIsMyOwnFader ) +void CClient::SetRemoteChanGain ( const int iId, + const float fGain, + const bool bIsMyOwnFader ) { // if this gain is for my own channel, apply the value for the Mute Myself function if ( bIsMyOwnFader ) { - dMuteOutStreamGain = dGain; + fMuteOutStreamGain = fGain; } - Channel.SetRemoteChanGain ( iId, dGain ); + Channel.SetRemoteChanGain ( iId, fGain ); } bool CClient::SetServerAddr ( QString strNAddr ) @@ -434,14 +416,6 @@ bool CClient::GetAndResetbJitterBufferOKFlag() return bSocketJitBufOKFlag; } -void CClient::SetDisplayChannelLevels ( const bool bNDCL ) -{ - bDisplayChannelLevels = bNDCL; - - // tell any connected server about the change - Channel.CreateReqChannelLevelListMes ( bDisplayChannelLevels ); -} - void CClient::SetSndCrdPrefFrameSizeFactor ( const int iNewFactor ) { // first check new input parameter @@ -531,7 +505,7 @@ void CClient::SetAudioChannels ( const EAudChanConf eNAudChanConf ) } } -QString CClient::SetSndCrdDev ( const int iNewDev ) +QString CClient::SetSndCrdDev ( const QString strNewDev ) { // if client was running then first // stop it and restart again after new initialization @@ -541,7 +515,7 @@ QString CClient::SetSndCrdDev ( const int iNewDev ) Sound.Stop(); } - const QString strReturn = Sound.SetDev ( iNewDev ); + const QString strError = Sound.SetDev ( strNewDev ); // init again because the sound card actual buffer size might // be changed on new device @@ -553,7 +527,13 @@ QString CClient::SetSndCrdDev ( const int iNewDev ) Sound.Start(); } - return strReturn; + // in case of an error inform the GUI about it + if ( !strError.isEmpty() ) + { + emit SoundDeviceChanged ( strError ); + } + + return strError; } void CClient::SetSndCrdLeftInputChannel ( const int iNewChan ) @@ -638,45 +618,50 @@ void CClient::SetSndCrdRightOutputChannel ( const int iNewChan ) void CClient::OnSndCrdReinitRequest ( int iSndCrdResetType ) { - // in older QT versions, enums cannot easily be used in signals without - // registering them -> workaroud: we use the int type and cast to the enum - const ESndCrdResetType eSndCrdResetType = - static_cast ( iSndCrdResetType ); + QString strError = ""; - // if client was running then first - // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) + // audio device notifications can come at any time and they are in a + // different thread, therefore we need a mutex here + MutexDriverReinit.lock(); { - Sound.Stop(); - } + // in older QT versions, enums cannot easily be used in signals without + // registering them -> workaroud: we use the int type and cast to the enum + const ESndCrdResetType eSndCrdResetType = + static_cast ( iSndCrdResetType ); - // perform reinit request as indicated by the request type parameter - if ( eSndCrdResetType != RS_ONLY_RESTART ) - { - if ( eSndCrdResetType != RS_ONLY_RESTART_AND_INIT ) + // if client was running then first + // stop it and restart again after new initialization + const bool bWasRunning = Sound.IsRunning(); + if ( bWasRunning ) { - // reinit the driver if requested - // (we use the currently selected driver) - Sound.SetDev ( Sound.GetDev() ); + Sound.Stop(); } - // init client object (must always be performed if the driver - // was changed) - Init(); - } + // perform reinit request as indicated by the request type parameter + if ( eSndCrdResetType != RS_ONLY_RESTART ) + { + if ( eSndCrdResetType != RS_ONLY_RESTART_AND_INIT ) + { + // reinit the driver if requested + // (we use the currently selected driver) + strError = Sound.SetDev ( Sound.GetDev() ); + } - if ( bWasRunning ) - { - // restart client - Sound.Start(); + // init client object (must always be performed if the driver + // was changed) + Init(); + } + + if ( bWasRunning ) + { + // restart client + Sound.Start(); + } } -} + MutexDriverReinit.unlock(); -void CClient::OnCLChannelLevelListReceived ( CHostAddress InetAddr, - CVector vecLevelList ) -{ - emit CLChannelLevelListReceived ( InetAddr, vecLevelList ); + // inform GUI about the sound card device change + emit SoundDeviceChanged ( strError ); } void CClient::OnHandledSignal ( int sigNum ) @@ -690,7 +675,13 @@ void CClient::OnHandledSignal ( int sigNum ) { case SIGINT: case SIGTERM: - // This should trigger OnAboutToQuit + // if connected, terminate connection (needed for headless mode) + if ( IsRunning() ) + { + Stop(); + } + + // this should trigger OnAboutToQuit QCoreApplication::instance()->exit(); break; @@ -700,6 +691,35 @@ void CClient::OnHandledSignal ( int sigNum ) #endif } +void CClient::OnControllerInFaderLevel ( int iChannelIdx, + int iValue ) +{ + // in case of a headless client the faders cannot be moved so we need + // to send the controller information directly to the server +#ifdef HEADLESS + // only apply new fader level if channel index is valid + if ( ( iChannelIdx >= 0 ) && ( iChannelIdx < MAX_NUM_CHANNELS ) ) + { + SetRemoteChanGain ( iChannelIdx, MathUtils::CalcFaderGain ( iValue ), false ); + } +#endif + + emit ControllerInFaderLevel ( iChannelIdx, iValue ); +} + +void CClient::OnClientIDReceived ( int iChanID ) +{ + // for headless mode we support to mute our own signal in the personal mix + // (note that the check for headless is done in the main.cpp and must not + // be checked here) + if ( bNuteMeInPersonalMix ) + { + SetRemoteChanGain ( iChanID, 0, false ); + } + + emit ClientIDReceived ( iChanID ); +} + void CClient::Start() { // init object @@ -891,7 +911,7 @@ void CClient::Init() vecZeros.Init ( iStereoBlockSizeSam, 0 ); vecsStereoSndCrdMuteStream.Init ( iStereoBlockSizeSam ); - dMuteOutStreamGain = 1.0; + fMuteOutStreamGain = 1.0f; opus_custom_encoder_ctl ( CurOpusEncoder, OPUS_SET_BITRATE ( @@ -908,8 +928,9 @@ void CClient::Init() iNumAudioChannels ); // init reverberation - AudioReverbL.Init ( SYSTEM_SAMPLE_RATE_HZ ); - AudioReverbR.Init ( SYSTEM_SAMPLE_RATE_HZ ); + AudioReverb.Init ( eAudioChannelConf, + iStereoBlockSizeSam, + SYSTEM_SAMPLE_RATE_HZ ); // init the sound card conversion buffers if ( bSndCrdConversionBufferRequired ) @@ -986,124 +1007,53 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) // Transmit signal --------------------------------------------------------- - // update stereo signal level meter - SignalLevelMeter.Update ( vecsStereoSndCrd ); + // update stereo signal level meter (not needed in headless mode) +#ifndef HEADLESS + SignalLevelMeter.Update ( vecsStereoSndCrd, + iMonoBlockSizeSam, + true ); +#endif // add reverberation effect if activated if ( iReverbLevel != 0 ) { - // calculate attenuation amplification factor - const double dRevLev = static_cast ( iReverbLevel ) / AUD_REVERB_MAX / 4; - - if ( eAudioChannelConf == CC_STEREO ) - { - // for stereo always apply reverberation effect on both channels - for ( i = 0; i < iStereoBlockSizeSam; i += 2 ) - { - // both channels (stereo) - AudioReverbL.ProcessSample ( vecsStereoSndCrd[i], vecsStereoSndCrd[i + 1], dRevLev ); - } - } - else - { - // mono and mono-in/stereo out mode - if ( bReverbOnLeftChan ) - { - for ( i = 0; i < iStereoBlockSizeSam; i += 2 ) - { - // left channel - int16_t sRightDummy = 0; // has to be 0 for mono reverb - AudioReverbL.ProcessSample ( vecsStereoSndCrd[i], sRightDummy, dRevLev ); - } - } - else - { - for ( i = 1; i < iStereoBlockSizeSam; i += 2 ) - { - // right channel - int16_t sRightDummy = 0; // has to be 0 for mono reverb - AudioReverbR.ProcessSample ( vecsStereoSndCrd[i], sRightDummy, dRevLev ); - } - } - } + AudioReverb.Process ( vecsStereoSndCrd, + bReverbOnLeftChan, + static_cast ( iReverbLevel ) / AUD_REVERB_MAX / 4 ); } - // mix both signals depending on the fading setting, convert - // from double to short - if ( iAudioInFader == AUD_FADER_IN_MIDDLE ) - { - // no action require if fader is in the middle and stereo is used - if ( eAudioChannelConf != CC_STEREO ) - { - // mix channels together (store result in first half of the vector) - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // for the sum make sure we have more bits available (cast to - // int32), after the normalization by 2, the result will fit - // into the old size so that cast to int16 is safe - vecsStereoSndCrd[i] = static_cast ( - ( static_cast ( vecsStereoSndCrd[j] ) + vecsStereoSndCrd[j + 1] ) / 2 ); - } - } - } - else + // apply pan (audio fader) and mix mono signals + if ( !( ( iAudioInFader == AUD_FADER_IN_MIDDLE ) && ( eAudioChannelConf == CC_STEREO ) ) ) { + // calculate pan gain in the range 0 to 1, where 0.5 is the middle position + const float fPan = static_cast ( iAudioInFader ) / AUD_FADER_IN_MAX; + if ( eAudioChannelConf == CC_STEREO ) { - // stereo - const double dAttFactStereo = static_cast ( - AUD_FADER_IN_MIDDLE - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / AUD_FADER_IN_MIDDLE; + // for stereo only apply pan attenuation on one channel (same as pan in the server) + const float fGainL = MathUtils::GetLeftPan ( fPan, false ); + const float fGainR = MathUtils::GetRightPan ( fPan, false ); - if ( iAudioInFader > AUD_FADER_IN_MIDDLE ) - { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on right channel - vecsStereoSndCrd[j + 1] = Double2Short ( dAttFactStereo * vecsStereoSndCrd[j + 1] ); - } - } - else + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on left channel - vecsStereoSndCrd[j] = Double2Short ( dAttFactStereo * vecsStereoSndCrd[j] ); - } + // note that the gain is always <= 1, therefore a simple cast is + // ok since we never can get an overload + vecsStereoSndCrd[j + 1] = static_cast ( fGainR * vecsStereoSndCrd[j + 1] ); + vecsStereoSndCrd[j] = static_cast ( fGainL * vecsStereoSndCrd[j] ); } } else { - // mono and mono-in/stereo out mode - // make sure that in the middle position the two channels are - // amplified by 1/2, if the pan is set to one channel, this - // channel should have an amplification of 1 - const double dAttFactMono = static_cast ( - AUD_FADER_IN_MIDDLE - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / AUD_FADER_IN_MIDDLE / 2; + // for mono implement a cross-fade between channels and mix them, for + // mono-in/stereo-out use no attenuation in pan center + const float fGainL = MathUtils::GetLeftPan ( fPan, eAudioChannelConf != CC_MONO_IN_STEREO_OUT ); + const float fGainR = MathUtils::GetRightPan ( fPan, eAudioChannelConf != CC_MONO_IN_STEREO_OUT ); - const double dAmplFactMono = 0.5 + static_cast ( - abs ( AUD_FADER_IN_MIDDLE - iAudioInFader ) ) / AUD_FADER_IN_MIDDLE / 2; - - if ( iAudioInFader > AUD_FADER_IN_MIDDLE ) - { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on right channel (store result in first half - // of the vector) - vecsStereoSndCrd[i] = Double2Short ( - dAmplFactMono * vecsStereoSndCrd[j] + - dAttFactMono * vecsStereoSndCrd[j + 1] ); - } - } - else + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) { - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // attenuation on left channel (store result in first half - // of the vector) - vecsStereoSndCrd[i] = Double2Short ( - dAmplFactMono * vecsStereoSndCrd[j + 1] + - dAttFactMono * vecsStereoSndCrd[j] ); - } + // note that we need the Float2Short for stereo pan mode + vecsStereoSndCrd[i] = Float2Short ( + fGainL * vecsStereoSndCrd[j] + fGainR * vecsStereoSndCrd[j + 1] ); } } } @@ -1194,27 +1144,13 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) } } -/* -// TEST -// fid=fopen('c:\\temp\test2.dat','r');x=fread(fid,'int16');fclose(fid); -static FILE* pFileDelay = fopen("c:\\temp\\test2.dat", "wb"); -short sData[2]; -for (i = 0; i < iMonoBlockSizeSam; i++) -{ - sData[0] = (short) vecsStereoSndCrd[i]; - fwrite(&sData, size_t(2), size_t(1), pFileDelay); -} -fflush(pFileDelay); -*/ - - // for muted stream we have to add our local data here if ( bMuteOutStream ) { for ( i = 0; i < iStereoBlockSizeSam; i++ ) { - vecsStereoSndCrd[i] = Double2Short ( - vecsStereoSndCrd[i] + vecsStereoSndCrdMuteStream[i] * dMuteOutStreamGain ); + vecsStereoSndCrd[i] = Float2Short ( + vecsStereoSndCrd[i] + vecsStereoSndCrdMuteStream[i] * fMuteOutStreamGain ); } } @@ -1246,7 +1182,7 @@ fflush(pFileDelay); int CClient::EstimatedOverallDelay ( const int iPingTimeMs ) { - const double dSystemBlockDurationMs = static_cast ( iOPUSFrameSizeSamples ) / + const float fSystemBlockDurationMs = static_cast ( iOPUSFrameSizeSamples ) / SYSTEM_SAMPLE_RATE_HZ * 1000; // If the jitter buffers are set effectively, i.e. they are exactly the @@ -1254,50 +1190,49 @@ int CClient::EstimatedOverallDelay ( const int iPingTimeMs ) // length. Since that is usually not the case but the buffers are usually // a bit larger than necessary, we introduce some factor for compensation. // Consider the jitter buffer on the client and on the server side, too. - const double dTotalJitterBufferDelayMs = dSystemBlockDurationMs * - static_cast ( GetSockBufNumFrames() + - GetServerSockBufNumFrames() ) * 0.7; + const float fTotalJitterBufferDelayMs = fSystemBlockDurationMs * + ( GetSockBufNumFrames() + GetServerSockBufNumFrames() ) * 0.7f; // consider delay introduced by the sound card conversion buffer by using // "GetSndCrdConvBufAdditionalDelayMonoBlSize()" - double dTotalSoundCardDelayMs = GetSndCrdConvBufAdditionalDelayMonoBlSize() * - 1000 / SYSTEM_SAMPLE_RATE_HZ; + float fTotalSoundCardDelayMs = GetSndCrdConvBufAdditionalDelayMonoBlSize() * + 1000.0f / SYSTEM_SAMPLE_RATE_HZ; // try to get the actual input/output sound card delay from the audio // interface, per definition it is not available if a 0 is returned - const double dSoundCardInputOutputLatencyMs = Sound.GetInOutLatencyMs(); + const float fSoundCardInputOutputLatencyMs = Sound.GetInOutLatencyMs(); - if ( dSoundCardInputOutputLatencyMs == 0.0 ) + if ( fSoundCardInputOutputLatencyMs == 0.0f ) { - // use an alternative aproach for estimating the sound card delay: + // use an alternative approach for estimating the sound card delay: // // we assume that we have two period sizes for the input and one for the // output, therefore we have "3 *" instead of "2 *" (for input and output) // the actual sound card buffer size // "GetSndCrdConvBufAdditionalDelayMonoBlSize" - dTotalSoundCardDelayMs += + fTotalSoundCardDelayMs += ( 3 * GetSndCrdActualMonoBlSize() ) * - 1000 / SYSTEM_SAMPLE_RATE_HZ; + 1000.0f / SYSTEM_SAMPLE_RATE_HZ; } else { // add the actual sound card latency in ms - dTotalSoundCardDelayMs += dSoundCardInputOutputLatencyMs; + fTotalSoundCardDelayMs += fSoundCardInputOutputLatencyMs; } // network packets are of the same size as the audio packets per definition // if no sound card conversion buffer is used - const double dDelayToFillNetworkPacketsMs = - GetSystemMonoBlSize() * 1000 / SYSTEM_SAMPLE_RATE_HZ; + const float fDelayToFillNetworkPacketsMs = + GetSystemMonoBlSize() * 1000.0f / SYSTEM_SAMPLE_RATE_HZ; // OPUS additional delay at small frame sizes is half a frame size - const double dAdditionalAudioCodecDelayMs = dSystemBlockDurationMs / 2; + const float fAdditionalAudioCodecDelayMs = fSystemBlockDurationMs / 2; - const double dTotalBufferDelayMs = - dDelayToFillNetworkPacketsMs + - dTotalJitterBufferDelayMs + - dTotalSoundCardDelayMs + - dAdditionalAudioCodecDelayMs; + const float fTotalBufferDelayMs = + fDelayToFillNetworkPacketsMs + + fTotalJitterBufferDelayMs + + fTotalSoundCardDelayMs + + fAdditionalAudioCodecDelayMs; - return MathUtils::round ( dTotalBufferDelayMs + iPingTimeMs ); + return MathUtils::round ( fTotalBufferDelayMs + iPingTimeMs ); } diff --git a/src/client.h b/src/client.h index bc6a1da4e1..255d78b6e9 100755 --- a/src/client.h +++ b/src/client.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -28,7 +28,7 @@ #include #include #include -#include +#include #ifdef USE_OPUS_SHARED_LIB # include "opus/opus_custom.h" #else @@ -89,14 +89,14 @@ #define OPUS_NUM_BYTES_MONO_HIGH_QUALITY 36 #define OPUS_NUM_BYTES_MONO_LOW_QUALITY_DBLE_FRAMESIZE 25 #define OPUS_NUM_BYTES_MONO_NORMAL_QUALITY_DBLE_FRAMESIZE 45 -#define OPUS_NUM_BYTES_MONO_HIGH_QUALITY_DBLE_FRAMESIZE 71 +#define OPUS_NUM_BYTES_MONO_HIGH_QUALITY_DBLE_FRAMESIZE 82 #define OPUS_NUM_BYTES_STEREO_LOW_QUALITY 24 #define OPUS_NUM_BYTES_STEREO_NORMAL_QUALITY 35 #define OPUS_NUM_BYTES_STEREO_HIGH_QUALITY 73 #define OPUS_NUM_BYTES_STEREO_LOW_QUALITY_DBLE_FRAMESIZE 47 #define OPUS_NUM_BYTES_STEREO_NORMAL_QUALITY_DBLE_FRAMESIZE 71 -#define OPUS_NUM_BYTES_STEREO_HIGH_QUALITY_DBLE_FRAMESIZE 142 +#define OPUS_NUM_BYTES_STEREO_HIGH_QUALITY_DBLE_FRAMESIZE 165 /* Classes ********************************************************************/ @@ -107,17 +107,21 @@ class CClient : public QObject public: CClient ( const quint16 iPortNumber, const QString& strConnOnStartupAddress, - const int iCtrlMIDIChannel, + const QString& strMIDISetup, const bool bNoAutoJackConnect, - const QString& strNClientName ); + const QString& strNClientName, + const bool bNMuteMeInPersonalMix ); + + virtual ~CClient(); void Start(); void Stop(); bool IsRunning() { return Sound.IsRunning(); } + bool IsCallbackEntered() const { return Sound.IsCallbackEntered(); } bool SetServerAddr ( QString strNAddr ); - double MicLeveldB_L() { return SignalLevelMeter.MicLeveldBLeft(); } - double MicLeveldB_R() { return SignalLevelMeter.MicLeveldBRight(); } + double GetLevelForMeterdBLeft() { return SignalLevelMeter.GetLevelForMeterdBLeftOrMono(); } + double GetLevelForMeterdBRight() { return SignalLevelMeter.GetLevelForMeterdBRight(); } bool GetAndResetbJitterBufferOKFlag(); @@ -126,21 +130,12 @@ class CClient : public QObject EGUIDesign GetGUIDesign() const { return eGUIDesign; } void SetGUIDesign ( const EGUIDesign eNGD ) { eGUIDesign = eNGD; } - bool GetDisplayChannelLevels() const { return bDisplayChannelLevels; } - void SetDisplayChannelLevels ( const bool bNDCL ); - EAudioQuality GetAudioQuality() const { return eAudioQuality; } void SetAudioQuality ( const EAudioQuality eNAudioQuality ); EAudChanConf GetAudioChannels() const { return eAudioChannelConf; } void SetAudioChannels ( const EAudChanConf eNAudChanConf ); - void SetServerListCentralServerAddress ( const QString& sNCentServAddr ) { strCentralServerAddress = sNCentServAddr; } - QString GetServerListCentralServerAddress() { return strCentralServerAddress; } - - void SetCentralServerAddressType ( const ECSAddType eNCSAT ); - ECSAddType GetCentralServerAddressType() { return eCentralServerAddressType; } - int GetAudioInFader() const { return iAudioInFader; } void SetAudioInFader ( const int iNV ) { iAudioInFader = iNV; } @@ -151,8 +146,7 @@ class CClient : public QObject void SetReverbOnLeftChan ( const bool bIL ) { bReverbOnLeftChan = bIL; - AudioReverbL.Clear(); - AudioReverbR.Clear(); + AudioReverb.Clear(); } void SetDoAutoSockBufSize ( const bool bValue ); @@ -180,12 +174,10 @@ class CClient : public QObject int GetUploadRateKbps() { return Channel.GetUploadRateKbps(); } // sound card device selection - int GetSndCrdNumDev() { return Sound.GetNumDev(); } - QString GetSndCrdDeviceName ( const int iDiD ) - { return Sound.GetDeviceName ( iDiD ); } + QStringList GetSndCrdDevNames() { return Sound.GetDevNames(); } - QString SetSndCrdDev ( const int iNewDev ); - int GetSndCrdDev() { return Sound.GetDev(); } + QString SetSndCrdDev ( const QString strNewDev ); + QString GetSndCrdDev() { return Sound.GetDev(); } void OpenSndCrdDriverSetup() { Sound.OpenDriverSetup(); } // sound card channel selection @@ -243,10 +235,10 @@ class CClient : public QObject void SetMuteOutStream ( const bool bDoMute ) { bMuteOutStream = bDoMute; } - void SetRemoteChanGain ( const int iId, const double dGain, const bool bIsMyOwnFader ); + void SetRemoteChanGain ( const int iId, const float fGain, const bool bIsMyOwnFader ); - void SetRemoteChanPan ( const int iId, const double dPan ) - { Channel.SetRemoteChanPan ( iId, dPan ); } + void SetRemoteChanPan ( const int iId, const float fPan ) + { Channel.SetRemoteChanPan ( iId, fPan ); } void SetRemoteInfo() { Channel.SetRemoteInfo ( ChannelInfo ); } @@ -278,28 +270,9 @@ class CClient : public QObject { Channel.GetBufErrorRates ( vecErrRates, dLimit, dMaxUpLimit ); } // settings - CVector vstrIPAddress; CChannelCoreInfo ChannelInfo; - CVector vecStoredFaderTags; - CVector vecStoredFaderLevels; - CVector vecStoredPanValues; - CVector vecStoredFaderIsSolo; - CVector vecStoredFaderIsMute; - int iNewClientFaderLevel; - bool bConnectDlgShowAllMusicians; QString strClientName; - // window position/state settings - QByteArray vecWindowPosMain; - QByteArray vecWindowPosSettings; - QByteArray vecWindowPosChat; - QByteArray vecWindowPosProfile; - QByteArray vecWindowPosConnect; - bool bWindowWasShownSettings; - bool bWindowWasShownChat; - bool bWindowWasShownProfile; - bool bWindowWasShownConnect; - #ifdef LLCON_VST_PLUGIN // VST version must have direct access to sound object CSound* GetSound() { return &Sound; } @@ -342,7 +315,7 @@ class CClient : public QObject int iNumAudioChannels; bool bIsInitializationPhase; bool bMuteOutStream; - double dMuteOutStreamGain; + float fMuteOutStreamGain; CVector vecCeltData; CHighPrioSocket Socket; @@ -354,16 +327,15 @@ class CClient : public QObject int iAudioInFader; bool bReverbOnLeftChan; int iReverbLevel; - CAudioReverb AudioReverbL; - CAudioReverb AudioReverbR; + CAudioReverb AudioReverb; int iSndCrdPrefFrameSizeFactor; int iSndCrdFrameSizeFactor; bool bSndCrdConversionBufferRequired; int iSndCardMonoBlockSizeSamConvBuff; - CBufferBase SndCrdConversionBufferIn; - CBufferBase SndCrdConversionBufferOut; + CBuffer SndCrdConversionBufferIn; + CBuffer SndCrdConversionBufferOut; CVector vecDataConvBuf; CVector vecsStereoSndCrdMuteStream; CVector vecZeros; @@ -376,23 +348,21 @@ class CClient : public QObject int iStereoBlockSizeSam; EGUIDesign eGUIDesign; - bool bDisplayChannelLevels; bool bEnableOPUS64; bool bJitterBufferOK; - - QString strCentralServerAddress; - ECSAddType eCentralServerAddressType; + bool bNuteMeInPersonalMix; + QMutex MutexDriverReinit; // server settings int iServerSockBufNumFrames; // for ping measurement - CPreciseTime PreciseTime; + QElapsedTimer PreciseTime; CSignalHandler* pSignalHandler; -public slots: +protected slots: void OnHandledSignal ( int sigNum ); void OnSendProtMessage ( CVector vecMessage ); void OnInvalidPacketReceived ( CHostAddress RecHostAddr ); @@ -417,9 +387,8 @@ public slots: int iNumClients ); void OnSndCrdReinitRequest ( int iSndCrdResetType ); - - void OnCLChannelLevelListReceived ( CHostAddress InetAddr, - CVector vecLevelList ); + void OnControllerInFaderLevel ( int iChannelIdx, int iValue ); + void OnClientIDReceived ( int iChanID ); signals: void ConClientListMesReceived ( CVector vecChanInfo ); @@ -429,10 +398,14 @@ public slots: void LicenceRequired ( ELicenceType eLicenceType ); void VersionAndOSReceived ( COSUtil::EOpSystemType eOSType, QString strVersion ); void PingTimeReceived ( int iPingTime ); + void RecorderStateReceived ( ERecorderState eRecorderState ); void CLServerListReceived ( CHostAddress InetAddr, CVector vecServerInfo ); + void CLRedServerListReceived ( CHostAddress InetAddr, + CVector vecServerInfo ); + void CLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ); @@ -440,16 +413,14 @@ public slots: int iPingTime, int iNumClients ); -#ifdef ENABLE_CLIENT_VERSION_AND_OS_DEBUGGING void CLVersionAndOSReceived ( CHostAddress InetAddr, COSUtil::EOpSystemType eOSType, QString strVersion ); -#endif void CLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); void Disconnected(); + void SoundDeviceChanged ( QString strError ); void ControllerInFaderLevel ( int iChannelIdx, int iValue ); - void CentralServerAddressTypeChanged(); }; diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index 4473608476..72725f78d9 100755 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -26,21 +26,23 @@ /* Implementation *************************************************************/ -CClientDlg::CClientDlg ( CClient* pNCliP, - CSettings* pNSetP, - const QString& strConnOnStartupAddress, - const bool bNewShowComplRegConnList, - const bool bShowAnalyzerConsole, - QWidget* parent, - Qt::WindowFlags f ) : - QDialog ( parent, f ), +CClientDlg::CClientDlg ( CClient* pNCliP, + CClientSettings* pNSetP, + const QString& strConnOnStartupAddress, + const QString& strMIDISetup, + const bool bNewShowComplRegConnList, + const bool bShowAnalyzerConsole, + const bool bMuteStream, + QWidget* parent ) : + QDialog ( parent, Qt::Window ), // use Qt::Window to get min/max window buttons pClient ( pNCliP ), pSettings ( pNSetP ), bConnectDlgWasShown ( false ), - ClientSettingsDlg ( pNCliP, parent, Qt::Window ), - ChatDlg ( parent, Qt::Window ), - ConnectDlg ( pNCliP, bNewShowComplRegConnList, parent, Qt::Dialog ), - AnalyzerConsole ( pNCliP, parent, Qt::Window ), + bMIDICtrlUsed ( !strMIDISetup.isEmpty() ), + ClientSettingsDlg ( pNCliP, pNSetP, parent ), + ChatDlg ( parent ), + ConnectDlg ( pNSetP, bNewShowComplRegConnList, parent ), + AnalyzerConsole ( pNCliP, parent ), MusicianProfileDlg ( pNCliP, parent ) { setupUi ( this ); @@ -48,25 +50,23 @@ CClientDlg::CClientDlg ( CClient* pNCliP, // Add help text to controls ----------------------------------------------- // input level meter - QString strInpLevH = "" + tr ( "Input Level Meter" ) + ": " + tr ( "The input level " - "indicators show the input level of the two stereo channels " - "of the current selected audio input." ) + "
" + + QString strInpLevH = "" + tr ( "Input Level Meter" ) + ": " + tr ( "This shows " + "the level of the two stereo channels " + "for your audio input." ) + "
" + tr ( "Make sure not to clip the input signal to avoid distortions of the " "audio signal." ); - QString strInpLevHTT = tr ( "If the " ) + APP_NAME + - tr ( " software is connected and " - "you play your instrument/sing in the microphone, the LED level " + QString strInpLevHTT = tr ( "If the application " + "is connected to a server and " + "you play your instrument/sing into the microphone, the VU " "meter should flicker. If this is not the case, you have " - "probably selected the wrong input channel (e.g. line in instead " + "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." ) + "
" + 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 (not the Recording mixer!)." ) + TOOLTIP_COM_END_TEXT; + "(Windows) audio mixer." ) + "
" + tr ( "For proper usage of the " + "application, you should not hear your singing/instrument through " + "the loudspeaker or your headphone when the software is not connected." + "This can be achieved by muting your input audio channel in the " + "Playback mixer (not the Recording mixer!)." ) + TOOLTIP_COM_END_TEXT; QString strInpLevHAccText = tr ( "Input level meter" ); QString strInpLevHAccDescr = tr ( "Simulates an analog LED level meter." ); @@ -85,23 +85,17 @@ CClientDlg::CClientDlg ( CClient* pNCliP, // connect/disconnect button butConnect->setWhatsThis ( "" + tr ( "Connect/Disconnect Button" ) + ": " + - tr ( "Push this button to connect to a server. A dialog where you can " - "select a server will open. If you are connected, pressing this " - "button will end the session." ) ); + tr ( "Opens a dialog where you can select a server to connect to. " + "If you are connected, pressing this button will end the session." ) ); butConnect->setAccessibleName ( tr ( "Connect and disconnect toggle button" ) ); - butConnect->setAccessibleDescription ( tr ( "Clicking on this " - "button changes the caption of the button from Connect to " - "Disconnect, i.e., it implements a toggle functionality for connecting " - "and disconnecting the " ) + APP_NAME + tr ( " software." ) ); - // local audio input fader QString strAudFader = "" + tr ( "Local Audio Input Fader" ) + ": " + - 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 " + tr ( "Controls the relative levels of the left and right local audio " + "channels. For a mono signal it acts as a pan between the two channels." + "For example, if 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 " ) + @@ -115,64 +109,63 @@ CClientDlg::CClientDlg ( CClient* pNCliP, sldAudioPan->setAccessibleName ( tr ( "Local audio input fader (left/right)" ) ); // reverberation level - QString strAudReverb = "" + tr ( "Reverberation Level" ) + ": " + - tr ( "A reverberation effect can be applied to one local mono audio channel or to both " + QString strAudReverb = "" + tr ( "Reverb effect" ) + ": " + + tr ( "Reverb 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 " + "reverb level can be modified. For example, if " + "a microphone signal is fed in to the right audio channel of the " + "sound card and a reverb effect needs to be applied, set the " "channel selector to right and move the fader upwards until the " - "desired reverberation level is reached." ) + "
" + tr ( - "The reverberation effect requires significant CPU so 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 " - "switched off and does not cause any additional CPU usage." ); + "desired reverb level is reached." ); lblAudioReverb->setWhatsThis ( strAudReverb ); sldAudioReverb->setWhatsThis ( strAudReverb ); - sldAudioReverb->setAccessibleName ( tr ( "Reverberation effect level setting" ) ); + sldAudioReverb->setAccessibleName ( tr ( "Reverb effect level setting" ) ); // reverberation channel selection - QString strRevChanSel = "" + tr ( "Reverberation Channel Selection" ) + ": " + + QString strRevChanSel = "" + tr ( "Reverb Channel Selection" ) + ": " + tr ( "With these radio buttons the audio input channel on which the " - "reverberation effect is applied can be chosen. Either the left " + "reverb effect is applied can be chosen. Either the left " "or right input channel can be selected." ); rbtReverbSelL->setWhatsThis ( strRevChanSel ); - rbtReverbSelL->setAccessibleName ( tr ( "Left channel selection for reverberation" ) ); + rbtReverbSelL->setAccessibleName ( tr ( "Left channel selection for reverb" ) ); rbtReverbSelR->setWhatsThis ( strRevChanSel ); - rbtReverbSelR->setAccessibleName ( tr ( "Right channel selection for reverberation" ) ); + rbtReverbSelR->setAccessibleName ( tr ( "Right channel selection for reverb" ) ); // delay LED QString strLEDDelay = "" + tr ( "Delay Status LED" ) + ": " + - 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 " - "large for jamming." ); + tr ( "Shows the current audio delay status:" ) + + "
    " + "
  • " "" + tr ( "Green" ) + ": " + tr ( "The delay is perfect for a jam " + "session." ) + "
  • " + "
  • " "" + tr ( "Yellow" ) + ": " + tr ( "A session is still possible " + "but it may be harder to play." ) + "
  • " + "
  • " "" + tr ( "Red" ) + ": " + tr ( "The delay is too large for " + "jamming." ) + "
  • " + "
"; lblDelay->setWhatsThis ( strLEDDelay ); ledDelay->setWhatsThis ( strLEDDelay ); ledDelay->setToolTip ( tr ( "If this LED indicator turns red, " - "you will not have much fun using the " ) + APP_NAME + - tr ( " software." ) + TOOLTIP_COM_END_TEXT ); + "you will not have much fun using the application." ) + + TOOLTIP_COM_END_TEXT ); ledDelay->setAccessibleName ( tr ( "Delay status LED indicator" ) ); // buffers LED QString strLEDBuffers = "" + tr ( "Buffers Status LED" ) + ": " + - 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:" ) + + tr ( "The buffers status LED shows the current audio/streaming " + "status. If the light is red, the audio stream is interrupted. " + "This is caused by one of the following problems:" ) + "
    " "
  • " + tr ( "The network jitter buffer is not large enough for the current " "network/audio interface jitter." ) + "
  • " - "
  • " + tr ( "The sound card buffer delay (buffer size) is set to too small a " - "value." ) + "
  • " - "
  • " + tr ( "The upload or download stream rate is too high for the current " - "available internet bandwidth." ) + "
  • " + "
  • " + tr ( "The sound card's buffer delay (buffer size) is too small " + "(see Settings window)." ) + "
  • " + "
  • " + tr ( "The upload or download stream rate is too high for your " + "internet bandwidth." ) + "
  • " "
  • " + tr ( "The CPU of the client or server is at 100%." ) + "
  • " "
"; @@ -184,20 +177,12 @@ CClientDlg::CClientDlg ( CClient* pNCliP, // init GUI design SetGUIDesign ( pClient->GetGUIDesign() ); + // set the settings pointer to the mixer board (must be done early) + MainMixerBoard->SetSettingsPointer ( pSettings ); + // reset mixer board MainMixerBoard->HideAll(); - // restore channel level display preference - MainMixerBoard->SetDisplayChannelLevels ( pClient->GetDisplayChannelLevels() ); - - // restore fader settings - MainMixerBoard->vecStoredFaderTags = pClient->vecStoredFaderTags; - MainMixerBoard->vecStoredFaderLevels = pClient->vecStoredFaderLevels; - MainMixerBoard->vecStoredPanValues = pClient->vecStoredPanValues; - MainMixerBoard->vecStoredFaderIsSolo = pClient->vecStoredFaderIsSolo; - MainMixerBoard->vecStoredFaderIsMute = pClient->vecStoredFaderIsMute; - MainMixerBoard->iNewClientFaderLevel = pClient->iNewClientFaderLevel; - // init status label OnTimerStatus(); @@ -205,8 +190,8 @@ CClientDlg::CClientDlg ( CClient* pNCliP, butConnect->setText ( tr ( "C&onnect" ) ); // init input level meter bars - lbrInputLevelL->setValue ( 0 ); - lbrInputLevelR->setValue ( 0 ); + lbrInputLevelL->SetValue ( 0 ); + lbrInputLevelR->SetValue ( 0 ); // init status LEDs ledBuffers->Reset(); @@ -227,11 +212,23 @@ CClientDlg::CClientDlg ( CClient* pNCliP, UpdateRevSelection(); // init connect dialog - ConnectDlg.SetShowAllMusicians ( pClient->bConnectDlgShowAllMusicians ); + ConnectDlg.SetShowAllMusicians ( pSettings->bConnectDlgShowAllMusicians ); // set window title (with no clients connected -> "0") SetMyWindowTitle ( 0 ); + // prepare Mute Myself info label (invisible by default) + lblGlobalInfoLabel->setStyleSheet ( ".QLabel { background: red; }" ); + lblGlobalInfoLabel->hide(); + + // prepare update check info label (invisible by default) + lblUpdateCheck->setText ( "" + QString ( APP_NAME ) + " " + + tr ( "software upgrade available" ) + "" ); + lblUpdateCheck->hide(); + + // setup timers + TimerCheckAudioDeviceOk.setSingleShot ( true ); // only check once after connection + // Connect on startup ------------------------------------------------------ if ( !strConnOnStartupAddress.isEmpty() ) @@ -242,19 +239,23 @@ CClientDlg::CClientDlg ( CClient* pNCliP, } - // Mac Workaround: - // If the connect button is the default button, on Mac it is highlighted - // by fading in and out a blue backgroud color. This operation consumes so - // much CPU that we get audio interruptions. - // Better solution: increase thread priority of worker thread (since the - // user can always highlight the button manually, too) -> TODO -#if defined ( __APPLE__ ) || defined ( __MACOSX ) - butConnect->setDefault ( false ); -#endif + // File menu -------------------------------------------------------------- + QMenu* pFileMenu = new QMenu ( tr ( "&File" ), this ); + + pFileMenu->addAction ( tr ( "&Load Mixer Channels Setup..." ), this, + SLOT ( OnLoadChannelSetup() ) ); + + pFileMenu->addAction ( tr ( "&Save Mixer Channels Setup..." ), this, + SLOT ( OnSaveChannelSetup() ) ); + + pFileMenu->addSeparator(); + + pFileMenu->addAction ( tr ( "E&xit" ), this, + SLOT ( close() ), QKeySequence ( Qt::CTRL + Qt::Key_Q ) ); // View menu -------------------------------------------------------------- - pViewMenu = new QMenu ( tr ( "&View" ), this ); + QMenu* pViewMenu = new QMenu ( tr ( "&View" ), this ); pViewMenu->addAction ( tr ( "&Connection Setup..." ), this, SLOT ( OnOpenConnectionSetupDialog() ) ); @@ -275,287 +276,266 @@ CClientDlg::CClientDlg ( CClient* pNCliP, SLOT ( OnOpenAnalyzerConsole() ) ); } - pViewMenu->addSeparator(); - pViewMenu->addAction ( tr ( "E&xit" ), this, - SLOT ( close() ), QKeySequence ( Qt::CTRL + Qt::Key_Q ) ); + // Edit menu -------------------------------------------------------------- + QMenu* pEditMenu = new QMenu ( tr ( "&Edit" ), this ); + QAction* NoSortAction = pEditMenu->addAction ( tr ( "N&o User Sorting" ), this, + SLOT ( OnNoSortChannels() ), QKeySequence ( Qt::CTRL + Qt::Key_O ) ); - // Main menu bar ----------------------------------------------------------- - pMenu = new QMenuBar ( this ); + QAction* ByNameAction = pEditMenu->addAction ( tr ( "Sort Users by &Name" ), this, + SLOT ( OnSortChannelsByName() ), QKeySequence ( Qt::CTRL + Qt::Key_N ) ); - pMenu->addMenu ( pViewMenu ); - pMenu->addMenu ( new CHelpMenu ( true, this ) ); + QAction* ByInstrAction = pEditMenu->addAction ( tr ( "Sort Users by &Instrument" ), this, + SLOT ( OnSortChannelsByInstrument() ), QKeySequence ( Qt::CTRL + Qt::Key_I ) ); - // Now tell the layout about the menu - layout()->setMenuBar ( pMenu ); + QAction* ByGroupAction = pEditMenu->addAction ( tr ( "Sort Users by &Group" ), this, + SLOT ( OnSortChannelsByGroupID() ), QKeySequence ( Qt::CTRL + Qt::Key_G ) ); + QAction* ByCityAction = pEditMenu->addAction ( tr ( "Sort Users by &City" ), this, + SLOT ( OnSortChannelsByCity() ), QKeySequence ( Qt::CTRL + Qt::Key_T ) ); - // Instrument pictures popup menu ------------------------------------------ - pInstrPictPopupMenu = new QMenu ( this ); + // the sorting menu entries shall be checkable and exclusive + QActionGroup* SortActionGroup = new QActionGroup ( this ); + SortActionGroup->setExclusive ( true ); + NoSortAction->setCheckable ( true ); + SortActionGroup->addAction ( NoSortAction ); + ByNameAction->setCheckable ( true ); + SortActionGroup->addAction ( ByNameAction ); + ByInstrAction->setCheckable ( true ); + SortActionGroup->addAction ( ByInstrAction ); + ByGroupAction->setCheckable ( true ); + SortActionGroup->addAction ( ByGroupAction ); + ByCityAction->setCheckable ( true ); + SortActionGroup->addAction ( ByCityAction ); - // add an entry for all known instruments - for ( int iCurInst = 0; iCurInst < CInstPictures::GetNumAvailableInst(); iCurInst++ ) + // initialize sort type setting (i.e., recover stored setting) + switch ( pSettings->eChannelSortType ) { - // create a menu action with text and image - QAction* pCurAction = new QAction ( - QIcon ( CInstPictures::GetResourceReference ( iCurInst ) ), - CInstPictures::GetName ( iCurInst ), - this ); - - // add data to identify the action data when it is triggered - pCurAction->setData ( iCurInst ); - - pInstrPictPopupMenu->addAction ( pCurAction ); + case ST_NO_SORT: NoSortAction->setChecked ( true ); break; + case ST_BY_NAME: ByNameAction->setChecked ( true ); break; + case ST_BY_INSTRUMENT: ByInstrAction->setChecked ( true ); break; + case ST_BY_GROUPID: ByGroupAction->setChecked ( true ); break; + case ST_BY_CITY: ByCityAction->setChecked ( true ); break; } + MainMixerBoard->SetFaderSorting ( pSettings->eChannelSortType ); + pEditMenu->addSeparator(); - // Country flag icons popup menu ------------------------------------------- - pCountryFlagPopupMenu = new QMenu ( this ); + QAction* NumRowsAction = pEditMenu->addAction ( tr ( "Use &Two Rows Mixer Panel" ), this, + SLOT ( OnUseTowRowsForMixerPanel ( bool ) ) ); - // add an entry for all known country flags - for ( int iCurCntry = static_cast ( QLocale::AnyCountry ); - iCurCntry < static_cast ( QLocale::LastCountry ); iCurCntry++ ) - { - // the "Default" country gets a special icon - QIcon CurFlagIcon; - QString sCurCountryName; + // initialize the "use two rows for mixer panel" menu entry (is checkable) + NumRowsAction->setCheckable ( true ); + NumRowsAction->setChecked ( pSettings->iNumMixerPanelRows > 1 ); + MainMixerBoard->SetNumMixerPanelRows ( pSettings->iNumMixerPanelRows ); - if ( static_cast ( iCurCntry ) == QLocale::AnyCountry ) - { - // default icon and name for no flag selected - CurFlagIcon.addFile ( ":/png/flags/res/flags/flagnone.png" ); - sCurCountryName = tr ( "None" ); - } - else - { - // get current country enum - QLocale::Country eCountry = - static_cast ( iCurCntry ); + pEditMenu->addAction ( tr ( "&Clear All Stored Solo and Mute Settings" ), this, + SLOT ( OnClearAllStoredSoloMuteSettings() ) ); - // get resource file name - CurFlagIcon.addFile ( CLocale::GetCountryFlagIconsResourceReference ( eCountry ) ); + pEditMenu->addAction ( tr ( "Set All Faders to New Client &Level" ), this, + SLOT ( OnSetAllFadersToNewClientLevel() ), QKeySequence ( Qt::CTRL + Qt::Key_L ) ); - // get the country name - sCurCountryName = QLocale::countryToString ( eCountry ); - } - // only add the entry if a flag is available - if ( !CurFlagIcon.isNull() ) - { - // create a menu action with text and image - QAction* pCurAction = - new QAction ( CurFlagIcon, sCurCountryName, this ); + // Main menu bar ----------------------------------------------------------- + QMenuBar* pMenu = new QMenuBar ( this ); - // add data to identify the action data when it is triggered - pCurAction->setData ( iCurCntry ); + pMenu->addMenu ( pFileMenu ); + pMenu->addMenu ( pViewMenu ); + pMenu->addMenu ( pEditMenu ); + pMenu->addMenu ( new CHelpMenu ( true, this ) ); - pCountryFlagPopupMenu->addAction ( pCurAction ); - } - } + // Now tell the layout about the menu + layout()->setMenuBar ( pMenu ); // Window positions -------------------------------------------------------- // main window - if ( !pClient->vecWindowPosMain.isEmpty() && !pClient->vecWindowPosMain.isNull() ) + if ( !pSettings->vecWindowPosMain.isEmpty() && !pSettings->vecWindowPosMain.isNull() ) { - restoreGeometry ( pClient->vecWindowPosMain ); + restoreGeometry ( pSettings->vecWindowPosMain ); } // settings window - if ( !pClient->vecWindowPosSettings.isEmpty() && !pClient->vecWindowPosSettings.isNull() ) + if ( !pSettings->vecWindowPosSettings.isEmpty() && !pSettings->vecWindowPosSettings.isNull() ) { - ClientSettingsDlg.restoreGeometry ( pClient->vecWindowPosSettings ); + ClientSettingsDlg.restoreGeometry ( pSettings->vecWindowPosSettings ); } - if ( pClient->bWindowWasShownSettings ) + if ( pSettings->bWindowWasShownSettings ) { ShowGeneralSettings(); } // chat window - if ( !pClient->vecWindowPosChat.isEmpty() && !pClient->vecWindowPosChat.isNull() ) + if ( !pSettings->vecWindowPosChat.isEmpty() && !pSettings->vecWindowPosChat.isNull() ) { - ChatDlg.restoreGeometry ( pClient->vecWindowPosChat ); + ChatDlg.restoreGeometry ( pSettings->vecWindowPosChat ); } - if ( pClient->bWindowWasShownChat ) + if ( pSettings->bWindowWasShownChat ) { ShowChatWindow(); } // musician profile window - if ( !pClient->vecWindowPosProfile.isEmpty() && !pClient->vecWindowPosProfile.isNull() ) + if ( !pSettings->vecWindowPosProfile.isEmpty() && !pSettings->vecWindowPosProfile.isNull() ) { - MusicianProfileDlg.restoreGeometry ( pClient->vecWindowPosProfile ); + MusicianProfileDlg.restoreGeometry ( pSettings->vecWindowPosProfile ); } - if ( pClient->bWindowWasShownProfile ) + if ( pSettings->bWindowWasShownProfile ) { ShowMusicianProfileDialog(); } // connection setup window - if ( !pClient->vecWindowPosConnect.isEmpty() && !pClient->vecWindowPosConnect.isNull() ) + if ( !pSettings->vecWindowPosConnect.isEmpty() && !pSettings->vecWindowPosConnect.isNull() ) { - ConnectDlg.restoreGeometry ( pClient->vecWindowPosConnect ); + ConnectDlg.restoreGeometry ( pSettings->vecWindowPosConnect ); } // Connections ------------------------------------------------------------- // push buttons - QObject::connect ( butConnect, SIGNAL ( clicked() ), - this, SLOT ( OnConnectDisconBut() ) ); + QObject::connect ( butConnect, &QPushButton::clicked, + this, &CClientDlg::OnConnectDisconBut ); // check boxes - QObject::connect ( chbSettings, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnSettingsStateChanged ( int ) ) ); + QObject::connect ( chbSettings, &QCheckBox::stateChanged, + this, &CClientDlg::OnSettingsStateChanged ); - QObject::connect ( chbChat, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnChatStateChanged ( int ) ) ); + QObject::connect ( chbChat, &QCheckBox::stateChanged, + this, &CClientDlg::OnChatStateChanged ); - QObject::connect ( chbLocalMute, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnLocalMuteStateChanged ( int ) ) ); + QObject::connect ( chbLocalMute, &QCheckBox::stateChanged, + this, &CClientDlg::OnLocalMuteStateChanged ); // timers - QObject::connect ( &TimerSigMet, SIGNAL ( timeout() ), - this, SLOT ( OnTimerSigMet() ) ); + QObject::connect ( &TimerSigMet, &QTimer::timeout, + this, &CClientDlg::OnTimerSigMet ); + + QObject::connect ( &TimerBuffersLED, &QTimer::timeout, + this, &CClientDlg::OnTimerBuffersLED ); - QObject::connect ( &TimerBuffersLED, SIGNAL ( timeout() ), - this, SLOT ( OnTimerBuffersLED() ) ); + QObject::connect ( &TimerStatus, &QTimer::timeout, + this, &CClientDlg::OnTimerStatus ); - QObject::connect ( &TimerStatus, SIGNAL ( timeout() ), - this, SLOT ( OnTimerStatus() ) ); + QObject::connect ( &TimerPing, &QTimer::timeout, + this, &CClientDlg::OnTimerPing ); - QObject::connect ( &TimerPing, SIGNAL ( timeout() ), - this, SLOT ( OnTimerPing() ) ); + QObject::connect ( &TimerCheckAudioDeviceOk, &QTimer::timeout, + this, &CClientDlg::OnTimerCheckAudioDeviceOk ); // sliders - QObject::connect ( sldAudioPan, SIGNAL ( valueChanged ( int ) ), - this, SLOT ( OnAudioPanValueChanged ( int ) ) ); + QObject::connect ( sldAudioPan, &QSlider::valueChanged, + this, &CClientDlg::OnAudioPanValueChanged ); - QObject::connect ( sldAudioReverb, SIGNAL ( valueChanged ( int ) ), - this, SLOT ( OnAudioReverbValueChanged ( int ) ) ); + QObject::connect ( sldAudioReverb, &QSlider::valueChanged, + this, &CClientDlg::OnAudioReverbValueChanged ); // radio buttons - QObject::connect ( rbtReverbSelL, SIGNAL ( clicked() ), - this, SLOT ( OnReverbSelLClicked() ) ); + QObject::connect ( rbtReverbSelL, &QRadioButton::clicked, + this, &CClientDlg::OnReverbSelLClicked ); - QObject::connect ( rbtReverbSelR, SIGNAL ( clicked() ), - this, SLOT ( OnReverbSelRClicked() ) ); + QObject::connect ( rbtReverbSelR, &QRadioButton::clicked, + this, &CClientDlg::OnReverbSelRClicked ); // other - QObject::connect ( pClient, - SIGNAL ( ConClientListMesReceived ( CVector ) ), - this, SLOT ( OnConClientListMesReceived ( CVector ) ) ); + QObject::connect ( pClient, &CClient::ConClientListMesReceived, + this, &CClientDlg::OnConClientListMesReceived ); - QObject::connect ( pClient, - SIGNAL ( Disconnected() ), - this, SLOT ( OnDisconnected() ) ); + QObject::connect ( pClient, &CClient::Disconnected, + this, &CClientDlg::OnDisconnected ); - QObject::connect ( pClient, - SIGNAL ( CentralServerAddressTypeChanged() ), - this, SLOT ( OnCentralServerAddressTypeChanged() ) ); + QObject::connect ( pClient, &CClient::ChatTextReceived, + this, &CClientDlg::OnChatTextReceived ); - QObject::connect ( pClient, - SIGNAL ( ChatTextReceived ( QString ) ), - this, SLOT ( OnChatTextReceived ( QString ) ) ); + QObject::connect ( pClient, &CClient::ClientIDReceived, + this, &CClientDlg::OnClientIDReceived ); - QObject::connect ( pClient, - SIGNAL ( ClientIDReceived ( int ) ), - this, SLOT ( OnClientIDReceived ( int ) ) ); + QObject::connect ( pClient, &CClient::MuteStateHasChangedReceived, + this, &CClientDlg::OnMuteStateHasChangedReceived ); - QObject::connect ( pClient, - SIGNAL ( MuteStateHasChangedReceived ( int, bool ) ), - this, SLOT ( OnMuteStateHasChangedReceived ( int, bool ) ) ); + QObject::connect ( pClient, &CClient::RecorderStateReceived, + this, &CClientDlg::OnRecorderStateReceived ); // This connection is a special case. On receiving a licence required message via the // protocol, a modal licence dialog is opened. Since this blocks the thread, we need // a queued connection to make sure the core protocol mechanism is not blocked, too. qRegisterMetaType ( "ELicenceType" ); - QObject::connect ( pClient, - SIGNAL ( LicenceRequired ( ELicenceType ) ), - this, SLOT ( OnLicenceRequired ( ELicenceType ) ), Qt::QueuedConnection ); - - QObject::connect ( pClient, - SIGNAL ( PingTimeReceived ( int ) ), - this, SLOT ( OnPingTimeResult ( int ) ) ); - - QObject::connect ( pClient, - SIGNAL ( CLServerListReceived ( CHostAddress, CVector ) ), - this, SLOT ( OnCLServerListReceived ( CHostAddress, CVector ) ) ); - - QObject::connect ( pClient, - SIGNAL ( CLConnClientsListMesReceived ( CHostAddress, CVector ) ), - this, SLOT ( OnCLConnClientsListMesReceived ( CHostAddress, CVector ) ) ); - - QObject::connect ( pClient, - SIGNAL ( CLPingTimeWithNumClientsReceived ( CHostAddress, int, int ) ), - this, SLOT ( OnCLPingTimeWithNumClientsReceived ( CHostAddress, int, int ) ) ); - - QObject::connect ( pClient, - SIGNAL ( ControllerInFaderLevel ( int, int ) ), - this, SLOT ( OnControllerInFaderLevel ( int, int ) ) ); - - QObject::connect ( pClient, - SIGNAL ( CLChannelLevelListReceived ( CHostAddress, CVector ) ), - this, SLOT ( OnCLChannelLevelListReceived ( CHostAddress, CVector ) ) ); - - QObject::connect ( pClient, - SIGNAL ( VersionAndOSReceived ( COSUtil::EOpSystemType, QString ) ), - this, SLOT ( OnVersionAndOSReceived ( COSUtil::EOpSystemType, QString ) ) ); - -#ifdef ENABLE_CLIENT_VERSION_AND_OS_DEBUGGING - QObject::connect ( pClient, - SIGNAL ( CLVersionAndOSReceived ( CHostAddress, COSUtil::EOpSystemType, QString ) ), - this, SLOT ( OnCLVersionAndOSReceived ( CHostAddress, COSUtil::EOpSystemType, QString ) ) ); -#endif + QObject::connect ( pClient, &CClient::LicenceRequired, + this, &CClientDlg::OnLicenceRequired, Qt::QueuedConnection ); + + QObject::connect ( pClient, &CClient::PingTimeReceived, + this, &CClientDlg::OnPingTimeResult ); + + QObject::connect ( pClient, &CClient::CLServerListReceived, + this, &CClientDlg::OnCLServerListReceived ); + + QObject::connect ( pClient, &CClient::CLRedServerListReceived, + this, &CClientDlg::OnCLRedServerListReceived ); + + QObject::connect ( pClient, &CClient::CLConnClientsListMesReceived, + this, &CClientDlg::OnCLConnClientsListMesReceived ); + + QObject::connect ( pClient, &CClient::CLPingTimeWithNumClientsReceived, + this, &CClientDlg::OnCLPingTimeWithNumClientsReceived ); + + QObject::connect ( pClient, &CClient::ControllerInFaderLevel, + this, &CClientDlg::OnControllerInFaderLevel ); + + QObject::connect ( pClient, &CClient::CLChannelLevelListReceived, + this, &CClientDlg::OnCLChannelLevelListReceived ); - QObject::connect ( QCoreApplication::instance(), SIGNAL ( aboutToQuit() ), - this, SLOT ( OnAboutToQuit() ) ); + QObject::connect ( pClient, &CClient::VersionAndOSReceived, + this, &CClientDlg::OnVersionAndOSReceived ); - QObject::connect ( &ClientSettingsDlg, SIGNAL ( GUIDesignChanged() ), - this, SLOT ( OnGUIDesignChanged() ) ); + QObject::connect ( pClient, &CClient::CLVersionAndOSReceived, + this, &CClientDlg::OnCLVersionAndOSReceived ); - QObject::connect ( &ClientSettingsDlg, SIGNAL ( DisplayChannelLevelsChanged() ), - this, SLOT ( OnDisplayChannelLevelsChanged() ) ); + QObject::connect ( pClient, &CClient::SoundDeviceChanged, + this, &CClientDlg::OnSoundDeviceChanged ); - QObject::connect ( &ClientSettingsDlg, SIGNAL ( AudioChannelsChanged() ), - this, SLOT ( OnAudioChannelsChanged() ) ); + QObject::connect ( &ClientSettingsDlg, &CClientSettingsDlg::GUIDesignChanged, + this, &CClientDlg::OnGUIDesignChanged ); - QObject::connect ( &ClientSettingsDlg, SIGNAL ( NewClientLevelChanged() ), - this, SLOT ( OnNewClientLevelChanged() ) ); + QObject::connect ( &ClientSettingsDlg, &CClientSettingsDlg::AudioChannelsChanged, + this, &CClientDlg::OnAudioChannelsChanged ); - QObject::connect ( MainMixerBoard, SIGNAL ( ChangeChanGain ( int, double, bool ) ), - this, SLOT ( OnChangeChanGain ( int, double, bool ) ) ); + QObject::connect ( &ClientSettingsDlg, &CClientSettingsDlg::CustomCentralServerAddrChanged, + &ConnectDlg, &CConnectDlg::OnCustomCentralServerAddrChanged ); - QObject::connect ( MainMixerBoard, SIGNAL ( ChangeChanPan ( int, double ) ), - this, SLOT ( OnChangeChanPan ( int, double ) ) ); + QObject::connect ( MainMixerBoard, &CAudioMixerBoard::ChangeChanGain, + this, &CClientDlg::OnChangeChanGain ); - QObject::connect ( MainMixerBoard, SIGNAL ( NumClientsChanged ( int ) ), - this, SLOT ( OnNumClientsChanged ( int ) ) ); + QObject::connect ( MainMixerBoard, &CAudioMixerBoard::ChangeChanPan, + this, &CClientDlg::OnChangeChanPan ); - QObject::connect ( &ChatDlg, SIGNAL ( NewLocalInputText ( QString ) ), - this, SLOT ( OnNewLocalInputText ( QString ) ) ); + QObject::connect ( MainMixerBoard, &CAudioMixerBoard::NumClientsChanged, + this, &CClientDlg::OnNumClientsChanged ); - QObject::connect ( &ConnectDlg, SIGNAL ( ReqServerListQuery ( CHostAddress ) ), - this, SLOT ( OnReqServerListQuery ( CHostAddress ) ) ); + QObject::connect ( &ChatDlg, &CChatDlg::NewLocalInputText, + this, &CClientDlg::OnNewLocalInputText ); + + QObject::connect ( &ConnectDlg, &CConnectDlg::ReqServerListQuery, + this, &CClientDlg::OnReqServerListQuery ); // note that this connection must be a queued connection, otherwise the server list ping // times are not accurate and the client list may not be retrieved for all servers listed // (it seems the sendto() function needs to be called from different threads to fire the // packet immediately and do not collect packets before transmitting) - QObject::connect ( &ConnectDlg, SIGNAL ( CreateCLServerListPingMes ( CHostAddress ) ), - this, SLOT ( OnCreateCLServerListPingMes ( CHostAddress ) ), Qt::QueuedConnection ); + QObject::connect ( &ConnectDlg, &CConnectDlg::CreateCLServerListPingMes, + this, &CClientDlg::OnCreateCLServerListPingMes, Qt::QueuedConnection ); - QObject::connect ( &ConnectDlg, SIGNAL ( CreateCLServerListReqVerAndOSMes ( CHostAddress ) ), - this, SLOT ( OnCreateCLServerListReqVerAndOSMes ( CHostAddress ) ) ); + QObject::connect ( &ConnectDlg, &CConnectDlg::CreateCLServerListReqVerAndOSMes, + this, &CClientDlg::OnCreateCLServerListReqVerAndOSMes ); - QObject::connect ( &ConnectDlg, SIGNAL ( CreateCLServerListReqConnClientsListMes ( CHostAddress ) ), - this, SLOT ( OnCreateCLServerListReqConnClientsListMes ( CHostAddress ) ) ); + QObject::connect ( &ConnectDlg, &CConnectDlg::CreateCLServerListReqConnClientsListMes, + this, &CClientDlg::OnCreateCLServerListReqConnClientsListMes ); - QObject::connect ( &ConnectDlg, SIGNAL ( accepted() ), - this, SLOT ( OnConnectDlgAccepted() ) ); + QObject::connect ( &ConnectDlg, &CConnectDlg::accepted, + this, &CClientDlg::OnConnectDlgAccepted ); // Initializations which have to be done after the signals are connected --- @@ -563,25 +543,42 @@ CClientDlg::CClientDlg ( CClient* pNCliP, TimerStatus.start ( LED_BAR_UPDATE_TIME_MS ); // restore connect dialog - if ( pClient->bWindowWasShownConnect ) + if ( pSettings->bWindowWasShownConnect ) { ShowConnectionSetupDialog(); } + + // mute stream on startup (must be done after the signal connections) + if ( bMuteStream ) + { + chbLocalMute->setCheckState ( Qt::Checked ); + } + + // query the central server version number needed for update check (note + // that the connection less message respond may not make it back but that + // is not critical since the next time Jamulus is started we have another + // chance and the update check is not time-critical at all) + CHostAddress CentServerHostAddress; + + if ( NetworkUtil().ParseNetworkAddress ( DEFAULT_SERVER_ADDRESS, CentServerHostAddress ) ) + { + pClient->CreateCLServerListReqVerAndOSMes ( CentServerHostAddress ); + } } void CClientDlg::closeEvent ( QCloseEvent* Event ) { // store window positions - pClient->vecWindowPosMain = saveGeometry(); - pClient->vecWindowPosSettings = ClientSettingsDlg.saveGeometry(); - pClient->vecWindowPosChat = ChatDlg.saveGeometry(); - pClient->vecWindowPosProfile = MusicianProfileDlg.saveGeometry(); - pClient->vecWindowPosConnect = ConnectDlg.saveGeometry(); + pSettings->vecWindowPosMain = saveGeometry(); + pSettings->vecWindowPosSettings = ClientSettingsDlg.saveGeometry(); + pSettings->vecWindowPosChat = ChatDlg.saveGeometry(); + pSettings->vecWindowPosProfile = MusicianProfileDlg.saveGeometry(); + pSettings->vecWindowPosConnect = ConnectDlg.saveGeometry(); - pClient->bWindowWasShownSettings = ClientSettingsDlg.isVisible(); - pClient->bWindowWasShownChat = ChatDlg.isVisible(); - pClient->bWindowWasShownProfile = MusicianProfileDlg.isVisible(); - pClient->bWindowWasShownConnect = ConnectDlg.isVisible(); + pSettings->bWindowWasShownSettings = ClientSettingsDlg.isVisible(); + pSettings->bWindowWasShownChat = ChatDlg.isVisible(); + pSettings->bWindowWasShownProfile = MusicianProfileDlg.isVisible(); + pSettings->bWindowWasShownConnect = ConnectDlg.isVisible(); // if settings/connect dialog or chat dialog is open, close it ClientSettingsDlg.close(); @@ -596,22 +593,46 @@ void CClientDlg::closeEvent ( QCloseEvent* Event ) pClient->Stop(); } - // store mixer fader settings (we have to hide all mixer faders first to - // initiate a storage of the current mixer fader levels in case we are - // just in a connected state) and other settings - MainMixerBoard->HideAll(); - pClient->vecStoredFaderTags = MainMixerBoard->vecStoredFaderTags; - pClient->vecStoredFaderLevels = MainMixerBoard->vecStoredFaderLevels; - pClient->vecStoredPanValues = MainMixerBoard->vecStoredPanValues; - pClient->vecStoredFaderIsSolo = MainMixerBoard->vecStoredFaderIsSolo; - pClient->vecStoredFaderIsMute = MainMixerBoard->vecStoredFaderIsMute; - pClient->iNewClientFaderLevel = MainMixerBoard->iNewClientFaderLevel; - pClient->bConnectDlgShowAllMusicians = ConnectDlg.GetShowAllMusicians(); + // make sure all current fader settings are applied to the settings struct + MainMixerBoard->StoreAllFaderSettings(); + + pSettings->bConnectDlgShowAllMusicians = ConnectDlg.GetShowAllMusicians(); + pSettings->eChannelSortType = MainMixerBoard->GetFaderSorting(); + pSettings->iNumMixerPanelRows = MainMixerBoard->GetNumMixerPanelRows(); // default implementation of this event handler routine Event->accept(); } +void CClientDlg::ManageDragNDrop ( QDropEvent* Event, + const bool bCheckAccept ) +{ + // we only want to use drag'n'drop with file URLs + QListIterator UrlIterator ( Event->mimeData()->urls() ); + + while ( UrlIterator.hasNext() ) + { + const QString strFileNameWithPath = UrlIterator.next().toLocalFile(); + + // check all given URLs and if any has the correct suffix + if ( !strFileNameWithPath.isEmpty() && ( QFileInfo ( strFileNameWithPath ).suffix() == MIX_SETTINGS_FILE_SUFFIX ) ) + { + if ( bCheckAccept ) + { + // only accept drops of supports file types + Event->acceptProposedAction(); + } + else + { + // load the first valid settings file and leave the loop + pSettings->LoadFaderSettings ( strFileNameWithPath ); + MainMixerBoard->LoadAllFaderSettings(); + break; + } + } + } +} + void CClientDlg::UpdateAudioFaderSlider() { // update slider and label of audio fader @@ -695,7 +716,7 @@ void CClientDlg::OnConnectDlgAccepted() { // store new address at the top of the list, if the list was already // full, the last element is thrown out - pClient->vstrIPAddress.StringFiFoWithCompare ( strSelectedAddress ); + pSettings->vstrIPAddress.StringFiFoWithCompare ( strSelectedAddress ); } // get name to be set in audio mixer group box title @@ -714,7 +735,7 @@ void CClientDlg::OnConnectDlgAccepted() // user strMixerBoardLabel = strSelectedAddress; - // special case: if the address is empty, we substitude the default + // special case: if the address is empty, we substitute the default // central server address so that a user which just pressed the connect // button without selecting an item in the table or manually entered an // address gets a successful connection @@ -753,15 +774,44 @@ void CClientDlg::OnConnectDisconBut() } } -void CClientDlg::OnCentralServerAddressTypeChanged() +void CClientDlg::OnClearAllStoredSoloMuteSettings() { - // if the server list is shown and the server type was changed, update the list - if ( ConnectDlg.isVisible() ) + // if we are in an active connection, we first have to store all fader settings in + // the settings struct, clear the solo and mute states and then apply the settings again + MainMixerBoard->StoreAllFaderSettings(); + pSettings->vecStoredFaderIsSolo.Reset ( false ); + pSettings->vecStoredFaderIsMute.Reset ( false ); + MainMixerBoard->LoadAllFaderSettings(); +} + +void CClientDlg::OnLoadChannelSetup() +{ + QString strFileName = QFileDialog::getOpenFileName ( this, + tr ( "Select Channel Setup File" ), + "", + QString ( "*." ) + MIX_SETTINGS_FILE_SUFFIX ); + + if ( !strFileName.isEmpty() ) { - ConnectDlg.SetCentralServerAddress ( NetworkUtil::GetCentralServerAddress ( pClient->GetCentralServerAddressType(), - pClient->GetServerListCentralServerAddress() ) ); + // first update the settings struct and then update the mixer panel + pSettings->LoadFaderSettings ( strFileName ); + MainMixerBoard->LoadAllFaderSettings(); + } +} + +void CClientDlg::OnSaveChannelSetup() +{ + QString strFileName = QFileDialog::getSaveFileName ( this, + tr ( "Select Channel Setup File" ), + "", + QString ( "*." ) + MIX_SETTINGS_FILE_SUFFIX ); - ConnectDlg.RequestServerList(); + if ( !strFileName.isEmpty() ) + { + // first store all current fader settings (in case we are in an active connection + // right now) and then save the information in the settings struct in the file + MainMixerBoard->StoreAllFaderSettings(); + pSettings->SaveFaderSettings ( strFileName ); } } @@ -777,13 +827,30 @@ void CClientDlg::OnVersionAndOSReceived ( COSUtil::EOpSystemType , #endif } +void CClientDlg::OnCLVersionAndOSReceived ( CHostAddress , + COSUtil::EOpSystemType , + QString strVersion ) +{ + // update check +#if ( QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) ) && !defined ( DISABLE_VERSION_CHECK ) + if ( QVersionNumber::compare ( QVersionNumber::fromString ( strVersion ), QVersionNumber::fromString ( VERSION ) ) > 0 ) + { + // show the label and hide it after one minute again + lblUpdateCheck->show(); + QTimer::singleShot ( 60000, [this]() { lblUpdateCheck->hide(); } ); + } +#endif +} + void CClientDlg::OnChatTextReceived ( QString strChatText ) { ChatDlg.AddChatText ( strChatText ); - // open window (note that we do not want to force the dialog to be upfront - // always when a new message arrives since this is annoying) - ShowChatWindow ( false ); + // Open chat dialog. If a server welcome message is received, we force + // the dialog to be upfront in case a licence text is shown. For all + // other new chat texts we do not want to force the dialog to be upfront + // always when a new message arrives since this is annoying. + ShowChatWindow ( ( strChatText.indexOf ( WELCOME_MESSAGE_PREFIX ) == 0 ) ); UpdateDisplay(); } @@ -816,6 +883,15 @@ void CClientDlg::OnLicenceRequired ( ELicenceType eLicenceType ) void CClientDlg::OnConClientListMesReceived ( CVector vecChanInfo ) { + // show channel numbers if --ctrlmidich is used (#241, #95) + if ( bMIDICtrlUsed ) + { + for ( int i = 0; i < vecChanInfo.Size(); i++ ) + { + vecChanInfo[i].strName.prepend ( QString().setNum ( vecChanInfo[i].iChanID ) + ":" ); + } + } + // update mixer board with the additional client infos MainMixerBoard->ApplyNewConClientList ( vecChanInfo ); } @@ -828,8 +904,9 @@ void CClientDlg::OnNumClientsChanged ( int iNewNumClients ) void CClientDlg::SetMyWindowTitle ( const int iNumClients ) { - // show number of connected clients in window title (and therefore also in - // the task bar of the OS) + // set the window title (and therefore also the task bar icon text of the OS) + // according to the following specification (#559): + // - users - Jamulus if ( iNumClients == 0 ) { // only application name @@ -837,15 +914,18 @@ void CClientDlg::SetMyWindowTitle ( const int iNumClients ) } else { + QString strWinTitle = MainMixerBoard->GetServerName(); + if ( iNumClients == 1 ) { - setWindowTitle ( QString ( pClient->strClientName ) + " (1 " + tr ( "user" ) + ")" ); + strWinTitle += " - 1 " + tr ( "user" ); } - else + else if ( iNumClients > 1 ) { - setWindowTitle ( QString ( pClient->strClientName ) + - QString ( " (%1 " + tr ( "users" ) + ")" ).arg ( iNumClients ) ); + strWinTitle += " - " + QString::number ( iNumClients ) + " " + tr ( "users" ); } + + setWindowTitle ( strWinTitle + " - " + pClient->strClientName ); } #if defined ( __APPLE__ ) || defined ( __MACOSX ) @@ -869,11 +949,6 @@ void CClientDlg::SetMyWindowTitle ( const int iNumClients ) void CClientDlg::ShowConnectionSetupDialog() { - // init the connect dialog - ConnectDlg.Init ( pClient->vstrIPAddress ); - ConnectDlg.SetCentralServerAddress ( NetworkUtil::GetCentralServerAddress ( pClient->GetCentralServerAddressType(), - pClient->GetServerListCentralServerAddress() ) ); - // show connect dialog bConnectDlgWasShown = true; ConnectDlg.show(); @@ -905,12 +980,12 @@ void CClientDlg::ShowGeneralSettings() void CClientDlg::ShowChatWindow ( const bool bForceRaise ) { - // open chat dialog if it is not visible - if ( bForceRaise || !ChatDlg.isVisible() ) - { - ChatDlg.show(); + ChatDlg.show(); + if ( bForceRaise ) + { // make sure dialog is upfront and has focus + ChatDlg.showNormal(); ChatDlg.raise(); ChatDlg.activateWindow(); } @@ -955,33 +1030,23 @@ void CClientDlg::OnChatStateChanged ( int value ) void CClientDlg::OnLocalMuteStateChanged ( int value ) { pClient->SetMuteOutStream ( value == Qt::Checked ); -} -void CClientDlg::OnTimerSigMet() -{ - // get current input levels - double dCurSigLeveldB_L = pClient->MicLeveldB_L(); - double dCurSigLeveldB_R = pClient->MicLeveldB_R(); - - // linear transformation of the input level range to the progress-bar range - dCurSigLeveldB_L -= LOW_BOUND_SIG_METER; - dCurSigLeveldB_L *= NUM_STEPS_LED_BAR / ( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ); - dCurSigLeveldB_R -= LOW_BOUND_SIG_METER; - dCurSigLeveldB_R *= NUM_STEPS_LED_BAR / ( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ); - - // lower bound the signal - if ( dCurSigLeveldB_L < 0 ) + // show/hide info label + if ( value == Qt::Checked ) { - dCurSigLeveldB_L = 0; + lblGlobalInfoLabel->show(); } - if ( dCurSigLeveldB_R < 0 ) + else { - dCurSigLeveldB_R = 0; + lblGlobalInfoLabel->hide(); } +} +void CClientDlg::OnTimerSigMet() +{ // show current level - lbrInputLevelL->setValue ( dCurSigLeveldB_L ); - lbrInputLevelR->setValue ( dCurSigLeveldB_R ); + lbrInputLevelL->SetValue ( pClient->GetLevelForMeterdBLeft() ); + lbrInputLevelR->SetValue ( pClient->GetLevelForMeterdBRight() ); } void CClientDlg::OnTimerBuffersLED() @@ -1047,6 +1112,43 @@ void CClientDlg::OnPingTimeResult ( int iPingTime ) ledDelay->SetLight ( eOverallDelayLEDColor ); } +void CClientDlg::OnTimerCheckAudioDeviceOk() +{ + // check if the audio device entered the audio callback after a pre-defined + // timeout to check if a valid device is selected and if we do not have + // fundamental settings errors (in which case the GUI would only show that + // it is trying to connect the server which does not help to solve the problem (#129)) + if ( !pClient->IsCallbackEntered() ) + { + QMessageBox::warning ( this, APP_NAME, tr ( "Your sound card is not working correctly. " + "Please open the settings dialog and check the device selection and the driver settings." ) ); + } +} + +void CClientDlg::OnSoundDeviceChanged ( QString strError ) +{ + if ( !strError.isEmpty() ) + { + // the sound device setup has a problem, disconnect any active connection + if ( pClient->IsRunning() ) + { + Disconnect(); + } + + // show the error message of the device setup + QMessageBox::critical ( this, APP_NAME, strError, tr ( "Ok" ), nullptr ); + } + + // if the check audio device timer is running, it must be restarted on a device change + if ( TimerCheckAudioDeviceOk.isActive() ) + { + TimerCheckAudioDeviceOk.start ( CHECK_AUDIO_DEV_OK_TIME_MS ); + } + + // update the settings dialog + ClientSettingsDlg.UpdateSoundDeviceChannelSelectionFrame(); +} + void CClientDlg::OnCLPingTimeWithNumClientsReceived ( CHostAddress InetAddr, int iPingTime, int iNumClients ) @@ -1073,7 +1175,7 @@ void CClientDlg::Connect ( const QString& strSelectedAddress, } } - catch ( CGenErr generr ) + catch ( const CGenErr& generr ) { // show error message and return the function QMessageBox::critical ( this, APP_NAME, generr.GetErrorText(), "Close", nullptr ); @@ -1087,9 +1189,10 @@ void CClientDlg::Connect ( const QString& strSelectedAddress, MainMixerBoard->SetServerName ( strMixerBoardLabel ); // start timer for level meter bar and ping time measurement - TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); - TimerBuffersLED.start ( BUFFER_LED_UPDATE_TIME_MS ); - TimerPing.start ( PING_UPDATE_TIME_MS ); + TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); + TimerBuffersLED.start ( BUFFER_LED_UPDATE_TIME_MS ); + TimerPing.start ( PING_UPDATE_TIME_MS ); + TimerCheckAudioDeviceOk.start ( CHECK_AUDIO_DEV_OK_TIME_MS ); // is single shot timer } } @@ -1112,12 +1215,13 @@ void CClientDlg::Disconnect() // stop timer for level meter bars and reset them TimerSigMet.stop(); - lbrInputLevelL->setValue ( 0 ); - lbrInputLevelR->setValue ( 0 ); + lbrInputLevelL->SetValue ( 0 ); + lbrInputLevelR->SetValue ( 0 ); // stop other timers TimerBuffersLED.stop(); TimerPing.stop(); + TimerCheckAudioDeviceOk.stop(); // TODO is this still required??? @@ -1206,12 +1310,14 @@ rbtReverbSelR->setStyleSheet ( "color: rgb(220, 220, 220);" "font: bold;" ); #endif - lbrInputLevelL->SetLevelMeterType ( CMultiColorLEDBar::MT_LED ); - lbrInputLevelR->SetLevelMeterType ( CMultiColorLEDBar::MT_LED ); + lbrInputLevelL->SetLevelMeterType ( CLevelMeter::MT_LED ); + lbrInputLevelR->SetLevelMeterType ( CLevelMeter::MT_LED ); + ledBuffers->SetType ( CMultiColorLED::MT_LED ); + ledDelay->SetType ( CMultiColorLED::MT_LED ); break; default: - // reset style sheet and set original paramters + // reset style sheet and set original parameters backgroundFrame->setStyleSheet ( "" ); #ifdef _WIN32 @@ -1220,8 +1326,10 @@ rbtReverbSelL->setStyleSheet ( "" ); rbtReverbSelR->setStyleSheet ( "" ); #endif - lbrInputLevelL->SetLevelMeterType ( CMultiColorLEDBar::MT_BAR ); - lbrInputLevelR->SetLevelMeterType ( CMultiColorLEDBar::MT_BAR ); + lbrInputLevelL->SetLevelMeterType ( CLevelMeter::MT_BAR ); + lbrInputLevelR->SetLevelMeterType ( CLevelMeter::MT_BAR ); + ledBuffers->SetType ( CMultiColorLED::MT_INDICATOR ); + ledDelay->SetType ( CMultiColorLED::MT_INDICATOR ); break; } diff --git a/src/clientdlg.h b/src/clientdlg.h index a54d22f5ed..b58c1e967e 100755 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -18,10 +18,12 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ +#pragma once + #include #include #include @@ -33,6 +35,8 @@ #include #include #include +#include +#include #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) # include #endif @@ -58,6 +62,7 @@ #define LEVELMETER_UPDATE_TIME_MS 100 // ms #define BUFFER_LED_UPDATE_TIME_MS 300 // ms #define LED_BAR_UPDATE_TIME_MS 1000 // ms +#define CHECK_AUDIO_DEV_OK_TIME_MS 5000 // ms // number of ping times > upper bound until error message is shown #define NUM_HIGH_PINGS_UNTIL_ERROR 5 @@ -69,13 +74,14 @@ class CClientDlg : public QDialog, private Ui_CClientDlgBase Q_OBJECT public: - CClientDlg ( CClient* pNCliP, - CSettings* pNSetP, - const QString& strConnOnStartupAddress, - const bool bNewShowComplRegConnList, - const bool bShowAnalyzerConsole, - QWidget* parent = nullptr, - Qt::WindowFlags f = nullptr ); + CClientDlg ( CClient* pNCliP, + CClientSettings* pNSetP, + const QString& strConnOnStartupAddress, + const QString& strMIDISetup, + const bool bNewShowComplRegConnList, + const bool bShowAnalyzerConsole, + const bool bMuteStream, + QWidget* parent = nullptr ); protected: void SetGUIDesign ( const EGUIDesign eNewDesign ); @@ -90,25 +96,26 @@ class CClientDlg : public QDialog, private Ui_CClientDlgBase void Connect ( const QString& strSelectedAddress, const QString& strMixerBoardLabel ); void Disconnect(); + void ManageDragNDrop ( QDropEvent* Event, + const bool bCheckAccept ); CClient* pClient; - CSettings* pSettings; + CClientSettings* pSettings; bool bConnected; bool bConnectDlgWasShown; + bool bMIDICtrlUsed; QTimer TimerSigMet; QTimer TimerBuffersLED; QTimer TimerStatus; QTimer TimerPing; + QTimer TimerCheckAudioDeviceOk; - virtual void closeEvent ( QCloseEvent* Event ); + virtual void closeEvent ( QCloseEvent* Event ); + virtual void dragEnterEvent ( QDragEnterEvent* Event ) { ManageDragNDrop ( Event, true ); } + virtual void dropEvent ( QDropEvent* Event ) { ManageDragNDrop ( Event, false ); } void UpdateDisplay(); - QMenu* pViewMenu; - QMenuBar* pMenu; - QMenu* pInstrPictPopupMenu; - QMenu* pCountryFlagPopupMenu; - CClientSettingsDlg ClientSettingsDlg; CChatDlg ChatDlg; CConnectDlg ConnectDlg; @@ -116,11 +123,10 @@ class CClientDlg : public QDialog, private Ui_CClientDlgBase CMusProfDlg MusicianProfileDlg; public slots: - void OnAboutToQuit() { pSettings->Save(); } - void OnConnectDisconBut(); void OnTimerSigMet(); void OnTimerBuffersLED(); + void OnTimerCheckAudioDeviceOk(); void OnTimerStatus() { UpdateDisplay(); } @@ -137,18 +143,25 @@ public slots: void OnVersionAndOSReceived ( COSUtil::EOpSystemType , QString strVersion ); -#ifdef ENABLE_CLIENT_VERSION_AND_OS_DEBUGGING - void OnCLVersionAndOSReceived ( CHostAddress InetAddr, - COSUtil::EOpSystemType eOSType, - QString strVersion ) - { ConnectDlg.SetVersionAndOSType ( InetAddr, eOSType, strVersion ); } -#endif + void OnCLVersionAndOSReceived ( CHostAddress , + COSUtil::EOpSystemType , + QString strVersion ); + void OnLoadChannelSetup(); + void OnSaveChannelSetup(); void OnOpenConnectionSetupDialog() { ShowConnectionSetupDialog(); } void OnOpenMusicianProfileDialog() { ShowMusicianProfileDialog(); } void OnOpenGeneralSettings() { ShowGeneralSettings(); } void OnOpenChatDialog() { ShowChatWindow(); } void OnOpenAnalyzerConsole() { ShowAnalyzerConsole(); } + void OnNoSortChannels() { MainMixerBoard->SetFaderSorting ( ST_NO_SORT ); } + void OnSortChannelsByName() { MainMixerBoard->SetFaderSorting ( ST_BY_NAME ); } + void OnSortChannelsByInstrument() { MainMixerBoard->SetFaderSorting ( ST_BY_INSTRUMENT ); } + void OnSortChannelsByGroupID() { MainMixerBoard->SetFaderSorting ( ST_BY_GROUPID ); } + void OnSortChannelsByCity() { MainMixerBoard->SetFaderSorting ( ST_BY_CITY ); } + void OnUseTowRowsForMixerPanel ( bool Checked ) { MainMixerBoard->SetNumMixerPanelRows ( Checked ? 2 : 1 ); } + void OnClearAllStoredSoloMuteSettings(); + void OnSetAllFadersToNewClientLevel() { MainMixerBoard->SetAllFaderLevelsToNewClientLevel(); } void OnSettingsStateChanged ( int value ); void OnChatStateChanged ( int value ); @@ -168,12 +181,13 @@ public slots: void OnConClientListMesReceived ( CVector vecChanInfo ); void OnChatTextReceived ( QString strChatText ); void OnLicenceRequired ( ELicenceType eLicenceType ); + void OnSoundDeviceChanged ( QString strError ); - void OnChangeChanGain ( int iId, double dGain, bool bIsMyOwnFader ) - { pClient->SetRemoteChanGain ( iId, dGain, bIsMyOwnFader ); } + void OnChangeChanGain ( int iId, float fGain, bool bIsMyOwnFader ) + { pClient->SetRemoteChanGain ( iId, fGain, bIsMyOwnFader ); } - void OnChangeChanPan ( int iId, double dPan ) - { pClient->SetRemoteChanPan ( iId, dPan ); } + void OnChangeChanPan ( int iId, float fPan ) + { pClient->SetRemoteChanPan ( iId, fPan ); } void OnNewLocalInputText ( QString strChatText ) { pClient->CreateChatTextMes ( strChatText ); } @@ -194,6 +208,10 @@ public slots: CVector vecServerInfo ) { ConnectDlg.SetServerList ( InetAddr, vecServerInfo ); } + void OnCLRedServerListReceived ( CHostAddress InetAddr, + CVector vecServerInfo ) + { ConnectDlg.SetServerList ( InetAddr, vecServerInfo, true ); } + void OnCLConnClientsListMesReceived ( CHostAddress InetAddr, CVector vecChanInfo ) { ConnectDlg.SetConnClientsList ( InetAddr, vecChanInfo ); } @@ -204,23 +222,19 @@ public slots: void OnMuteStateHasChangedReceived ( int iChanID, bool bIsMuted ) { MainMixerBoard->SetRemoteFaderIsMute ( iChanID, bIsMuted ); } - void OnCLChannelLevelListReceived ( CHostAddress /* unused */, + void OnCLChannelLevelListReceived ( CHostAddress /* unused */, CVector vecLevelList ) { MainMixerBoard->SetChannelLevels ( vecLevelList ); } void OnConnectDlgAccepted(); void OnDisconnected() { Disconnect(); } - void OnCentralServerAddressTypeChanged(); - - void OnGUIDesignChanged() - { SetGUIDesign ( pClient->GetGUIDesign() ); } + void OnGUIDesignChanged() { SetGUIDesign ( pClient->GetGUIDesign() ); } - void OnDisplayChannelLevelsChanged() - { MainMixerBoard->SetDisplayChannelLevels ( pClient->GetDisplayChannelLevels() ); } + void OnRecorderStateReceived ( ERecorderState eRecorderState ) + { MainMixerBoard->SetRecorderState ( eRecorderState ); } void OnAudioChannelsChanged() { UpdateRevSelection(); } void OnNumClientsChanged ( int iNewNumClients ); - void OnNewClientLevelChanged() { MainMixerBoard->iNewClientFaderLevel = pClient->iNewClientFaderLevel; } void accept() { close(); } // introduced by pljones diff --git a/src/clientdlgbase.ui b/src/clientdlgbase.ui index 6f30a5e8da..96694165aa 100755 --- a/src/clientdlgbase.ui +++ b/src/clientdlgbase.ui @@ -6,10 +6,13 @@ 0 0 - 345 - 363 + 425 + 490
+ + true + @@ -61,43 +64,36 @@ 3 - - - - - - 0 - 0 - - - - :/png/main/res/fronticon.png - - - Qt::AlignCenter - - - false - - - - - - - Qt::Horizontal - - - QSizePolicy::Minimum - - - - 40 - 20 - - - - - + + + Qt::Vertical + + + + 10 + 0 + + + + + + + + + 0 + 0 + + + + :/png/main/res/fronticon.png + + + Qt::AlignCenter + + + false + + @@ -240,7 +236,7 @@ - + 0 @@ -256,7 +252,7 @@ - + 0 @@ -325,23 +321,23 @@ - + - Settings + &Mute Myself - + - Chat + &Settings - + - Mute Myself + &Chat @@ -350,8 +346,11 @@ C&onnect + + false + - true + false @@ -548,14 +547,38 @@ - - - - 0 - 0 - - - + + + + + MUTED (Other people won't hear you) + + + Qt::AlignCenter + + + 6 + + + + + + + + 0 + 0 + + + + + + + + Update check + + + + @@ -574,17 +597,17 @@
audiomixerboard.h
- CMultiColorLEDBar + CLevelMeter QWidget -
multicolorledbar.h
+
levelmeter.h
1
butConnect + chbLocalMute chbSettings chbChat - chbLocalMute sldAudioPan sldAudioReverb rbtReverbSelL diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 3078704325..c697682227 100755 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -26,8 +26,12 @@ /* Implementation *************************************************************/ -CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, - Qt::WindowFlags f ) : QDialog ( parent, f ), pClient ( pNCliP ) +CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, + CClientSettings* pNSetP, + QWidget* parent ) : + QDialog ( parent, Qt::Window ), // use Qt::Window to get min/max window buttons + pClient ( pNCliP ), + pSettings ( pNSetP ) { setupUi ( this ); @@ -36,31 +40,30 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, // jitter buffer QString strJitterBufferSize = "" + tr ( "Jitter Buffer Size" ) + ": " + 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 " + "size of the buffer therefore influences the quality of " "the audio stream (how many dropouts occur) and the overall delay " "(the longer the buffer, the higher the delay)." ) + "
" + tr ( - "The jitter buffer size can be manually chosen for the local client " + "You can set the jitter buffer size manually for the local client " "and the remote server. For the local jitter buffer, dropouts in the " "audio stream are indicated by the light below the " "jitter buffer size faders. If the light turns to red, a buffer " - "overrun/underrun took place and the audio stream is interrupted." ) + "
" + tr ( + "overrun/underrun has taken place and the audio stream is interrupted." ) + "
" + tr ( "The jitter buffer setting is therefore a trade-off between audio " "quality and overall delay." ) + "
" + 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 " + "If the Auto setting 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 Auto check is enabled, the jitter buffer size faders are " + "Auto is enabled, the jitter buffer size faders are " "disabled (they cannot be moved with the mouse)." ); - QString strJitterBufferSizeTT = tr ( "If the auto setting of the " - "jitter buffer is enabled, the network buffers of the local client and " + QString strJitterBufferSizeTT = tr ( "If the Auto setting " + "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 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 " + "audio delay/latency it is recommended to disable the Auto setting " + "and to lower the jitter buffer size manually by " + "using the sliders until your personal acceptable amount " + "of dropouts is reached. The LED indicator will display the audio " "dropouts of the local jitter buffer with a red light." ) + TOOLTIP_COM_END_TEXT; @@ -133,28 +136,27 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, // sound card buffer delay QString strSndCrdBufDelay = "" + tr ( "Sound Card Buffer Delay" ) + ": " + - tr ( "The buffer delay setting is a fundamental setting of the " ) + - APP_NAME + tr ( " software. This setting has influence on many " + tr ( "The buffer delay setting is a fundamental setting of this " + "software. This setting has an influence on many " "connection properties." ) + "
" + tr ( "Three buffer sizes are supported" ) + ":
    " - "
  • " + tr ( "64 samples: This is the preferred setting since it provides the lowest " - "latency but does not work with all sound cards." ) + "
  • " - "
  • " + tr ( "128 samples: This setting should work for most available " - "sound cards." ) + "
  • " - "
  • " + tr ( "256 samples: This setting should only be used if only a very slow " - "computer or a slow internet connection is available." ) + "
  • " + "
  • " + tr ( "64 samples: The preferred setting. Provides the lowest latency " + "but does not work with all sound cards." ) + "
  • " + "
  • " + tr ( "128 samples: Should work for most available sound cards." ) + + "
  • " + "
  • " + tr ( "256 samples: Should only be used on very slow " + "computers or with a slow internet connection." ) + "
  • " "
" + tr ( "Some sound card drivers 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, " + "from within the application. " + "In this case the buffer delay setting is disabled and has to be " + "changed using 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." ) + "
" + 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 " + "unsupported buffer size is used by the driver. The application " + "will still work with this setting but with restricted " "performance." ) + "
" + tr ( "The actual buffer delay has influence on the connection status, the " "current upload rate and the overall delay. The lower the buffer size, " @@ -166,8 +168,8 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, QString strSndCrdBufDelayTT = tr ( "If the buffer delay settings are " "disabled, it is prohibited by the audio driver to modify this " - "setting from within the " ) + APP_NAME + - tr ( " software. On Windows, press the ASIO Setup button to open the " + "setting from within the software. " + "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." ) + TOOLTIP_COM_END_TEXT; @@ -185,34 +187,32 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, butDriverSetup->setToolTip ( strSndCrdBufDelayTT ); // fancy skin - chbGUIDesignFancy->setWhatsThis ( "" + tr ( "Fancy Skin" ) + ": " + tr ( - "If enabled, a fancy skin will be applied to the main window." ) ); + cbxSkin->setWhatsThis ( "" + tr ( "Skin" ) + ": " + tr ( + "Select the skin to be used for the main window." ) ); - chbGUIDesignFancy->setAccessibleName ( tr ( "Fancy skin check box" ) ); - - // display channel levels - chbDisplayChannelLevels->setWhatsThis ( "" + tr ( "Display Channel Levels" ) + ": " + - tr ( "If enabled, each client channel will display a pre-fader level bar." ) ); - - chbDisplayChannelLevels->setAccessibleName ( tr ( "Display channel levels check box" ) ); + cbxSkin->setAccessibleName ( tr ( "Skin combo box" ) ); // audio channels QString strAudioChannels = "" + tr ( "Audio Channels" ) + ": " + 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 mono-in/stereo-out mode " - "the audio signal which is sent to the server is mono but the " + "Selects the number of audio channels to be used for communication between " + "client and server. There are three modes available:" ) + + "
    " + "
  • " "" + tr ( "Mono" ) + " " + tr ( "and " ) + + "" + tr ( "Stereo" ) + ": " + tr ( "These modes use " + "one and two audio channels respectively." ) + "
  • " + "
  • " "" + tr ( "Mono in/Stereo-out" ) + ": " + tr ( + "The audio signal sent to the server is mono but the " "return signal is stereo. This is useful if the " "sound card has 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." ) + "
    " + 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." ) + "
    " + tr ( - "In 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." ); + "microphone on the other. In that case the two input signals " + "can be mixed to one mono channel but the server mix is heard in " + "stereo." ) + "
  • " + "
  • " + tr ( "Enabling " ) + "" + tr ( "Stereo" ) + " " + tr ( " mode " + "will increase your stream's data rate. Make sure your upload rate does not " + "exceed the available upload speed of your internet connection." ) + "
  • " + "
" + "
" + tr ( "In stereo streaming mode, no audio channel selection " + "for the reverb effect will be available on the main window " + "since the effect is applied to both channels in this case." ); lblAudioChannels->setWhatsThis ( strAudioChannels ); cbxAudioChannels->setWhatsThis ( strAudioChannels ); @@ -220,11 +220,9 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, // audio quality QString strAudioQuality = "" + tr ( "Audio Quality" ) + ": " + 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 " - "upload rate does not exceed the available bandwidth of your " - "internet connection." ); + "The higher the audio quality, the higher your audio stream's " + "data rate. Make sure your upload rate does not exceed the " + "available bandwidth of your internet connection."); lblAudioQuality->setWhatsThis ( strAudioQuality ); cbxAudioQuality->setWhatsThis ( strAudioQuality ); @@ -232,10 +230,10 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, // new client fader level QString strNewClientLevel = "" + tr ( "New Client Level" ) + ": " + - 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 " + tr ( "This setting defines the fader level of a newly " + "connected client in percent. If a new client connects " + "to the current server, they will get the specified initial " + "fader level if no other fader level from a previous connection " "of that client was already stored." ); lblNewClientLevel->setWhatsThis ( strNewClientLevel ); @@ -244,29 +242,27 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, // custom central server address QString strCentrServAddr = "" + tr ( "Custom Central Server Address" ) + ": " + - tr ( "The custom central server address is the IP address or URL of the central " - "server at which the server list of the connection dialog is managed. This " - "address is only used if the custom server list is selected in the connection " - "dialog." ); + tr ( "Leave this blank unless you need to enter the address of a central " + "server other than the default." ); lblCentralServerAddress->setWhatsThis ( strCentrServAddr ); - edtCentralServerAddress->setWhatsThis ( strCentrServAddr ); - edtCentralServerAddress->setAccessibleName ( tr ( "Central server address line edit" ) ); + cbxCentralServerAddress->setWhatsThis ( strCentrServAddr ); + cbxCentralServerAddress->setAccessibleName ( tr ( "Central server address combo box" ) ); // current connection status parameter QString strConnStats = "" + tr ( "Current Connection Status " - "Parameter" ) + ": " + tr ( "The ping time is the time required for the audio " + "Parameter" ) + ": " + tr ( "The Ping Time is the time required for the audio " "stream to travel from the client to the server and back again. 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 " + "delay is introduced by the network and should be about " + "20-30 ms. If this delay is higher than about 50 ms, your distance to " "the server is too large or your internet connection is not " "sufficient." ) + "
" + tr ( - "The overall delay is calculated from the current ping time and the " - "delay which is introduced by the current buffer settings." ) + "
" + 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 " - "your internet connection by, e.g., using speedtest.net)." ); + "Overall Delay is calculated from the current Ping Time and the " + "delay introduced by the current buffer settings." ) + "
" + tr ( + "Audio Upstream Rate depends on the current audio packet size and " + "compression setting. Make sure that the upstream rate is not " + "higher than your available internet upload speed (check this with a " + "service such as speedtest.net)." ); lblPingTime->setWhatsThis ( strConnStats ); lblPingTimeValue->setWhatsThis ( strConnStats ); @@ -291,6 +287,8 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, // init delay and other information controls ledNetw->Reset(); ledOverallDelay->Reset(); + ledNetw->SetType ( CMultiColorLED::MT_INDICATOR ); + ledOverallDelay->SetType ( CMultiColorLED::MT_INDICATOR ); lblPingTimeValue->setText ( "---" ); lblOverallDelayValue->setText ( "---" ); lblUpstreamValue->setText ( "---" ); @@ -303,49 +301,39 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, sldNetBufServer->setRange ( MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL ); UpdateJitterBufferFrame(); - // init combo box containing all available sound cards in the system - cbxSoundcard->clear(); - for ( int iSndDevIdx = 0; iSndDevIdx < pClient->GetSndCrdNumDev(); iSndDevIdx++ ) - { - cbxSoundcard->addItem ( pClient->GetSndCrdDeviceName ( iSndDevIdx ) ); - } - cbxSoundcard->setCurrentIndex ( pClient->GetSndCrdDev() ); - // init sound card channel selection frame - UpdateSoundChannelSelectionFrame(); + UpdateSoundDeviceChannelSelectionFrame(); - // fancy GUI design check box - if ( pClient->GetGUIDesign() == GD_STANDARD ) - { - chbGUIDesignFancy->setCheckState ( Qt::Unchecked ); - } - else - { - chbGUIDesignFancy->setCheckState ( Qt::Checked ); - } - - // Display Channel Levels check box - chbDisplayChannelLevels->setCheckState ( pClient->GetDisplayChannelLevels() ? Qt::Checked : Qt::Unchecked ); - - // "Audio Channels" combo box + // Audio Channels combo box cbxAudioChannels->clear(); 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 ( pClient->GetAudioChannels() ) ); - // "Audio Quality" combo box + // Audio Quality combo box cbxAudioQuality->clear(); cbxAudioQuality->addItem ( tr ( "Low" ) ); // AQ_LOW cbxAudioQuality->addItem ( tr ( "Normal" ) ); // AQ_NORMAL cbxAudioQuality->addItem ( tr ( "High" ) ); // AQ_HIGH cbxAudioQuality->setCurrentIndex ( static_cast ( pClient->GetAudioQuality() ) ); - // custom central server address - edtCentralServerAddress->setText ( pClient->GetServerListCentralServerAddress() ); + // GUI design (skin) combo box + cbxSkin->clear(); + cbxSkin->addItem ( tr ( "Normal" ) ); // GD_STANDARD + cbxSkin->addItem ( tr ( "Fancy" ) ); // GD_ORIGINAL + cbxSkin->addItem ( tr ( "Compact" ) ); // GD_SLIMFADER + cbxSkin->setCurrentIndex ( static_cast ( pClient->GetGUIDesign() ) ); + + // language combo box (corrects the setting if language not found) + cbxLanguage->Init ( pSettings->strLanguage ); + + // init custom central server address combo box (max MAX_NUM_SERVER_ADDR_ITEMS entries) + cbxCentralServerAddress->setMaxCount ( MAX_NUM_SERVER_ADDR_ITEMS ); + cbxCentralServerAddress->setInsertPolicy ( QComboBox::NoInsert ); // update new client fader level edit box - edtNewClientLevel->setText ( QString::number ( pClient->iNewClientFaderLevel ) ); + edtNewClientLevel->setText ( QString::number ( pSettings->iNewClientFaderLevel ) ); // update enable small network buffers check box chbEnableOPUS64->setCheckState ( pClient->GetEnableOPUS64() ? Qt::Checked : Qt::Unchecked ); @@ -371,66 +359,69 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, // Connections ------------------------------------------------------------- // timers - QObject::connect ( &TimerStatus, SIGNAL ( timeout() ), - this, SLOT ( OnTimerStatus() ) ); + QObject::connect ( &TimerStatus, &QTimer::timeout, + this, &CClientSettingsDlg::OnTimerStatus ); // slider controls - QObject::connect ( sldNetBuf, SIGNAL ( valueChanged ( int ) ), - this, SLOT ( OnNetBufValueChanged ( int ) ) ); + QObject::connect ( sldNetBuf, &QSlider::valueChanged, + this, &CClientSettingsDlg::OnNetBufValueChanged ); - QObject::connect ( sldNetBufServer, SIGNAL ( valueChanged ( int ) ), - this, SLOT ( OnNetBufServerValueChanged ( int ) ) ); + QObject::connect ( sldNetBufServer, &QSlider::valueChanged, + this, &CClientSettingsDlg::OnNetBufServerValueChanged ); // check boxes - QObject::connect ( chbGUIDesignFancy, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnGUIDesignFancyStateChanged ( int ) ) ); + QObject::connect ( chbAutoJitBuf, &QCheckBox::stateChanged, + this, &CClientSettingsDlg::OnAutoJitBufStateChanged ); - QObject::connect ( chbDisplayChannelLevels, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnDisplayChannelLevelsStateChanged ( int ) ) ); + QObject::connect ( chbEnableOPUS64, &QCheckBox::stateChanged, + this, &CClientSettingsDlg::OnEnableOPUS64StateChanged ); - QObject::connect ( chbAutoJitBuf, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnAutoJitBufStateChanged ( int ) ) ); + // line edits + QObject::connect ( edtNewClientLevel, &QLineEdit::editingFinished, + this, &CClientSettingsDlg::OnNewClientLevelEditingFinished ); - QObject::connect ( chbEnableOPUS64, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnEnableOPUS64StateChanged ( int ) ) ); + // combo boxes + QObject::connect ( cbxSoundcard, static_cast ( &QComboBox::activated ), + this, &CClientSettingsDlg::OnSoundcardActivated ); - // line edits - QObject::connect ( edtCentralServerAddress, SIGNAL ( editingFinished() ), - this, SLOT ( OnCentralServerAddressEditingFinished() ) ); + QObject::connect ( cbxLInChan, static_cast ( &QComboBox::activated ), + this, &CClientSettingsDlg::OnLInChanActivated ); - QObject::connect ( edtNewClientLevel, SIGNAL ( editingFinished() ), - this, SLOT ( OnNewClientLevelEditingFinished() ) ); + QObject::connect ( cbxRInChan, static_cast ( &QComboBox::activated ), + this, &CClientSettingsDlg::OnRInChanActivated ); - // combo boxes - QObject::connect ( cbxSoundcard, SIGNAL ( activated ( int ) ), - this, SLOT ( OnSoundcardActivated ( int ) ) ); + QObject::connect ( cbxLOutChan, static_cast ( &QComboBox::activated ), + this, &CClientSettingsDlg::OnLOutChanActivated ); + + QObject::connect ( cbxROutChan, static_cast ( &QComboBox::activated ), + this, &CClientSettingsDlg::OnROutChanActivated ); - QObject::connect ( cbxLInChan, SIGNAL ( activated ( int ) ), - this, SLOT ( OnLInChanActivated ( int ) ) ); + QObject::connect ( cbxAudioChannels, static_cast ( &QComboBox::activated ), + this, &CClientSettingsDlg::OnAudioChannelsActivated ); - QObject::connect ( cbxRInChan, SIGNAL ( activated ( int ) ), - this, SLOT ( OnRInChanActivated ( int ) ) ); + QObject::connect ( cbxAudioQuality, static_cast ( &QComboBox::activated ), + this, &CClientSettingsDlg::OnAudioQualityActivated ); - QObject::connect ( cbxLOutChan, SIGNAL ( activated ( int ) ), - this, SLOT ( OnLOutChanActivated ( int ) ) ); + QObject::connect ( cbxSkin, static_cast ( &QComboBox::activated ), + this, &CClientSettingsDlg::OnGUIDesignActivated ); - QObject::connect ( cbxROutChan, SIGNAL ( activated ( int ) ), - this, SLOT ( OnROutChanActivated ( int ) ) ); + QObject::connect ( cbxCentralServerAddress->lineEdit(), &QLineEdit::editingFinished, + this, &CClientSettingsDlg::OnCentralServerAddressEditingFinished ); - QObject::connect ( cbxAudioChannels, SIGNAL ( activated ( int ) ), - this, SLOT ( OnAudioChannelsActivated ( int ) ) ); + QObject::connect ( cbxCentralServerAddress, static_cast ( &QComboBox::activated ), + this, &CClientSettingsDlg::OnCentralServerAddressEditingFinished ); - QObject::connect ( cbxAudioQuality, SIGNAL ( activated ( int ) ), - this, SLOT ( OnAudioQualityActivated ( int ) ) ); + QObject::connect ( cbxLanguage, &CLanguageComboBox::LanguageChanged, + this, &CClientSettingsDlg::OnLanguageChanged ); // buttons - QObject::connect ( butDriverSetup, SIGNAL ( clicked() ), - this, SLOT ( OnDriverSetupClicked() ) ); + QObject::connect ( butDriverSetup, &QPushButton::clicked, + this, &CClientSettingsDlg::OnDriverSetupClicked ); // misc QObject::connect ( &SndCrdBufferDelayButtonGroup, - SIGNAL ( buttonClicked ( QAbstractButton* ) ), this, - SLOT ( OnSndCrdBufferDelayButtonGroupClicked ( QAbstractButton* ) ) ); + static_cast ( &QButtonGroup::buttonClicked ), + this, &CClientSettingsDlg::OnSndCrdBufferDelayButtonGroupClicked ); // Timers ------------------------------------------------------------------ @@ -438,6 +429,12 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, TimerStatus.start ( DISPLAY_UPDATE_TIME ); } +void CClientSettingsDlg::showEvent ( QShowEvent* ) +{ + UpdateDisplay(); + UpdateCustomCentralServerComboBox(); +} + void CClientSettingsDlg::UpdateJitterBufferFrame() { // update slider value and text @@ -514,8 +511,20 @@ void CClientSettingsDlg::UpdateSoundCardFrame() } } -void CClientSettingsDlg::UpdateSoundChannelSelectionFrame() +void CClientSettingsDlg::UpdateSoundDeviceChannelSelectionFrame() { + // update combo box containing all available sound cards in the system + QStringList slSndCrdDevNames = pClient->GetSndCrdDevNames(); + cbxSoundcard->clear(); + + foreach ( QString strDevName, slSndCrdDevNames ) + { + cbxSoundcard->addItem ( strDevName ); + } + + cbxSoundcard->setCurrentText ( pClient->GetSndCrdDev() ); + + // update input/output channel selection #if defined ( _WIN32 ) || defined ( __APPLE__ ) || defined ( __MACOSX ) int iSndChanIdx; @@ -587,45 +596,34 @@ void CClientSettingsDlg::OnNetBufServerValueChanged ( int value ) void CClientSettingsDlg::OnSoundcardActivated ( int iSndDevIdx ) { - const QString strError = pClient->SetSndCrdDev ( iSndDevIdx ); + pClient->SetSndCrdDev ( cbxSoundcard->itemText ( iSndDevIdx ) ); - if ( !strError.isEmpty() ) - { - QMessageBox::critical ( this, APP_NAME, - QString ( tr ( "The selected audio device could not be used " - "because of the following error: " ) ) + strError + - QString ( tr ( " The previous driver will be selected." ) ), - tr ( "Ok" ), nullptr ); - - // recover old selection - cbxSoundcard->setCurrentIndex ( pClient->GetSndCrdDev() ); - } - UpdateSoundChannelSelectionFrame(); + UpdateSoundDeviceChannelSelectionFrame(); UpdateDisplay(); } void CClientSettingsDlg::OnLInChanActivated ( int iChanIdx ) { pClient->SetSndCrdLeftInputChannel ( iChanIdx ); - UpdateSoundChannelSelectionFrame(); + UpdateSoundDeviceChannelSelectionFrame(); } void CClientSettingsDlg::OnRInChanActivated ( int iChanIdx ) { pClient->SetSndCrdRightInputChannel ( iChanIdx ); - UpdateSoundChannelSelectionFrame(); + UpdateSoundDeviceChannelSelectionFrame(); } void CClientSettingsDlg::OnLOutChanActivated ( int iChanIdx ) { pClient->SetSndCrdLeftOutputChannel ( iChanIdx ); - UpdateSoundChannelSelectionFrame(); + UpdateSoundDeviceChannelSelectionFrame(); } void CClientSettingsDlg::OnROutChanActivated ( int iChanIdx ) { pClient->SetSndCrdRightOutputChannel ( iChanIdx ); - UpdateSoundChannelSelectionFrame(); + UpdateSoundDeviceChannelSelectionFrame(); } void CClientSettingsDlg::OnAudioChannelsActivated ( int iChanIdx ) @@ -641,6 +639,13 @@ void CClientSettingsDlg::OnAudioQualityActivated ( int iQualityIdx ) UpdateDisplay(); // upload rate will be changed } +void CClientSettingsDlg::OnGUIDesignActivated ( int iDesignIdx ) +{ + pClient->SetGUIDesign ( static_cast ( iDesignIdx ) ); + emit GUIDesignChanged(); + UpdateDisplay(); +} + void CClientSettingsDlg::OnAutoJitBufStateChanged ( int value ) { pClient->SetDoAutoSockBufSize ( value == Qt::Checked ); @@ -653,42 +658,24 @@ void CClientSettingsDlg::OnEnableOPUS64StateChanged ( int value ) UpdateDisplay(); } -void CClientSettingsDlg::OnGUIDesignFancyStateChanged ( int value ) +void CClientSettingsDlg::OnCentralServerAddressEditingFinished() { - if ( value == Qt::Unchecked ) + // if the user has selected and deleted an entry in the combo box list, + // we delete the corresponding entry in the central server address vector + if ( cbxCentralServerAddress->currentText().isEmpty() && cbxCentralServerAddress->currentData().isValid() ) { - pClient->SetGUIDesign ( GD_STANDARD ); + pSettings->vstrCentralServerAddress[cbxCentralServerAddress->currentData().toInt()] = ""; } else { - pClient->SetGUIDesign ( GD_ORIGINAL ); + // store new address at the top of the list, if the list was already + // full, the last element is thrown out + pSettings->vstrCentralServerAddress.StringFiFoWithCompare ( NetworkUtil::FixAddress ( cbxCentralServerAddress->currentText() ) ); } - emit GUIDesignChanged(); - UpdateDisplay(); -} - -void CClientSettingsDlg::OnDisplayChannelLevelsStateChanged ( int value ) -{ - pClient->SetDisplayChannelLevels ( value != Qt::Unchecked ); - emit DisplayChannelLevelsChanged(); -} -void CClientSettingsDlg::OnCentralServerAddressEditingFinished() -{ - // store new setting in the client - pClient->SetServerListCentralServerAddress ( - edtCentralServerAddress->text() ); -} - -void CClientSettingsDlg::OnNewClientLevelEditingFinished() -{ - // store new setting in the client - pClient->iNewClientFaderLevel = - edtNewClientLevel->text().toInt(); - - // inform that the level has changed and the mixer board settings must - // be updated - emit NewClientLevelChanged(); + // update combo box list and inform connect dialog about the new address + UpdateCustomCentralServerComboBox(); + emit CustomCentralServerAddrChanged(); } void CClientSettingsDlg::OnSndCrdBufferDelayButtonGroupClicked ( QAbstractButton* button ) @@ -719,19 +706,21 @@ void CClientSettingsDlg::SetPingTimeResult ( const int i // a certain value if ( iPingTime > 500 ) { - const QString sErrorText = - ">500 ms"; - + const QString sErrorText = ">500 ms"; lblPingTimeValue->setText ( sErrorText ); lblOverallDelayValue->setText ( sErrorText ); } else { - lblPingTimeValue->setText ( QString().setNum ( iPingTime ) + " ms" ); - lblOverallDelayValue->setText ( - QString().setNum ( iOverallDelayMs ) + " ms" ); + lblPingTimeValue->setText ( QString().setNum ( iPingTime ) + " ms" ); + lblOverallDelayValue->setText ( QString().setNum ( iOverallDelayMs ) + " ms" ); } + // update upstream rate information label (note that we update this together + // with the ping time since the network packet sequence number feature might + // be enabled at any time which has influence on the upstream rate) + lblUpstreamValue->setText ( QString().setNum ( pClient->GetUploadRateKbps() ) + " kbps" ); + // set current LED status ledOverallDelay->SetLight ( eOverallDelayLEDColor ); } @@ -749,10 +738,19 @@ void CClientSettingsDlg::UpdateDisplay() lblOverallDelayValue->setText ( "---" ); lblUpstreamValue->setText ( "---" ); } - else +} + +void CClientSettingsDlg::UpdateCustomCentralServerComboBox() +{ + cbxCentralServerAddress->clear(); + cbxCentralServerAddress->clearEditText(); + + for ( int iLEIdx = 0; iLEIdx < MAX_NUM_SERVER_ADDR_ITEMS; iLEIdx++ ) { - // update upstream rate information label (only if client is running) - lblUpstreamValue->setText ( - QString().setNum ( pClient->GetUploadRateKbps() ) + " kbps" ); + if ( !pSettings->vstrCentralServerAddress[iLEIdx].isEmpty() ) + { + // store the index as user data to the combo box item, too + cbxCentralServerAddress->addItem ( pSettings->vstrCentralServerAddress[iLEIdx], iLEIdx ); + } } } diff --git a/src/clientsettingsdlg.h b/src/clientsettingsdlg.h index fd377492b5..78f17b80ed 100755 --- a/src/clientsettingsdlg.h +++ b/src/clientsettingsdlg.h @@ -18,10 +18,12 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ +#pragma once + #include #include #include @@ -34,8 +36,10 @@ #include #include #include +#include #include "global.h" #include "client.h" +#include "settings.h" #include "multicolorled.h" #include "ui_clientsettingsdlgbase.h" @@ -51,9 +55,9 @@ class CClientSettingsDlg : public QDialog, private Ui_CClientSettingsDlgBase Q_OBJECT public: - CClientSettingsDlg ( CClient* pNCliP, - QWidget* parent = nullptr, - Qt::WindowFlags f = nullptr ); + CClientSettingsDlg ( CClient* pNCliP, + CClientSettings* pNSetP, + QWidget* parent = nullptr ); void SetStatus ( const CMultiColorLED::ELightColor eStatus ) { ledNetw->SetLight ( eStatus ); } @@ -68,30 +72,30 @@ class CClientSettingsDlg : public QDialog, private Ui_CClientSettingsDlgBase const CMultiColorLED::ELightColor eOverallDelayLEDColor ); void UpdateDisplay(); + void UpdateSoundDeviceChannelSelectionFrame(); protected: void UpdateJitterBufferFrame(); void UpdateSoundCardFrame(); - void UpdateSoundChannelSelectionFrame(); - QString GenSndCrdBufferDelayString ( const int iFrameSize, + void UpdateCustomCentralServerComboBox(); + QString GenSndCrdBufferDelayString ( const int iFrameSize, const QString strAddText = "" ); - virtual void showEvent ( QShowEvent* ) { UpdateDisplay(); } + virtual void showEvent ( QShowEvent* ); - CClient* pClient; - QTimer TimerStatus; - QButtonGroup SndCrdBufferDelayButtonGroup; + CClient* pClient; + CClientSettings* pSettings; + QTimer TimerStatus; + QButtonGroup SndCrdBufferDelayButtonGroup; - public slots: +public slots: void OnTimerStatus() { UpdateDisplay(); } void OnNetBufValueChanged ( int value ); void OnNetBufServerValueChanged ( int value ); void OnAutoJitBufStateChanged ( int value ); - void OnGUIDesignFancyStateChanged ( int value ); - void OnDisplayChannelLevelsStateChanged ( int value ); void OnEnableOPUS64StateChanged ( int value ); void OnCentralServerAddressEditingFinished(); - void OnNewClientLevelEditingFinished(); + void OnNewClientLevelEditingFinished() { pSettings->iNewClientFaderLevel = edtNewClientLevel->text().toInt(); } void OnSndCrdBufferDelayButtonGroupClicked ( QAbstractButton* button ); void OnSoundcardActivated ( int iSndDevIdx ); void OnLInChanActivated ( int iChanIdx ); @@ -100,11 +104,12 @@ class CClientSettingsDlg : public QDialog, private Ui_CClientSettingsDlgBase void OnROutChanActivated ( int iChanIdx ); void OnAudioChannelsActivated ( int iChanIdx ); void OnAudioQualityActivated ( int iQualityIdx ); + void OnGUIDesignActivated ( int iDesignIdx ); void OnDriverSetupClicked(); + void OnLanguageChanged ( QString strLanguage ) { pSettings->strLanguage = strLanguage; } signals: void GUIDesignChanged(); - void DisplayChannelLevelsChanged(); void AudioChannelsChanged(); - void NewClientLevelChanged(); + void CustomCentralServerAddrChanged(); }; diff --git a/src/clientsettingsdlgbase.ui b/src/clientsettingsdlgbase.ui index 7f625fdea7..a6d81c8482 100755 --- a/src/clientsettingsdlgbase.ui +++ b/src/clientsettingsdlgbase.ui @@ -274,12 +274,6 @@ - - - 50 - 0 - - Local @@ -293,12 +287,6 @@ - - - 50 - 0 - - Server @@ -316,12 +304,6 @@ - - - 50 - 0 - - Size @@ -335,12 +317,6 @@ - - - 50 - 0 - - Size @@ -508,6 +484,20 @@ + + + + Skin + + + + + + + Language + + + @@ -532,28 +522,16 @@ + + + + + + - - - - - - Fancy Skin - - - - - - - Display Channel Levels - - - - - @@ -562,12 +540,9 @@ - - - - 0 - 0 - + + + true @@ -708,6 +683,11 @@ QWidget
multicolorled.h
+ + CLanguageComboBox + QComboBox +
util.h
+
cbxSoundcard @@ -726,9 +706,9 @@ cbxAudioChannels cbxAudioQuality edtNewClientLevel - chbGUIDesignFancy - chbDisplayChannelLevels - edtCentralServerAddress + cbxSkin + cbxLanguage + cbxCentralServerAddress diff --git a/src/connectdlg.cpp b/src/connectdlg.cpp index 9327538c40..b714f2f8cb 100755 --- a/src/connectdlg.cpp +++ b/src/connectdlg.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -26,20 +26,19 @@ /* Implementation *************************************************************/ -CConnectDlg::CConnectDlg ( CClient* pNCliP, - const bool bNewShowCompleteRegList, - QWidget* parent, - Qt::WindowFlags f ) - : QDialog ( parent, f ), - pClient ( pNCliP ), - strCentralServerAddress ( "" ), - strSelectedAddress ( "" ), - strSelectedServerName ( "" ), - bShowCompleteRegList ( bNewShowCompleteRegList ), - bServerListReceived ( false ), - bServerListItemWasChosen ( false ), - bListFilterWasActive ( false ), - bShowAllMusicians ( true ) +CConnectDlg::CConnectDlg ( CClientSettings* pNSetP, + const bool bNewShowCompleteRegList, + QWidget* parent ) + : QDialog ( parent, Qt::Dialog ), + pSettings ( pNSetP ), + strSelectedAddress ( "" ), + strSelectedServerName ( "" ), + bShowCompleteRegList ( bNewShowCompleteRegList ), + bServerListReceived ( false ), + bReducedServerListReceived ( false ), + bServerListItemWasChosen ( false ), + bListFilterWasActive ( false ), + bShowAllMusicians ( true ) { setupUi ( this ); @@ -47,27 +46,23 @@ CConnectDlg::CConnectDlg ( CClient* pNCliP, // Add help text to controls ----------------------------------------------- // server list lvwServers->setWhatsThis ( "" + tr ( "Server List" ) + ": " + 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." ) + "
" + 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." ) ); + "The Connection Setup window shows a list of available servers. " + "Server operators can optionally list their servers by music genre. " + "Use the List dropdown to select a genre, click on the server you want " + "to join and press the Connect button to connect to it. Alternatively, " + "double click on on the server name. Permanent servers (those that have " + "been listed for longer than 48 hours) are shown in bold." ) ); lvwServers->setAccessibleName ( tr ( "Server list view" ) ); // server address QString strServAddrH = "" + tr ( "Server Address" ) + ": " + 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 " + "If you know the IP address or URL of a server, you can connect to it " + "using the Server name/Address field. 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 ( DEFAULT_PORT_NUMBER ) + tr ( ". A list of " - "the most recent used server IP addresses or URLs is available for " - "selection." ); + QString().setNum ( DEFAULT_PORT_NUMBER ) + tr ( ". The field will " + "also show a list of the most recently used server addresses."); lblServerAddr->setWhatsThis ( strServAddrH ); cbxServerAddr->setWhatsThis ( strServAddrH ); @@ -147,6 +142,12 @@ CConnectDlg::CConnectDlg ( CClient* pNCliP, lvwServers->sortItems ( 0, Qt::AscendingOrder ); } + // set a placeholder text to explain how to filter occupied servers (#397) + edtFilter->setPlaceholderText ( tr ( "Type # for occupied servers" ) ); + + // setup timers + TimerInitialSort.setSingleShot ( true ); // only once after list request + #ifdef ANDROID // for the android version maximize the window setWindowState ( Qt::WindowMaximized ); @@ -155,49 +156,44 @@ CConnectDlg::CConnectDlg ( CClient* pNCliP, // Connections ------------------------------------------------------------- // list view - QObject::connect ( lvwServers, - SIGNAL ( itemSelectionChanged() ), - this, SLOT ( OnServerListItemSelectionChanged() ) ); - - QObject::connect ( lvwServers, - SIGNAL ( itemDoubleClicked ( QTreeWidgetItem*, int ) ), - this, SLOT ( OnServerListItemDoubleClicked ( QTreeWidgetItem*, int ) ) ); + QObject::connect ( lvwServers, &QTreeWidget::itemDoubleClicked, + this, &CConnectDlg::OnServerListItemDoubleClicked ); - QObject::connect ( lvwServers, // to get default return key behaviour working - SIGNAL ( activated ( QModelIndex ) ), - this, SLOT ( OnConnectClicked() ) ); + // to get default return key behaviour working + QObject::connect ( lvwServers, &QTreeWidget::activated, + this, &CConnectDlg::OnConnectClicked ); // line edit - QObject::connect ( edtFilter, SIGNAL ( textEdited ( const QString& ) ), - this, SLOT ( OnFilterTextEdited ( const QString& ) ) ); + QObject::connect ( edtFilter, &QLineEdit::textEdited, + this, &CConnectDlg::OnFilterTextEdited ); // combo boxes - QObject::connect ( cbxServerAddr, SIGNAL ( editTextChanged ( const QString& ) ), - this, SLOT ( OnServerAddrEditTextChanged ( const QString& ) ) ); + QObject::connect ( cbxServerAddr, &QComboBox::editTextChanged, + this, &CConnectDlg::OnServerAddrEditTextChanged ); - QObject::connect ( cbxCentServAddrType, SIGNAL ( activated ( int ) ), - this, SLOT ( OnCentServAddrTypeChanged ( int ) ) ); + QObject::connect ( cbxCentServAddrType, static_cast ( &QComboBox::activated ), + this, &CConnectDlg::OnCentServAddrTypeChanged ); // check boxes - QObject::connect ( chbExpandAll, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnExpandAllStateChanged ( int ) ) ); + QObject::connect ( chbExpandAll, &QCheckBox::stateChanged, + this, &CConnectDlg::OnExpandAllStateChanged ); // buttons - QObject::connect ( butCancel, SIGNAL ( clicked() ), - this, SLOT ( close() ) ); + QObject::connect ( butCancel, &QPushButton::clicked, + this, &CConnectDlg::close ); - QObject::connect ( butConnect, SIGNAL ( clicked() ), - this, SLOT ( OnConnectClicked() ) ); + QObject::connect ( butConnect, &QPushButton::clicked, + this, &CConnectDlg::OnConnectClicked ); // timers - QObject::connect ( &TimerPing, SIGNAL ( timeout() ), - this, SLOT ( OnTimerPing() ) ); + QObject::connect ( &TimerPing, &QTimer::timeout, + this, &CConnectDlg::OnTimerPing ); - QObject::connect ( &TimerReRequestServList, SIGNAL ( timeout() ), - this, SLOT ( OnTimerReRequestServList() ) ); + QObject::connect ( &TimerReRequestServList, &QTimer::timeout, + this, &CConnectDlg::OnTimerReRequestServList ); } -void CConnectDlg::Init ( const CVector& vstrIPAddresses ) +void CConnectDlg::showEvent ( QShowEvent* ) { // load stored IP addresses in combo box cbxServerAddr->clear(); @@ -205,15 +201,12 @@ void CConnectDlg::Init ( const CVector& vstrIPAddresses ) for ( int iLEIdx = 0; iLEIdx < MAX_NUM_SERVER_ADDR_ITEMS; iLEIdx++ ) { - if ( !vstrIPAddresses[iLEIdx].isEmpty() ) + if ( !pSettings->vstrIPAddress[iLEIdx].isEmpty() ) { - cbxServerAddr->addItem ( vstrIPAddresses[iLEIdx] ); + cbxServerAddr->addItem ( pSettings->vstrIPAddress[iLEIdx] ); } } -} -void CConnectDlg::showEvent ( QShowEvent* ) -{ // on opening the connect dialg, we always want to request a // new updated server list per definition RequestServerList(); @@ -222,9 +215,10 @@ void CConnectDlg::showEvent ( QShowEvent* ) void CConnectDlg::RequestServerList() { // reset flags - bServerListReceived = false; - bServerListItemWasChosen = false; - bListFilterWasActive = false; + bServerListReceived = false; + bReducedServerListReceived = false; + bServerListItemWasChosen = false; + bListFilterWasActive = false; // clear current address and name strSelectedAddress = ""; @@ -233,18 +227,17 @@ void CConnectDlg::RequestServerList() // clear server list view lvwServers->clear(); - // clear filter edit box - edtFilter->setText ( "" ); - // update list combo box (disable events to avoid a signal) cbxCentServAddrType->blockSignals ( true ); - cbxCentServAddrType->setCurrentIndex ( static_cast ( pClient->GetCentralServerAddressType() ) ); + cbxCentServAddrType->setCurrentIndex ( static_cast ( pSettings->eCentralServerAddressType ) ); cbxCentServAddrType->blockSignals ( false ); - // get the IP address of the central server (using the ParseNetworAddress + // Get the IP address of the central server (using the ParseNetworAddress // function) when the connect dialog is opened, this seems to be the correct - // time to do it - if ( NetworkUtil().ParseNetworkAddress ( strCentralServerAddress, + // time to do it. Note that in case of custom central server address we + // use the first entry in the vector per definition. + if ( NetworkUtil().ParseNetworkAddress ( NetworkUtil::GetCentralServerAddress ( pSettings->eCentralServerAddressType, + pSettings->vstrCentralServerAddress[0] ), CentralServerAddress ) ) { // send the request for the server list @@ -253,6 +246,7 @@ void CConnectDlg::RequestServerList() // start timer, if this message did not get any respond to retransmit // the server list request message TimerReRequestServList.start ( SERV_LIST_REQ_UPDATE_TIME_MS ); + TimerInitialSort.start ( SERV_LIST_REQ_UPDATE_TIME_MS ); // reuse the time value } } @@ -263,6 +257,13 @@ void CConnectDlg::hideEvent ( QHideEvent* ) TimerReRequestServList.stop(); } +void CConnectDlg::OnCentServAddrTypeChanged ( int iTypeIdx ) +{ + // store the new central server address type and request new list + pSettings->eCentralServerAddressType = static_cast ( iTypeIdx ); + RequestServerList(); +} + void CConnectDlg::OnTimerReRequestServList() { // if the server list is not yet received, retransmit the request for the @@ -276,11 +277,41 @@ void CConnectDlg::OnTimerReRequestServList() } void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, - const CVector& vecServerInfo ) + const CVector& vecServerInfo, + const bool bIsReducedServerList ) { - // set flag and disable timer for resend server list request - bServerListReceived = true; - TimerReRequestServList.stop(); + // If the normal list was received, we do not accept any further list + // updates (to avoid the reduced list overwrites the normal list (#657)). Also, + // we only accept a server list from the server address we have sent the + // request for this to (note that we cannot use the port number since the + // receive port and send port might be different at the central server). + if ( bServerListReceived || + ( InetAddr.InetAddr != CentralServerAddress.InetAddr ) ) + { + return; + } + + // special treatment if a reduced server list was received + if ( bIsReducedServerList ) + { + // make sure we only apply the reduced version list once + if ( bReducedServerListReceived ) + { + // do nothing + return; + } + else + { + bReducedServerListReceived = true; + } + } + else + { + // set flag and disable timer for resend server list request if full list + // was received (i.e. not the reduced list) + bServerListReceived = true; + TimerReRequestServList.stop(); + } // first clear list lvwServers->clear(); @@ -301,7 +332,7 @@ void CConnectDlg::SetServerList ( const CHostAddress& InetAddr, } else { - // substitude the receive host address for central server + // substitute the receive host address for central server CurHostAddress = InetAddr; } @@ -418,7 +449,7 @@ void CConnectDlg::SetConnClientsList ( const CHostAddress& InetAddr, if ( pCurListViewItem ) { - // first remove any existing childs + // first remove any existing children DeleteAllListViewItemChilds ( pCurListViewItem ); // get number of connected clients @@ -434,7 +465,7 @@ void CConnectDlg::SetConnClientsList ( const CHostAddress& InetAddr, pNewChildListViewItem->setFirstColumnSpanned ( true ); // set the clients name - QString sClientText = vecChanInfo[i].GenNameForDisplay(); + QString sClientText = vecChanInfo[i].strName; // set the icon: country flag has priority over instrument bool bCountryFlagIsUsed = false; @@ -451,13 +482,6 @@ void CConnectDlg::SetConnClientsList ( const CHostAddress& InetAddr, // set correct picture pNewChildListViewItem->setIcon ( 0, QIcon ( CountryFlagPixmap ) ); - // add the instrument information as text - if ( !CInstPictures::IsNotUsedInstrument ( vecChanInfo[i].iInstrument ) ) - { - sClientText.append ( " (" + - CInstPictures::GetName ( vecChanInfo[i].iInstrument ) + ")" ); - } - bCountryFlagIsUsed = true; } } @@ -477,35 +501,26 @@ void CConnectDlg::SetConnClientsList ( const CHostAddress& InetAddr, } } + // add the instrument information as text + if ( !CInstPictures::IsNotUsedInstrument ( vecChanInfo[i].iInstrument ) ) + { + sClientText.append ( " (" + + CInstPictures::GetName ( vecChanInfo[i].iInstrument ) + ")" ); + } + // apply the client text to the list view item pNewChildListViewItem->setText ( 0, sClientText ); // add the new child to the corresponding server item pCurListViewItem->addChild ( pNewChildListViewItem ); - // at least one server has childs now, show decoration to be able - // to show the childs + // at least one server has children now, show decoration to be able + // to show the children lvwServers->setRootIsDecorated ( true ); } - } -} - -void CConnectDlg::OnServerListItemSelectionChanged() -{ - // get current selected item (we are only interested in the first selcted - // item) - QList CurSelListItemList = lvwServers->selectedItems(); - // if an item is clicked/selected, copy the server name to the combo box - if ( CurSelListItemList.count() > 0 ) - { - // make sure no signals are send when we change the text - cbxServerAddr->blockSignals ( true ); - { - cbxServerAddr->setEditText ( - GetParentListViewItem ( CurSelListItemList[0] )->text ( 0 ) ); - } - cbxServerAddr->blockSignals ( false ); + // the clients list may have changed, update the filter selection + UpdateListFilter(); } } @@ -527,6 +542,15 @@ void CConnectDlg::OnServerAddrEditTextChanged ( const QString& ) lvwServers->clearSelection(); } +void CConnectDlg::OnCustomCentralServerAddrChanged() +{ + // only update list if the custom server list is selected + if ( pSettings->eCentralServerAddressType == AT_CUSTOM ) + { + RequestServerList(); + } +} + void CConnectDlg::ShowAllMusicians ( const bool bState ) { bShowAllMusicians = bState; @@ -563,25 +587,38 @@ void CConnectDlg::UpdateListFilter() QTreeWidgetItem* pCurListViewItem = lvwServers->topLevelItem ( iIdx ); bool bFilterFound = false; - // search server name - if ( pCurListViewItem->text ( 0 ).indexOf ( sFilterText, 0, Qt::CaseInsensitive ) >= 0 ) + // DEFINITION: if "#" is set at the beginning of the filter text, we show + // occupied servers (#397) + if ( ( sFilterText.indexOf ( "#" ) == 0 ) && ( sFilterText.length() == 1 ) ) { - bFilterFound = true; + // special case: filter for occupied servers + if ( pCurListViewItem->childCount() > 0 ) + { + bFilterFound = true; + } } - - // search location - if ( pCurListViewItem->text ( 3 ).indexOf ( sFilterText, 0, Qt::CaseInsensitive ) >= 0 ) + else { - bFilterFound = true; - } + // search server name + if ( pCurListViewItem->text ( 0 ).indexOf ( sFilterText, 0, Qt::CaseInsensitive ) >= 0 ) + { + bFilterFound = true; + } - // search childs - for ( int iCCnt = 0; iCCnt < pCurListViewItem->childCount(); iCCnt++ ) - { - if ( pCurListViewItem->child ( iCCnt )->text ( 0 ).indexOf ( sFilterText, 0, Qt::CaseInsensitive ) >= 0 ) + // search location + if ( pCurListViewItem->text ( 3 ).indexOf ( sFilterText, 0, Qt::CaseInsensitive ) >= 0 ) { bFilterFound = true; } + + // search children + for ( int iCCnt = 0; iCCnt < pCurListViewItem->childCount(); iCCnt++ ) + { + if ( pCurListViewItem->child ( iCCnt )->text ( 0 ).indexOf ( sFilterText, 0, Qt::CaseInsensitive ) >= 0 ) + { + bFilterFound = true; + } + } } // only update Hide state if ping time was received @@ -656,7 +693,7 @@ void CConnectDlg::OnConnectClicked() } else { - strSelectedAddress = cbxServerAddr->currentText(); + strSelectedAddress = NetworkUtil::FixAddress ( cbxServerAddr->currentText() ); } // tell the parent window that the connection shall be initiated @@ -679,16 +716,26 @@ void CConnectDlg::OnTimerPing() data ( 0, Qt::UserRole ).toString(), CurServerAddress ) ) { - // if address is valid, send ping or the version and OS request -#ifdef ENABLE_CLIENT_VERSION_AND_OS_DEBUGGING - emit CreateCLServerListReqVerAndOSMes ( CurServerAddress ); -#else - emit CreateCLServerListPingMes ( CurServerAddress ); -#endif + // if address is valid, send ping message using a new thread + QtConcurrent::run ( this, + &CConnectDlg::EmitCLServerListPingMes, + CurServerAddress ); } } } +void CConnectDlg::EmitCLServerListPingMes ( const CHostAddress& CurServerAddress ) +{ + // The ping time messages for all servers should not be sent all in a very + // short time since it showed that this leads to errors in the ping time + // measurement (#49). We therefore introduce a short delay for each server + // (since we are doing this in a separate thread for each server, we do not + // block the GUI). + QThread::msleep ( 11 ); + + emit CreateCLServerListPingMes ( CurServerAddress ); +} + void CConnectDlg::SetPingTimeAndNumClientsResult ( const CHostAddress& InetAddr, const int iPingTime, const int iNumClients ) @@ -754,20 +801,25 @@ void CConnectDlg::SetPingTimeAndNumClientsResult ( const CHostAddress& InetAddr, } else { + // prepend spaces so that we can sort correctly (fieldWidth of + // 4 is sufficient since the maximum width is ">500") (#201) pCurListViewItem-> - setText ( 1, QString().setNum ( iMinPingTime ) + " ms" ); + setText ( 1, QString ( "%1 ms" ).arg ( iMinPingTime, 4, 10, QLatin1Char ( ' ' ) ) ); } // update number of clients text - if ( iNumClients >= pCurListViewItem->text ( 5 ).toInt() ) + if ( pCurListViewItem->text ( 5 ).toInt() == 0 ) { - pCurListViewItem-> - setText ( 2, QString().setNum ( iNumClients ) + " (full)" ); + // special case: reduced server list + pCurListViewItem->setText ( 2, QString().setNum ( iNumClients ) ); + } + else if ( iNumClients >= pCurListViewItem->text ( 5 ).toInt() ) + { + pCurListViewItem->setText ( 2, QString().setNum ( iNumClients ) + " (full)" ); } else { - pCurListViewItem-> - setText ( 2, QString().setNum ( iNumClients ) ); + pCurListViewItem->setText ( 2, QString().setNum ( iNumClients ) + "/" + pCurListViewItem->text ( 5 ) ); } // check if the number of child list items matches the number of @@ -784,20 +836,24 @@ void CConnectDlg::SetPingTimeAndNumClientsResult ( const CHostAddress& InetAddr, } // Update sorting. Note that the sorting must be the last action for the - // current item since the topLevelItem ( iIdx ) is then no longer valid. - if ( bDoSorting && !bShowCompleteRegList ) // do not sort if "show all servers" + // current item since the topLevelItem(iIdx) is then no longer valid. + // To avoid that the list is sorted shortly before a double click (which + // could lead to connecting an incorrect server) the sorting is disabled + // as long as the mouse is over the list (but it is not disabled for the + // initial timer of about 2s, see TimerInitialSort) (#293). + if ( bDoSorting && !bShowCompleteRegList && (TimerInitialSort.isActive() || !lvwServers->underMouse()) ) // do not sort if "show all servers" { lvwServers->sortByColumn ( 4, Qt::AscendingOrder ); } } - // if no server item has childs, do not show decoration - bool bAnyListItemHasChilds = false; - const int iServerListLen = lvwServers->topLevelItemCount(); + // if no server item has children, do not show decoration + bool bAnyListItemHasChilds = false; + const int iServerListLen = lvwServers->topLevelItemCount(); for ( int iIdx = 0; iIdx < iServerListLen; iIdx++ ) { - // check if the current list item has childs + // check if the current list item has children if ( lvwServers->topLevelItem ( iIdx )->childCount() > 0 ) { bAnyListItemHasChilds = true; @@ -852,7 +908,7 @@ QTreeWidgetItem* CConnectDlg::GetParentListViewItem ( QTreeWidgetItem* pItem ) void CConnectDlg::DeleteAllListViewItemChilds ( QTreeWidgetItem* pItem ) { - // loop over all childs + // loop over all children while ( pItem->childCount() > 0 ) { // get the first child in the list @@ -865,28 +921,3 @@ void CConnectDlg::DeleteAllListViewItemChilds ( QTreeWidgetItem* pItem ) delete pCurChildItem; } } - -#ifdef ENABLE_CLIENT_VERSION_AND_OS_DEBUGGING -void CConnectDlg::SetVersionAndOSType ( CHostAddress InetAddr, - COSUtil::EOpSystemType eOSType, - QString strVersion ) -{ - // apply the received version and OS type to the correct server list entry - QTreeWidgetItem* pCurListViewItem = FindListViewItem ( InetAddr ); - - if ( pCurListViewItem ) - { -// TEST since this is just a debug info, we just reuse the ping column (note -// the we have to replace the ping message emit with the version and OS request -// so that this works, see above code) -pCurListViewItem-> - setText ( 1, strVersion + "/" + COSUtil::GetOperatingSystemString ( eOSType ) ); - -// a version and OS type was received, set item to visible -if ( pCurListViewItem->isHidden() ) -{ - pCurListViewItem->setHidden ( false ); -} - } -} -#endif diff --git a/src/connectdlg.h b/src/connectdlg.h index ae6442c8a8..5c15f3011a 100755 --- a/src/connectdlg.h +++ b/src/connectdlg.h @@ -18,19 +18,21 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ +#pragma once + #include #include #include #include #include -#include #include +#include #include "global.h" -#include "client.h" +#include "settings.h" #include "multicolorled.h" #include "ui_connectdlgbase.h" @@ -47,19 +49,16 @@ class CConnectDlg : public QDialog, private Ui_CConnectDlgBase Q_OBJECT public: - CConnectDlg ( CClient* pNCliP, - const bool bNewShowCompleteRegList, - QWidget* parent = nullptr, - Qt::WindowFlags f = nullptr ); - - void Init ( const CVector& vstrIPAddresses ); - void SetCentralServerAddress ( const QString strNewCentralServerAddr ) { strCentralServerAddress = strNewCentralServerAddr; } + CConnectDlg ( CClientSettings* pNSetP, + const bool bNewShowCompleteRegList, + QWidget* parent = nullptr ); void SetShowAllMusicians ( const bool bState ) { ShowAllMusicians ( bState ); } bool GetShowAllMusicians() { return bShowAllMusicians; } void SetServerList ( const CHostAddress& InetAddr, - const CVector& vecServerInfo ); + const CVector& vecServerInfo, + const bool bIsReducedServerList = false ); void SetConnClientsList ( const CHostAddress& InetAddr, const CVector& vecChanInfo ); @@ -68,16 +67,9 @@ class CConnectDlg : public QDialog, private Ui_CConnectDlgBase const int iPingTime, const int iNumClients ); -#ifdef ENABLE_CLIENT_VERSION_AND_OS_DEBUGGING - void SetVersionAndOSType ( CHostAddress InetAddr, - COSUtil::EOpSystemType eOSType, - QString strVersion ); -#endif - bool GetServerListItemWasChosen() const { return bServerListItemWasChosen; } QString GetSelectedAddress() const { return strSelectedAddress; } QString GetSelectedServerName() const { return strSelectedServerName; } - void RequestServerList(); protected: virtual void showEvent ( QShowEvent* ); @@ -88,28 +80,31 @@ class CConnectDlg : public QDialog, private Ui_CConnectDlgBase void DeleteAllListViewItemChilds ( QTreeWidgetItem* pItem ); void UpdateListFilter(); void ShowAllMusicians ( const bool bState ); - - CClient* pClient; - - QTimer TimerPing; - QTimer TimerReRequestServList; - QString strCentralServerAddress; - CHostAddress CentralServerAddress; - QString strSelectedAddress; - QString strSelectedServerName; - bool bShowCompleteRegList; - bool bServerListReceived; - bool bServerListItemWasChosen; - bool bListFilterWasActive; - bool bShowAllMusicians; + void RequestServerList(); + void EmitCLServerListPingMes ( const CHostAddress& CurServerAddress ); + + CClientSettings* pSettings; + + QTimer TimerPing; + QTimer TimerReRequestServList; + QTimer TimerInitialSort; + CHostAddress CentralServerAddress; + QString strSelectedAddress; + QString strSelectedServerName; + bool bShowCompleteRegList; + bool bServerListReceived; + bool bReducedServerListReceived; + bool bServerListItemWasChosen; + bool bListFilterWasActive; + bool bShowAllMusicians; public slots: - void OnServerListItemSelectionChanged(); void OnServerListItemDoubleClicked ( QTreeWidgetItem* Item, int ); void OnServerAddrEditTextChanged ( const QString& ); - void OnCentServAddrTypeChanged ( int iTypeIdx ) { pClient->SetCentralServerAddressType ( static_cast ( iTypeIdx ) ); } + void OnCentServAddrTypeChanged ( int iTypeIdx ); void OnFilterTextEdited ( const QString& ) { UpdateListFilter(); } void OnExpandAllStateChanged ( int value ) { ShowAllMusicians ( value == Qt::Checked ); } + void OnCustomCentralServerAddrChanged(); void OnConnectClicked(); void OnTimerPing(); void OnTimerReRequestServList(); diff --git a/src/connectdlgbase.ui b/src/connectdlgbase.ui index 5f0a820ac2..6b5c338a16 100755 --- a/src/connectdlgbase.ui +++ b/src/connectdlgbase.ui @@ -93,7 +93,7 @@ - Server Name/Address + Server Address Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter diff --git a/src/global.h b/src/global.h index 5e2aeafdaa..3a8b2d3e80 100755 --- a/src/global.h +++ b/src/global.h @@ -43,7 +43,7 @@ LED bar: lbr * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -72,11 +72,6 @@ LED bar: lbr //#define _DEBUG_ #undef _DEBUG_ -// define this macro if the version and operating system debugging shall -// be enabled in the client (the ping time column in the connect dialog then -// shows the requested information instead of the ping time) -#undef ENABLE_CLIENT_VERSION_AND_OS_DEBUGGING - // version and application name (use version from qt prject file) #undef VERSION #define VERSION APP_VERSION @@ -92,9 +87,6 @@ LED bar: lbr // file name for logging file #define DEFAULT_LOG_FILE_NAME "Jamulussrvlog.txt" -// default oldest item to draw in history graph (days ago) -#define DEFAULT_DAYS_HISTORY 60 - // System block size, this is the block size on which the audio coder works. // All other block sizes must be a multiple of this size. // Note that the UpdateAutoSetting() function assumes a value of 128. @@ -110,13 +102,13 @@ LED bar: lbr #define CENTSERV_GENRE_CLASSICAL_FOLK "jamulusclassical.fischvolk.de:22524" // getting started and software manual URL -#define CLIENT_GETTING_STARTED_URL "https://github.com/corrados/jamulus/wiki/Getting-Started" -#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" +#define CLIENT_GETTING_STARTED_URL "https://jamulus.io/wiki/Getting-Started" +#define SERVER_GETTING_STARTED_URL "https://jamulus.io/wiki/Running-a-Server" +#define SOFTWARE_MANUAL_URL "https://jamulus.io/wiki/Software-Manual" // determining server internal address uses well-known host and port -// (Google DNS, or something else reliable) -#define WELL_KNOWN_HOST "8.8.8.8" // Google +// (You can change the service used here to something like Cloudflare (1.1.1.1), Google DNS (8.8.8.8), or something else reliable) +#define WELL_KNOWN_HOST "1.1.1.1" // CloudFlare #define WELL_KNOWN_PORT 53 // DNS #define IP_LOOKUP_TIMEOUT 500 // ms @@ -152,6 +144,9 @@ LED bar: lbr #define AUD_MIX_FADER_MAX 100 #define AUD_MIX_PAN_MAX 100 +// maximum number of fader groups (must be consistent to audiomixerboard implementation) +#define MAX_NUM_FADER_GROUPS 4 + // maximum number of recognized sound cards installed in the system #define MAX_NUMBER_SOUND_CARDS 129 // e.g. 16 inputs, 8 outputs + default entry (MacOS) @@ -171,8 +166,13 @@ LED bar: lbr #define LOW_BOUND_SIG_METER ( -50.0 ) // dB #define UPPER_BOUND_SIG_METER ( 0.0 ) // dB +// defines for LED level meter CLevelMeter +#define NUM_STEPS_LED_BAR 8 +#define RED_BOUND_LED_BAR 7 +#define YELLOW_BOUND_LED_BAR 5 + // maximum number of connected clients at the server (must not be larger than 256) -#define MAX_NUM_CHANNELS 50 // max number channels for server +#define MAX_NUM_CHANNELS 150 // max number channels for server // actual number of used channels in the server // this parameter can safely be changed from 1 to MAX_NUM_CHANNELS @@ -206,7 +206,7 @@ LED bar: lbr #define SERVLIST_REGIST_INTERV_MINUTES 15 // minutes // defines the minimum time a server must run to be a permanent server -#define SERVLIST_TIME_PERMSERV_MINUTES 4320 // minutes, 4320 = 60 min * 24 h * 3 d +#define SERVLIST_TIME_PERMSERV_MINUTES 2880 // minutes, 2880 = 60 min * 24 h * 2 d // registration response timeout #define REGISTER_SERVER_TIME_OUT_MS 500 // ms @@ -220,22 +220,28 @@ LED bar: lbr // Maximum length of fader tag and text message strings (Since for chat messages // some HTML code is added, we also have to define a second length which includes // this additionl HTML code. Right now the length of the HTML code is approx. 66 -// character. Here, we add some headroom to this number) +// characters. Here, we add some headroom to this number) #define MAX_LEN_FADER_TAG 16 #define MAX_LEN_CHAT_TEXT 1600 #define MAX_LEN_CHAT_TEXT_PLUS_HTML 1800 #define MAX_LEN_SERVER_NAME 20 #define MAX_LEN_IP_ADDRESS 15 #define MAX_LEN_SERVER_CITY 20 -#define MAX_LEN_VERSION_TEXT 20 +#define MAX_LEN_VERSION_TEXT 30 // common tool tip bottom line text #define TOOLTIP_COM_END_TEXT \ "
" + \ - QCoreApplication::translate ( "global","For more information use the ""What's " \ + QCoreApplication::translate ( "global", "For more information use the ""What's " \ "This"" help (help menu, right mouse button or Shift+F1)" ) + \ "
" +// server welcome message title (do not change for compatibility!) +#define WELCOME_MESSAGE_PREFIX "Server Welcome Message: " + +// mixer settings file name suffix +#define MIX_SETTINGS_FILE_SUFFIX "jch" + #define _MAXSHORT 32767 #define _MINSHORT ( -32768 ) #define INVALID_INDEX -1 // define invalid index as a negative value (a valid index must always be >= 0) @@ -277,7 +283,7 @@ class CGenErr CGenErr ( QString strNewErrorMsg, QString strNewErrorType = "" ) : strErrorMsg ( strNewErrorMsg ), strErrorType ( strNewErrorType ) {} - QString GetErrorText() + QString GetErrorText() const { // return formatted error text if ( strErrorType.isEmpty() ) diff --git a/src/historygraph.cpp b/src/historygraph.cpp deleted file mode 100644 index 175e9de243..0000000000 --- a/src/historygraph.cpp +++ /dev/null @@ -1,511 +0,0 @@ -/******************************************************************************\ - * Copyright (c) 2004-2020 - * - * Author(s): - * Volker Fischer - * 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 - * -\******************************************************************************/ - -#include "historygraph.h" - - -/* Abstract class *************************************************************/ -AHistoryGraph::AHistoryGraph ( const int iMaxDaysHistory ) : - sFileName ( "" ), - bDoHistory ( false ), - vHistoryDataFifo ( NUM_ITEMS_HISTORY ), - iNumTicksX ( 0 ), // number of days in history - iHistMaxDays ( iMaxDaysHistory ), - - BackgroundColor ( "white" ), // background - FrameColor ( "black" ), // frame - GridColor ( "gray" ), // grid - TextColor ( "black" ), // text - MarkerNewColor ( "darkCyan" ), // marker for new connection - MarkerNewLocalColor ( "blue" ), // marker for new local connection - MarkerStopColor ( "red" ), // marker for server stop - - canvasRectX ( 0 ), - canvasRectY ( 0 ), - canvasRectWidth ( 640 ), - canvasRectHeight ( 450 ), - - iGridFrameOffset ( 10 ), - iGridWidthWeekend ( 3 ), // should be an odd value - iXAxisTextHeight ( 22 ), - gridFrameX ( canvasRectX + iGridFrameOffset ), - gridFrameY ( canvasRectY + iGridFrameOffset ), - gridFrameWidth ( canvasRectWidth - 2 * iGridFrameOffset ), - gridFrameHeight ( canvasRectHeight - 2 * iGridFrameOffset - iXAxisTextHeight ), - gridFrameRight ( gridFrameX + gridFrameWidth - 1 ), - gridFrameBottom ( gridFrameY + gridFrameHeight - 1 ), - - axisFontFamily ( "Arial" ), - axisFontWeight ( "100" ), - axisFontSize ( 12 ), - - iYAxisStart ( 0 ), - iYAxisEnd ( 24 ), - iNumTicksY ( 5 ), - - iTextOffsetToGrid ( 3 ), - iTextOffsetX ( 18 ), - - iMarkerSizeNewCon ( 10 ), - iMarkerSizeServSt ( 6 ) -{ -} - -void AHistoryGraph::Start ( const QString& sNewFileName ) -{ - QTextStream& tsConsoleStream = *( ( new ConsoleWriterFactory() )->get() ); - tsConsoleStream << QString ( "AHistoryGraph::Start ( %1 )" ).arg ( sNewFileName ) << endl; // on console - - if ( !sNewFileName.isEmpty() ) - { - // save file name - sFileName = sNewFileName; - - // set enable flag - bDoHistory = true; - - // enable timer (update once a day) - TimerDailyUpdate.start ( 3600000 * 24 ); - - // initial update (empty graph) - Update(); - } -} - -void AHistoryGraph::Add ( const QDateTime& newDateTime, const EHistoryItemType curType ) -{ - if ( bDoHistory ) - { - // create and add new element in FIFO - SHistoryData curHistoryData; - curHistoryData.DateTime = newDateTime; - curHistoryData.Type = curType; - - vHistoryDataFifo.Add ( curHistoryData ); - } -} - -void AHistoryGraph::Add ( const QDateTime& newDateTime, const QHostAddress ClientInetAddr ) -{ - if ( bDoHistory ) - { - // add element to history, distinguish between a local connection - // and a remote connection - if ( ( ClientInetAddr == QHostAddress ( "127.0.0.1" ) ) || - ( ClientInetAddr.toString().left ( 7 ).compare ( "192.168" ) == 0 ) ) - { - // local connection - Add ( newDateTime, HIT_LOCAL_CONNECTION ); - } - else - { - // remote connection - Add ( newDateTime, HIT_REMOTE_CONNECTION ); - } - } -} - -void AHistoryGraph::Update ( ) -{ - if ( bDoHistory ) - { - int i; - - // store current date for reference - curDate = QDate::currentDate(); - - // set oldest date to draw - QDate minDate = curDate.addDays ( iHistMaxDays * -1 ); - - // get oldest date in history - QDate oldestDate = curDate.addDays ( 1 ); // one day in the future - const int iNumItemsForHistory = vHistoryDataFifo.Size(); - - for ( i = 0; i < iNumItemsForHistory; i++ ) - { - // only use valid dates - if ( vHistoryDataFifo[i].DateTime.date().isValid() ) - { - if ( vHistoryDataFifo[i].DateTime.date() < oldestDate ) - { - oldestDate = vHistoryDataFifo[i].DateTime.date(); - } - } - } - - if ( oldestDate < minDate ) - { - oldestDate = minDate; - } - - const int iNumDaysInHistory = -curDate.daysTo ( oldestDate ) + 1; - - // draw frame of the graph - DrawFrame ( iNumDaysInHistory ); - - // add markers - for ( i = 0; i < iNumItemsForHistory; i++ ) - { - // only use valid dates - if ( vHistoryDataFifo[i].DateTime.date().isValid() && ( oldestDate <= vHistoryDataFifo[i].DateTime.date() ) ) - { - AddMarker ( vHistoryDataFifo[i] ); - } - } - - // save graph as picture in file - Save ( sFileName ); - } -} - -void AHistoryGraph::DrawFrame ( const int iNewNumTicksX ) -{ - int i; - - // store number of x-axis ticks (number of days we want to draw - // the history for - iNumTicksX = iNewNumTicksX; - - - // Create actual plot region (grid frame) ---------------------------------- - rect( gridFrameX, gridFrameY, gridFrameWidth, gridFrameHeight ); - - // calculate step for x-axis ticks so that we get the desired number of - // ticks -> 5 ticks - -// TODO the following equation does not work as expected but results are acceptable - - // we want to have "floor ( iNumTicksX / 5 )" which is the same as - // "iNumTicksX / 5" since "iNumTicksX" is an integer variable - const int iXAxisTickStep = iNumTicksX / 5 + 1; - - // grid (ticks) for x-axis - dayXSpace = static_cast ( gridFrameWidth ) / ( iNumTicksX + 1 ); - - for ( i = 0; i < static_cast ( iNumTicksX ); i++ ) - { - int iBottomExtraTickLen = 0; - const int iCurX = gridFrameX + static_cast ( dayXSpace * ( i + 1 ) ); - const QDate curXAxisDate = curDate.addDays ( 0 - static_cast ( iNumTicksX ) + i + 1 ); - - // text (print only every "iXAxisTickStep" tick) - if ( !( i % iXAxisTickStep ) ) - { - text ( iCurX - iTextOffsetX, gridFrameBottom + iXAxisTextHeight + iTextOffsetToGrid, curXAxisDate.toString ( "dd.MM." ) ); - - iBottomExtraTickLen = 5; - } - - // regular grid - line ( iCurX, 1 + gridFrameY, iCurX, gridFrameBottom + iBottomExtraTickLen ); - - // different grid width for weekends (overwrite regular grid) - if ( ( curXAxisDate.dayOfWeek() == 6 ) || // check for Saturday - ( curXAxisDate.dayOfWeek() == 7 ) ) // check for Sunday - { - const int iGridWidthWeekendHalf = iGridWidthWeekend / 2; - - line ( iCurX, 1 + gridFrameY + iGridWidthWeekendHalf, iCurX, gridFrameBottom - iGridWidthWeekendHalf, iGridWidthWeekend ); - } - } - - // grid (ticks) for y-axis, draw iNumTicksY - 2 grid lines and - // iNumTicksY - 1 text labels (the lowest grid line is the grid frame) - iYSpace = gridFrameHeight / ( iNumTicksY - 1 ); - - for ( i = 0; i < ( static_cast ( iNumTicksY ) - 1 ); i++ ) - { - const int iCurY = gridFrameY + iYSpace * ( i + 1 ); - - // text - text ( gridFrameX + iTextOffsetToGrid, - iCurY - iTextOffsetToGrid, - QString ( "%1:00" ).arg ( ( iYAxisEnd - iYAxisStart ) / ( iNumTicksY - 1 ) * ( ( iNumTicksY - 2 ) - i ) ) ); - - // grid (do not overwrite frame) - if ( i < ( static_cast ( iNumTicksY ) - 2 ) ) - { - line ( gridFrameX, iCurY, gridFrameRight, iCurY ); - } - } -} - -void AHistoryGraph::AddMarker ( const SHistoryData& curHistoryData ) -{ - // calculate x-axis offset (difference of days compared to - // current date) - const int iXAxisOffs = - curDate.daysTo ( curHistoryData.DateTime.date() ); - - // check range, if out of range, do not plot anything - if ( -iXAxisOffs > ( static_cast ( iNumTicksX ) - 1 ) ) - { - return; - } - - // calculate y-axis offset (consider hours and minutes) - const double dYAxisOffs = 24 - curHistoryData.DateTime.time().hour() - - static_cast ( curHistoryData.DateTime.time().minute() ) / 60; - - // calculate the actual point in the graph (in pixels) - int curPointX = gridFrameX + static_cast ( dayXSpace * ( static_cast ( iNumTicksX ) + iXAxisOffs ) ); - int curPointY = gridFrameY + static_cast ( static_cast ( - gridFrameHeight ) / ( iYAxisEnd - iYAxisStart ) * dYAxisOffs ); - - QString curPointColour = MarkerNewColor; - int curPointSize = iMarkerSizeNewCon; - - // we use different markers for new connection and server stop items - switch ( curHistoryData.Type ) - { - case HIT_SERVER_STOP: - curPointColour = MarkerStopColor; - curPointSize = iMarkerSizeServSt; - break; - - case HIT_LOCAL_CONNECTION: - curPointColour = MarkerNewLocalColor; - break; - - case HIT_REMOTE_CONNECTION: - curPointColour = MarkerNewColor; - break; - } - - point ( curPointX - curPointSize / 2, curPointY - curPointSize / 2, curPointSize, curPointColour ); -} - - -/* JPEG History Graph implementation ******************************************/ -CJpegHistoryGraph::CJpegHistoryGraph ( const int iMaxDaysHistory ) : - AHistoryGraph ( iMaxDaysHistory ), - PlotPixmap ( 1, 1, QImage::Format_RGB32 ), - iAxisFontWeight ( -1 ) -{ - // scale pixmap to correct size - PlotPixmap = PlotPixmap.scaled ( canvasRectWidth, canvasRectHeight ); - - // axisFontWeight is a string matching the CSS2 font-weight definition - // Thin = 0, // 100 - // ExtraLight = 12, // 200 - // Light = 25, // 300 - // Normal = 50, // 400 - // Medium = 57, // 500 - // DemiBold = 63, // 600 - // Bold = 75, // 700 - // ExtraBold = 81, // 800 - // Black = 87 // 900 - bool ok; - int weight = axisFontWeight.toInt( &ok ); - - if ( !ok ) - { - if ( !QString ( "normal" ).compare ( axisFontWeight, Qt::CaseSensitivity::CaseInsensitive ) ) - { - iAxisFontWeight = 50; - } - else if ( !QString ( "bold" ).compare ( axisFontWeight, Qt::CaseSensitivity::CaseInsensitive ) ) - { - weight = 75; - } - } - else - { - if ( weight <= 100 ) { iAxisFontWeight = 0; } - else if ( weight <= 200 ) { iAxisFontWeight = 12; } - else if ( weight <= 300 ) { iAxisFontWeight = 25; } - else if ( weight <= 400 ) { iAxisFontWeight = 50; } - else if ( weight <= 500 ) { iAxisFontWeight = 57; } - else if ( weight <= 600 ) { iAxisFontWeight = 63; } - else if ( weight <= 700 ) { iAxisFontWeight = 75; } - else if ( weight <= 800 ) { iAxisFontWeight = 81; } - else if ( weight <= 900 ) { iAxisFontWeight = 87; } - } - // if all else fails, it's left at -1 - -// QTextStream& tsConsoleStream = *( ( new ConsoleWriterFactory() )->get() ); -// tsConsoleStream << "CJpegHistoryGraph" << endl; // on console - - - // Connections ------------------------------------------------------------- - QObject::connect ( &TimerDailyUpdate, SIGNAL ( timeout() ), - this, SLOT ( OnTimerDailyUpdate() ) ); -} - -// Override Update to blank out the plot area each time -void CJpegHistoryGraph::Update() -{ - if ( bDoHistory ) - { - // create JPEG image - PlotPixmap.fill ( BackgroundColor ); // fill background - - AHistoryGraph::Update(); - } -} - -void CJpegHistoryGraph::Save ( const QString sFileName ) -{ - // save plot as a file - PlotPixmap.save ( sFileName, "JPG", 90 ); -} - -void CJpegHistoryGraph::rect ( const unsigned int x, const unsigned int y, const unsigned int width, const unsigned int height ) -{ - QPainter PlotPainter ( &PlotPixmap ); - PlotPainter.setPen ( FrameColor ); - PlotPainter.drawRect ( x, y, width, height ); -} - -void CJpegHistoryGraph::text ( const unsigned int x, const unsigned int y, const QString& value ) -{ - QPainter PlotPainter ( &PlotPixmap ); - PlotPainter.setPen ( TextColor ); - // QFont(const QString &family, int pointSize = -1, int weight = -1, bool italic = false); - PlotPainter.setFont ( QFont( axisFontFamily, static_cast ( axisFontSize ), iAxisFontWeight ) ); - PlotPainter.drawText ( QPoint ( x, y ), value ); -} - -void CJpegHistoryGraph::line ( const unsigned int x1, const unsigned int y1, const unsigned int x2, const unsigned int y2, const unsigned int strokeWidth ) -{ - QPainter PlotPainter ( &PlotPixmap ); - PlotPainter.setPen ( QPen ( QBrush ( QColor ( GridColor ) ), strokeWidth ) ); - PlotPainter.drawLine ( x1, y1, x2, y2 ); -} - -void CJpegHistoryGraph::point ( const unsigned int x, const unsigned int y, const unsigned int size, const QString& colour ) -{ - QPainter PlotPainter ( &PlotPixmap ); - PlotPainter.setPen ( QPen ( QBrush( QColor ( colour ) ), size ) ); - PlotPainter.drawPoint ( x, y ); -} - - -/* SVG History Graph implementation *******************************************/ -CSvgHistoryGraph::CSvgHistoryGraph ( const int iMaxDaysHistory ) : - AHistoryGraph ( iMaxDaysHistory ), - svgImage ( "" ), - svgStreamWriter ( &svgImage ) -{ - // set SVG veiwBox to correct size to ensure correct scaling - svgRootAttributes.append ( "viewBox", - QString ( "%1, %2, %3, %4" ) - .arg ( canvasRectX ) - .arg ( canvasRectY ) - .arg ( canvasRectWidth ) - .arg ( canvasRectHeight ) - ); - - svgRootAttributes.append ( "xmlns", "http://www.w3.org/2000/svg" ); - svgRootAttributes.append ( "xmlns:xlink", "http://www.w3.org/1999/xlink" ); - -// QTextStream& tsConsoleStream = *( ( new ConsoleWriterFactory() )->get() ); -// tsConsoleStream << "CSvgHistoryGraph" << endl; // on console - - - // Connections ------------------------------------------------------------- - QObject::connect ( &TimerDailyUpdate, SIGNAL ( timeout() ), - this, SLOT ( OnTimerDailyUpdate() ) ); -} - -// Override Update to create the fresh SVG stream each time -void CSvgHistoryGraph::Update() -{ - if ( bDoHistory ) - { - // create SVG document - svgImage = ""; - - svgStreamWriter.setAutoFormatting ( true ); - svgStreamWriter.writeStartDocument(); - svgStreamWriter.writeStartElement ( "svg" ); - svgStreamWriter.writeAttributes ( svgRootAttributes ); - - AHistoryGraph::Update(); - } -} - -void CSvgHistoryGraph::Save ( const QString sFileName ) -{ - svgStreamWriter.writeEndDocument(); - - QFile outf ( sFileName ); - - if ( !outf.open ( QFile::WriteOnly ) ) - { - throw std::runtime_error ( ( sFileName + " could not be written. Aborting." ).toStdString() ); - } - QTextStream out ( &outf ); - - out << svgImage << endl; -} - -void CSvgHistoryGraph::rect ( const unsigned int x, const unsigned int y, const unsigned int width, const unsigned int height ) -{ - svgStreamWriter.writeEmptyElement ( "rect" ); - svgStreamWriter.writeAttribute ( "x", QString ( "%1" ).arg ( x ) ); - svgStreamWriter.writeAttribute ( "y", QString ( "%1" ).arg ( y ) ); - svgStreamWriter.writeAttribute ( "width", QString ( "%1" ).arg ( width ) ); - svgStreamWriter.writeAttribute ( "height", QString ( "%1" ).arg ( height ) ); - svgStreamWriter.writeAttribute ( "stroke", FrameColor ); - svgStreamWriter.writeAttribute ( "stroke-width", QString ( "1" ) ); - svgStreamWriter.writeAttribute ( "style", QString ( "fill: none;" ) ); -} - -void CSvgHistoryGraph::text ( const unsigned int x, const unsigned int y, const QString& value ) -{ - svgStreamWriter.writeStartElement ( "text" ); - svgStreamWriter.writeAttribute ( "x", QString ( "%1" ).arg ( x ) ); - svgStreamWriter.writeAttribute ( "y", QString ( "%1" ).arg ( y ) ); - svgStreamWriter.writeAttribute ( "stroke", TextColor ); - svgStreamWriter.writeAttribute ( "font-family", axisFontFamily ); - svgStreamWriter.writeAttribute ( "font-weight", axisFontWeight ); - svgStreamWriter.writeAttribute ( "font-size", QString ( "%1" ).arg ( axisFontSize ) ); - - svgStreamWriter.writeCharacters( value ); - svgStreamWriter.writeEndElement(); -} - -void CSvgHistoryGraph::line ( const unsigned int x1, const unsigned int y1, const unsigned int x2, const unsigned int y2, const unsigned int strokeWidth ) -{ - svgStreamWriter.writeEmptyElement ( "line" ); - svgStreamWriter.writeAttribute ( "x1", QString ( "%1" ).arg ( x1 ) ); - svgStreamWriter.writeAttribute ( "y1", QString ( "%1" ).arg ( y1 ) ); - svgStreamWriter.writeAttribute ( "x2", QString ( "%1" ).arg ( x2 ) ); - svgStreamWriter.writeAttribute ( "y2", QString ( "%1" ).arg ( y2 ) ); - svgStreamWriter.writeAttribute ( "stroke", GridColor ); - svgStreamWriter.writeAttribute ( "stroke-width", QString ( "%1" ).arg ( strokeWidth ) ); -} - -void CSvgHistoryGraph::point ( const unsigned int x, const unsigned int y, const unsigned int size, const QString& colour ) -{ - svgStreamWriter.writeEmptyElement ( "rect" ); - svgStreamWriter.writeAttribute ( "x", QString ( "%1" ).arg ( x ) ); - svgStreamWriter.writeAttribute ( "y", QString ( "%1" ).arg ( y ) ); - svgStreamWriter.writeAttribute ( "width", QString ( "%1" ).arg ( size ) ); - svgStreamWriter.writeAttribute ( "height", QString ( "%1" ).arg ( size ) ); - svgStreamWriter.writeAttribute ( "stroke-opacity", "0" ); - svgStreamWriter.writeAttribute ( "fill", colour ); -} diff --git a/src/historygraph.h b/src/historygraph.h deleted file mode 100644 index 1a5709b4aa..0000000000 --- a/src/historygraph.h +++ /dev/null @@ -1,184 +0,0 @@ -/******************************************************************************\ - * Copyright (c) 2004-2020 - * - * Author(s): - * Volker Fischer - * 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 - * -\******************************************************************************/ - -#pragma once - -#include -#include -#include -#include -#include -#include "global.h" -#include "util.h" - -// for CJpegHistoryGraph -#include -#include - -// for CSvgHistoryGraph -#include -#include - - -/* Definitions ****************************************************************/ -// number of history items to store -#define NUM_ITEMS_HISTORY 20000 - - -/* Interface ******************************************************************/ -class AHistoryGraph -{ -public: - enum EHistoryItemType - { - HIT_LOCAL_CONNECTION, - HIT_REMOTE_CONNECTION, - HIT_SERVER_STOP - }; - - AHistoryGraph ( const int iMaxDaysHistory ); - ~AHistoryGraph() { } - - void Start ( const QString& sNewFileName ); - void Add ( const QDateTime& newDateTime, const EHistoryItemType curType ); - void Add ( const QDateTime& newDateTime, const QHostAddress ClientInetAddr ); - virtual void Update ( ); - -protected: - struct SHistoryData - { - QDateTime DateTime; - EHistoryItemType Type; - }; - void DrawFrame ( const int iNewNumTicksX ); - void AddMarker ( const SHistoryData& curHistoryData ); - virtual void Save ( const QString sFileName ) = 0; - - virtual void rect ( const unsigned int x, const unsigned int y, const unsigned int width, const unsigned int height ) = 0; - virtual void text ( const unsigned int x, const unsigned int y, const QString& value ) = 0; - virtual void line ( const unsigned int x1, const unsigned int y1, const unsigned int x2, const unsigned int y2, const unsigned int strokeWidth = 1 ) = 0; - virtual void point ( const unsigned int x, const unsigned int y, const unsigned int size, const QString& colour ) = 0; - - // Constructor sets these - QString sFileName; - bool bDoHistory; - CFIFO vHistoryDataFifo; - unsigned int iNumTicksX; // Class global, not sure why - int iHistMaxDays; - - QString BackgroundColor; - QString FrameColor; - QString GridColor; - QString TextColor; - QString MarkerNewColor; - QString MarkerNewLocalColor; - QString MarkerStopColor; - - const unsigned int canvasRectX; - const unsigned int canvasRectY; - const unsigned int canvasRectWidth; - const unsigned int canvasRectHeight; - - const unsigned int iGridFrameOffset; - const unsigned int iGridWidthWeekend; - const unsigned int iXAxisTextHeight; - const unsigned int gridFrameX; - const unsigned int gridFrameY; - const unsigned int gridFrameWidth; - const unsigned int gridFrameHeight; - const unsigned int gridFrameRight; - const unsigned int gridFrameBottom; - - const QString axisFontFamily; - const QString axisFontWeight; - const unsigned int axisFontSize; - - const unsigned int iYAxisStart; - const unsigned int iYAxisEnd; - const unsigned int iNumTicksY; - - const unsigned int iTextOffsetToGrid; - const unsigned int iTextOffsetX; - - const unsigned int iMarkerSizeNewCon; - const unsigned int iMarkerSizeServSt; - - // others - double dayXSpace; - unsigned int iYSpace; - QDate curDate; - QTimer TimerDailyUpdate; -}; - - -/* Implementations ************************************************************/ -class CJpegHistoryGraph : public QObject, virtual public AHistoryGraph -{ - Q_OBJECT - -public: - CJpegHistoryGraph ( const int iMaxDaysHistory ); - virtual void Update ( ); - -protected: - virtual void Save ( const QString sFileName ); - - virtual void rect ( const unsigned int x, const unsigned int y, const unsigned int width, const unsigned int height ); - virtual void text ( const unsigned int x, const unsigned int y, const QString& value ); - virtual void line ( const unsigned int x1, const unsigned int y1, const unsigned int x2, const unsigned int y2, const unsigned int strokeWidth = 1 ); - virtual void point ( const unsigned int x, const unsigned int y, const unsigned int size, const QString& colour ); - -private: - QImage PlotPixmap; - int iAxisFontWeight; - -public slots: - void OnTimerDailyUpdate() { Update(); } -}; - -class CSvgHistoryGraph : public QObject, virtual public AHistoryGraph -{ - Q_OBJECT - -public: - CSvgHistoryGraph ( const int iMaxDaysHistory ); - virtual void Update(); - -protected: - virtual void Save ( const QString sFileName ); - - virtual void rect ( const unsigned int x, const unsigned int y, const unsigned int width, const unsigned int height ); - virtual void text ( const unsigned int x, const unsigned int y, const QString& value ); - virtual void line ( const unsigned int x1, const unsigned int y1, const unsigned int x2, const unsigned int y2, const unsigned int strokeWidth = 1 ); - virtual void point ( const unsigned int x, const unsigned int y, const unsigned int size, const QString& colour ); - -private: - QXmlStreamAttributes svgRootAttributes; - QString svgImage; - QXmlStreamWriter svgStreamWriter; - -public slots: - void OnTimerDailyUpdate() { Update(); } -}; diff --git a/src/levelmeter.cpp b/src/levelmeter.cpp new file mode 100755 index 0000000000..c077efd559 --- /dev/null +++ b/src/levelmeter.cpp @@ -0,0 +1,315 @@ +/******************************************************************************\ + * Copyright (c) 2004-2020 + * + * Author(s): + * Volker Fischer + * + * Description: + * Implements a multi color LED bar + * + ****************************************************************************** + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#include "levelmeter.h" + + +/* Implementation *************************************************************/ +CLevelMeter::CLevelMeter ( QWidget* parent ) : + QWidget ( parent ), + eLevelMeterType ( MT_BAR ) +{ + // initialize LED meter + QWidget* pLEDMeter = new QWidget(); + QVBoxLayout* pLEDLayout = new QVBoxLayout ( pLEDMeter ); + pLEDLayout->setAlignment ( Qt::AlignHCenter ); + pLEDLayout->setMargin ( 0 ); + pLEDLayout->setSpacing ( 0 ); + + // create LEDs plus the clip LED + vecpLEDs.Init ( NUM_LEDS_INCL_CLIP_LED ); + + for ( int iLEDIdx = NUM_LEDS_INCL_CLIP_LED - 1; iLEDIdx >= 0; iLEDIdx-- ) + { + // 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) + if ( iLEDIdx < NUM_LEDS_INCL_CLIP_LED - 1 ) + { + pLEDLayout->addStretch(); + } + + pLEDLayout->addWidget ( vecpLEDs[iLEDIdx]->GetLabelPointer() ); + } + + // initialize bar meter + pBarMeter = new QProgressBar(); + pBarMeter->setOrientation ( Qt::Vertical ); + pBarMeter->setRange ( 0, 100 * NUM_STEPS_LED_BAR ); // use factor 100 to reduce quantization (bar is continuous) + pBarMeter->setFormat ( "" ); // suppress percent numbers + + // setup stacked layout for meter type switching mechanism + pStackedLayout = new QStackedLayout ( this ); + pStackedLayout->addWidget ( pLEDMeter ); + pStackedLayout->addWidget ( pBarMeter ); + + // 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." + pBarMeter->setMinimumSize ( QSize ( 1, 1 ) ); + pLEDMeter->setMinimumSize ( QSize ( 1, 1 ) ); + + // update the meter type (using the default value of the meter type) + SetLevelMeterType ( eLevelMeterType ); + + // setup clip indicator timer + TimerClip.setSingleShot ( true ); + TimerClip.setInterval ( CLIP_IND_TIME_OUT_MS ); + + + // Connections ------------------------------------------------------------- + QObject::connect ( &TimerClip, &QTimer::timeout, + this, &CLevelMeter::ClipReset ); +} + +CLevelMeter::~CLevelMeter() +{ + // clean up the LED objects + for ( int iLEDIdx = 0; iLEDIdx < NUM_LEDS_INCL_CLIP_LED; iLEDIdx++ ) + { + delete vecpLEDs[iLEDIdx]; + } +} + +void CLevelMeter::SetLevelMeterType ( const ELevelMeterType eNType ) +{ + eLevelMeterType = eNType; + + switch ( eNType ) + { + case MT_LED: + // initialize all LEDs + for ( int iLEDIdx = 0; iLEDIdx < NUM_LEDS_INCL_CLIP_LED; iLEDIdx++ ) + { + vecpLEDs[iLEDIdx]->SetColor ( cLED::RL_BLACK ); + } + pStackedLayout->setCurrentIndex ( 0 ); + break; + + case MT_BAR: + pStackedLayout->setCurrentIndex ( 1 ); + break; + + case MT_SLIM_BAR: + // set all LEDs to disabled, otherwise we would not get our desired small width + for ( int iLEDIdx = 0; iLEDIdx < NUM_LEDS_INCL_CLIP_LED; iLEDIdx++ ) + { + vecpLEDs[iLEDIdx]->SetColor ( cLED::RL_DISABLED ); + } + + pStackedLayout->setCurrentIndex ( 1 ); + break; + } + + // update bar meter style and reset clip state + SetBarMeterStyleAndClipStatus ( eNType, false ); +} + +void CLevelMeter::SetBarMeterStyleAndClipStatus ( const ELevelMeterType eNType, + const bool bIsClip ) +{ + switch ( eNType ) + { + case MT_SLIM_BAR: + if ( bIsClip ) + { + pBarMeter->setStyleSheet ( + "QProgressBar { border: 0px solid red;" + " margin: 0px;" + " padding: 0px;" + " width: 4px;" + " background: red; }" + "QProgressBar::chunk { background: green; }" ); + } + else + { + pBarMeter->setStyleSheet ( + "QProgressBar { border: 0px;" + " margin: 0px;" + " padding: 0px;" + " width: 4px; }" + "QProgressBar::chunk { background: green; }" ); + } + break; + + default: /* MT_BAR */ + if ( bIsClip ) + { + pBarMeter->setStyleSheet ( + "QProgressBar { border: 2px solid red;" + " margin: 1px;" + " padding: 1px;" + " width: 15px;" + " background: transparent; }" + "QProgressBar::chunk { background: green; }" ); + } + else + { + pBarMeter->setStyleSheet ( + "QProgressBar { margin: 1px;" + " padding: 1px;" + " width: 15px; }" + "QProgressBar::chunk { background: green; }" ); + } + break; + } +} + +void CLevelMeter::SetValue ( const double dValue ) +{ + switch ( eLevelMeterType ) + { + case MT_LED: + // update state of all LEDs for current level value (except of the clip LED) + for ( int iLEDIdx = 0; iLEDIdx < NUM_STEPS_LED_BAR; iLEDIdx++ ) + { + // set active LED color if value is above current LED index + if ( iLEDIdx < dValue ) + { + // check which color we should use (green, yellow or red) + if ( iLEDIdx < YELLOW_BOUND_LED_BAR ) + { + // green region + vecpLEDs[iLEDIdx]->SetColor ( cLED::RL_GREEN ); + } + else + { + if ( iLEDIdx < RED_BOUND_LED_BAR ) + { + // yellow region + vecpLEDs[iLEDIdx]->SetColor ( cLED::RL_YELLOW ); + } + else + { + // red region + vecpLEDs[iLEDIdx]->SetColor ( cLED::RL_RED ); + } + } + } + else + { + // we use black LED for inactive state + vecpLEDs[iLEDIdx]->SetColor ( cLED::RL_BLACK ); + } + } + break; + + case MT_BAR: + case MT_SLIM_BAR: + pBarMeter->setValue ( 100 * dValue ); + break; + } + + // clip indicator management (note that in case of clipping, i.e. full + // scale level, the value is above NUM_STEPS_LED_BAR since the minimum + // value of int16 is -32768 but we normalize with 32767 -> therefore + // we really only show the clipping indicator, if actually the largest + // value of int16 is used) + if ( dValue > NUM_STEPS_LED_BAR ) + { + switch ( eLevelMeterType ) + { + case MT_LED: + vecpLEDs[NUM_STEPS_LED_BAR]->SetColor ( cLED::RL_RED ); + break; + + case MT_BAR: + case MT_SLIM_BAR: + SetBarMeterStyleAndClipStatus ( eLevelMeterType, true ); + break; + } + + TimerClip.start(); + } +} + +void CLevelMeter::ClipReset() +{ + // we manually want to reset the clipping indicator: stop timer and reset + // clipping indicator GUI element + TimerClip.stop(); + + switch ( eLevelMeterType ) + { + case MT_LED: + vecpLEDs[NUM_STEPS_LED_BAR]->SetColor ( cLED::RL_BLACK ); + break; + + case MT_BAR: + case MT_SLIM_BAR: + SetBarMeterStyleAndClipStatus ( eLevelMeterType, false ); + break; + } +} + + +CLevelMeter::cLED::cLED ( QWidget* parent ) : + BitmCubeRoundBlack ( QString::fromUtf8 ( ":/png/LEDs/res/HLEDBlackSmall.png" ) ), + BitmCubeRoundGreen ( QString::fromUtf8 ( ":/png/LEDs/res/HLEDGreenSmall.png" ) ), + BitmCubeRoundYellow ( QString::fromUtf8 ( ":/png/LEDs/res/HLEDYellowSmall.png" ) ), + BitmCubeRoundRed ( QString::fromUtf8 ( ":/png/LEDs/res/HLEDRedSmall.png" ) ) +{ + // create LED label + pLEDLabel = new QLabel ( "", parent ); + + // set initial bitmap + pLEDLabel->setPixmap ( BitmCubeRoundBlack ); + eCurLightColor = RL_BLACK; +} + +void CLevelMeter::cLED::SetColor ( const ELightColor eNewColor ) +{ + // only update LED if color has changed + if ( eNewColor != eCurLightColor ) + { + switch ( eNewColor ) + { + case RL_DISABLED: + // note that this is required for the compact channel mode + pLEDLabel->setPixmap ( QPixmap() ); + break; + + case RL_BLACK: + pLEDLabel->setPixmap ( BitmCubeRoundBlack ); + break; + + case RL_GREEN: + pLEDLabel->setPixmap ( BitmCubeRoundGreen ); + break; + + case RL_YELLOW: + pLEDLabel->setPixmap ( BitmCubeRoundYellow ); + break; + + case RL_RED: + pLEDLabel->setPixmap ( BitmCubeRoundRed ); + break; + } + eCurLightColor = eNewColor; + } +} diff --git a/src/multicolorledbar.h b/src/levelmeter.h similarity index 68% rename from src/multicolorledbar.h rename to src/levelmeter.h index bd3863c61e..30f39f35b1 100755 --- a/src/multicolorledbar.h +++ b/src/levelmeter.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -35,14 +35,12 @@ /* Definitions ****************************************************************/ -// defines for LED level meter CMultiColorLEDBar -#define NUM_STEPS_LED_BAR 8 -#define RED_BOUND_LED_BAR 7 -#define YELLOW_BOUND_LED_BAR 5 +#define NUM_LEDS_INCL_CLIP_LED ( NUM_STEPS_LED_BAR + 1 ) +#define CLIP_IND_TIME_OUT_MS 20000 /* Classes ********************************************************************/ -class CMultiColorLEDBar : public QWidget +class CLevelMeter : public QWidget { Q_OBJECT @@ -50,13 +48,14 @@ class CMultiColorLEDBar : public QWidget enum ELevelMeterType { MT_LED, - MT_BAR + MT_BAR, + MT_SLIM_BAR }; - CMultiColorLEDBar ( QWidget* parent = nullptr, Qt::WindowFlags f = nullptr ); - virtual ~CMultiColorLEDBar(); + CLevelMeter ( QWidget* parent = nullptr ); + virtual ~CLevelMeter(); - void setValue ( const double dValue ); + void SetValue ( const double dValue ); void SetLevelMeterType ( const ELevelMeterType eNType ); protected: @@ -66,19 +65,20 @@ class CMultiColorLEDBar : public QWidget enum ELightColor { RL_DISABLED, - RL_GREY, + RL_BLACK, RL_GREEN, RL_YELLOW, RL_RED }; cLED ( QWidget* parent ); - void setColor ( const ELightColor eNewColor ); - QLabel* getLabelPointer() { return pLEDLabel; } + + void SetColor ( const ELightColor eNewColor ); + ELightColor GetColor() { return eCurLightColor; }; + QLabel* GetLabelPointer() { return pLEDLabel; } protected: - QPixmap BitmCubeRoundDisabled; - QPixmap BitmCubeRoundGrey; + QPixmap BitmCubeRoundBlack; QPixmap BitmCubeRoundGreen; QPixmap BitmCubeRoundYellow; QPixmap BitmCubeRoundRed; @@ -87,11 +87,18 @@ class CMultiColorLEDBar : public QWidget QLabel* pLEDLabel; }; - void Reset ( const bool bEnabled ); - virtual void changeEvent ( QEvent* curEvent ); + virtual void mousePressEvent ( QMouseEvent* ) override { ClipReset(); } + + void SetBarMeterStyleAndClipStatus ( const ELevelMeterType eNType, + const bool bIsClip ); QStackedLayout* pStackedLayout; ELevelMeterType eLevelMeterType; CVector vecpLEDs; - QProgressBar* pProgressBar; + QProgressBar* pBarMeter; + + QTimer TimerClip; + +public slots: + void ClipReset(); }; diff --git a/src/main.cpp b/src/main.cpp index 6dda585252..b59013a627 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,25 +18,29 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ -#include -#include +#include #include #include -#include -#include #include "global.h" -#include "clientdlg.h" -#include "serverdlg.h" +#ifndef HEADLESS +# include +# include +# include "clientdlg.h" +# include "serverdlg.h" +#endif #include "settings.h" #include "testbench.h" #include "util.h" #ifdef ANDROID # include #endif +#if defined ( __APPLE__ ) || defined ( __MACOSX ) +# include "mac/activity.h" +#endif // Implementation ************************************************************** @@ -44,9 +48,10 @@ int main ( int argc, char** argv ) { - QTextStream& tsConsole = *( ( new ConsoleWriterFactory() )->get() ); - QString strArgument; - double rDbleArgument; + QTextStream& tsConsole = *( ( new ConsoleWriterFactory() )->get() ); + QString strArgument; + double rDbleArgument; + QList CommandLineOptions; // initialize all flags and string which might be changed by command line // arguments @@ -61,25 +66,26 @@ int main ( int argc, char** argv ) bool bShowComplRegConnList = false; bool bDisconnectAllClientsOnQuit = false; bool bUseDoubleSystemFrameSize = true; // default is 128 samples frame size + bool bUseMultithreading = false; bool bShowAnalyzerConsole = false; - bool bCentServPingServerInList = false; + bool bMuteStream = false; + bool bMuteMeInPersonalMix = false; + bool bDisableRecording = 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 = DEFAULT_PORT_NUMBER; ELicenceType eLicenceType = LT_NO_LICENCE; + QString strMIDISetup = ""; QString strConnOnStartupAddress = ""; QString strIniFileName = ""; QString strHTMLStatusFileName = ""; - QString strServerName = ""; QString strLoggingFileName = ""; - QString strHistoryFileName = ""; QString strRecordingDirName = ""; QString strCentralServer = ""; QString strServerInfo = ""; + QString strServerListFilter = ""; QString strWelcomeMessage = ""; QString strClientName = APP_NAME; @@ -96,6 +102,7 @@ int main ( int argc, char** argv ) { bIsClient = false; tsConsole << "- server mode chosen" << endl; + CommandLineOptions << "--server"; continue; } @@ -108,6 +115,7 @@ int main ( int argc, char** argv ) { bUseGUI = false; tsConsole << "- no GUI mode chosen" << endl; + CommandLineOptions << "--nogui"; continue; } @@ -121,11 +129,12 @@ int main ( int argc, char** argv ) // right now only the creative commons licence is supported eLicenceType = LT_CREATIVECOMMONS; tsConsole << "- licence required" << endl; + CommandLineOptions << "--licence"; continue; } - // Use 64 samples frame size mode ---------------------------------------------------- + // Use 64 samples frame size mode -------------------------------------- if ( GetFlagArgument ( argv, i, "-F", @@ -133,6 +142,20 @@ int main ( int argc, char** argv ) { bUseDoubleSystemFrameSize = false; // 64 samples frame size tsConsole << "- using " << SYSTEM_FRAME_SIZE_SAMPLES << " samples frame size mode" << endl; + CommandLineOptions << "--fastupdate"; + continue; + } + + + // Use multithreading -------------------------------------------------- + if ( GetFlagArgument ( argv, + i, + "-T", + "--multithreading" ) ) + { + bUseMultithreading = true; + tsConsole << "- using multithreading" << endl; + CommandLineOptions << "--multithreading"; continue; } @@ -153,26 +176,7 @@ int main ( int argc, char** argv ) tsConsole << "- maximum number of channels: " << iNumServerChannels << endl; - continue; - } - - - // Maximum days in history display ------------------------------------- - if ( GetNumericArgument ( tsConsole, - argc, - argv, - i, - "-D", - "--histdays", - 1, - 366, - rDbleArgument ) ) - { - iMaxDaysHistory = static_cast ( rDbleArgument ); - - tsConsole << "- maximum days in history display: " - << iMaxDaysHistory << endl; - + CommandLineOptions << "--numchannels"; continue; } @@ -185,18 +189,7 @@ int main ( int argc, char** argv ) { bStartMinimized = true; tsConsole << "- start minimized enabled" << endl; - continue; - } - - - // Ping servers in list for central server ----------------------------- - if ( GetFlagArgument ( argv, - i, - "-g", - "--pingservers" ) ) - { - bCentServPingServerInList = true; - tsConsole << "- ping servers in slave server list" << endl; + CommandLineOptions << "--startminimized"; continue; } @@ -209,6 +202,7 @@ int main ( int argc, char** argv ) { bDisconnectAllClientsOnQuit = true; tsConsole << "- disconnect all clients on quit" << endl; + CommandLineOptions << "--discononquit"; continue; } @@ -221,6 +215,7 @@ int main ( int argc, char** argv ) { bNoAutoJackConnect = true; tsConsole << "- disable auto Jack connections" << endl; + CommandLineOptions << "--nojackconnect"; continue; } @@ -233,6 +228,7 @@ int main ( int argc, char** argv ) { bUseTranslation = false; tsConsole << "- translations disabled" << endl; + CommandLineOptions << "--notranslation"; continue; } @@ -248,6 +244,7 @@ int main ( int argc, char** argv ) { bShowComplRegConnList = true; tsConsole << "- show all registered servers in server list" << endl; + CommandLineOptions << "--showallservers"; continue; } @@ -262,23 +259,23 @@ int main ( int argc, char** argv ) { bShowAnalyzerConsole = true; tsConsole << "- show analyzer console" << endl; + CommandLineOptions << "--showanalyzerconsole"; continue; } // Controller MIDI channel --------------------------------------------- - if ( GetNumericArgument ( tsConsole, - argc, - argv, - i, - "--ctrlmidich", // no short form - "--ctrlmidich", - 0, - 15, - rDbleArgument ) ) + if ( GetStringArgument ( tsConsole, + argc, + argv, + i, + "--ctrlmidich", // no short form + "--ctrlmidich", + strArgument ) ) { - iCtrlMIDIChannel = static_cast ( rDbleArgument ); - tsConsole << "- selected controller MIDI channel: " << iCtrlMIDIChannel << endl; + strMIDISetup = strArgument; + tsConsole << "- MIDI controller settings: " << strMIDISetup << endl; + CommandLineOptions << "--ctrlmidich"; continue; } @@ -294,6 +291,7 @@ int main ( int argc, char** argv ) { strLoggingFileName = strArgument; tsConsole << "- logging file name: " << strLoggingFileName << endl; + CommandLineOptions << "--log"; continue; } @@ -312,6 +310,7 @@ int main ( int argc, char** argv ) iPortNumber = static_cast ( rDbleArgument ); bCustomPortNumberGiven = true; tsConsole << "- selected port number: " << iPortNumber << endl; + CommandLineOptions << "--port"; continue; } @@ -327,19 +326,7 @@ int main ( int argc, char** argv ) { strHTMLStatusFileName = strArgument; tsConsole << "- HTML status file name: " << strHTMLStatusFileName << endl; - continue; - } - - if ( GetStringArgument ( tsConsole, - argc, - argv, - i, - "-a", - "--servername", - strArgument ) ) - { - strServerName = strArgument; - tsConsole << "- server name for HTML status file: " << strServerName << endl; + CommandLineOptions << "--htmlstatus"; continue; } @@ -349,27 +336,13 @@ int main ( int argc, char** argv ) argc, argv, i, - "--clientname", + "--clientname", // no short form "--clientname", strArgument ) ) { strClientName = QString ( APP_NAME ) + " " + strArgument; tsConsole << "- client name: " << strClientName << endl; - continue; - } - - - // Server history file name -------------------------------------------- - if ( GetStringArgument ( tsConsole, - argc, - argv, - i, - "-y", - "--history", - strArgument ) ) - { - strHistoryFileName = strArgument; - tsConsole << "- history file name: " << strHistoryFileName << endl; + CommandLineOptions << "--clientname"; continue; } @@ -385,6 +358,20 @@ int main ( int argc, char** argv ) { strRecordingDirName = strArgument; tsConsole << "- recording directory name: " << strRecordingDirName << endl; + CommandLineOptions << "--recording"; + continue; + } + + + // Disable recording on startup ---------------------------------------- + if ( GetFlagArgument ( argv, + i, + "--norecord", // no short form + "--norecord" ) ) + { + bDisableRecording = true; + tsConsole << "- recording will not be enabled" << endl; + CommandLineOptions << "--norecord"; continue; } @@ -400,6 +387,7 @@ int main ( int argc, char** argv ) { strCentralServer = strArgument; tsConsole << "- central server: " << strCentralServer << endl; + CommandLineOptions << "--centralserver"; continue; } @@ -415,6 +403,23 @@ int main ( int argc, char** argv ) { strServerInfo = strArgument; tsConsole << "- server info: " << strServerInfo << endl; + CommandLineOptions << "--serverinfo"; + continue; + } + + + // Server list filter -------------------------------------------------- + if ( GetStringArgument ( tsConsole, + argc, + argv, + i, + "-f", + "--listfilter", + strArgument ) ) + { + strServerListFilter = strArgument; + tsConsole << "- server list filter: " << strServerListFilter << endl; + CommandLineOptions << "--listfilter"; continue; } @@ -430,6 +435,7 @@ int main ( int argc, char** argv ) { strWelcomeMessage = strArgument; tsConsole << "- welcome message: " << strWelcomeMessage << endl; + CommandLineOptions << "--welcomemessage"; continue; } @@ -445,6 +451,7 @@ int main ( int argc, char** argv ) { strIniFileName = strArgument; tsConsole << "- initialization file name: " << strIniFileName << endl; + CommandLineOptions << "--inifile"; continue; } @@ -458,8 +465,35 @@ int main ( int argc, char** argv ) "--connect", strArgument ) ) { - strConnOnStartupAddress = strArgument; + strConnOnStartupAddress = NetworkUtil::FixAddress ( strArgument ); tsConsole << "- connect on startup to address: " << strConnOnStartupAddress << endl; + CommandLineOptions << "--connect"; + continue; + } + + + // Mute stream on startup ---------------------------------------------- + if ( GetFlagArgument ( argv, + i, + "-M", + "--mutestream" ) ) + { + bMuteStream = true; + tsConsole << "- mute stream activated" << endl; + CommandLineOptions << "--mutestream"; + continue; + } + + + // For headless client mute my own signal in personal mix -------------- + if ( GetFlagArgument ( argv, + i, + "--mutemyown", // no short form + "--mutemyown" ) ) + { + bMuteMeInPersonalMix = true; + tsConsole << "- mute me in my personal mix" << endl; + CommandLineOptions << "--mutemyown"; continue; } @@ -468,7 +502,7 @@ int main ( int argc, char** argv ) if ( ( !strcmp ( argv[i], "--version" ) ) || ( !strcmp ( argv[i], "-v" ) ) ) { - tsConsole << CAboutDlg::GetVersionAndNameStr ( false ) << endl; + tsConsole << GetVersionAndNameStr ( false ) << endl; exit ( 1 ); } @@ -498,6 +532,31 @@ int main ( int argc, char** argv ) // Dependencies ------------------------------------------------------------ +#ifdef HEADLESS + if ( bUseGUI ) + { + bUseGUI = false; + tsConsole << "No GUI support compiled. Running in headless mode." << endl; + } + Q_UNUSED ( bStartMinimized ) // avoid compiler warnings + Q_UNUSED ( bShowComplRegConnList ) // avoid compiler warnings + Q_UNUSED ( bShowAnalyzerConsole ) // avoid compiler warnings + Q_UNUSED ( bMuteStream ) // avoid compiler warnings +#endif + + // the inifile is not supported for the headless server mode + if ( !bIsClient && !bUseGUI && !strIniFileName.isEmpty() ) + { + tsConsole << "No initialization file support in headless server mode." << endl; + } + + // mute my own signal in personal mix is only supported for headless mode + if ( bIsClient && bUseGUI && bMuteMeInPersonalMix ) + { + bMuteMeInPersonalMix = false; + tsConsole << "Mute my own signal in my personal mix is only supported in headless mode." << endl; + } + // per definition: if we are in "GUI" server mode and no central server // address is given, we use the default central server address if ( !bIsClient && bUseGUI && strCentralServer.isEmpty() ) @@ -512,18 +571,16 @@ int main ( int argc, char** argv ) iPortNumber += 10; // increment by 10 } - // 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 +#ifdef HEADLESS + QCoreApplication* pApp = new QCoreApplication ( argc, argv ); +#else QCoreApplication* pApp = bUseGUI - ? new QApplication ( argc, argv ) - : new QCoreApplication ( argc, argv ); + ? new QApplication ( argc, argv ) + : new QCoreApplication ( argc, argv ); +#endif #ifdef ANDROID // special Android coded needed for record audio permission handling @@ -551,25 +608,16 @@ int main ( int argc, char** argv ) pApp->addLibraryPath ( QString ( ApplDir.absolutePath() ) ); #endif - // init resources - Q_INIT_RESOURCE(resources); - - // load translations - QTranslator myappTranslator, myqtTranslator; +#if defined ( __APPLE__ ) || defined ( __MACOSX ) + // On OSX we need to declare an activity to ensure the process doesn't get + // throttled by OS level Nap, Sleep, and Thread Priority systems. + CActivity activity; - if ( bUseGUI && bUseTranslation ) - { - if ( myappTranslator.load ( ":/translations/translation.qm" ) ) - { - pApp->installTranslator ( &myappTranslator ); - } + activity.BeginActivity(); +#endif - // allows the Qt messages to be translated in the application - if ( myqtTranslator.load ( QLocale(), "qt", "_", QLibraryInfo::location ( QLibraryInfo::TranslationsPath ) ) ) - { - pApp->installTranslator ( &myqtTranslator ); - } - } + // init resources + Q_INIT_RESOURCE(resources); // TEST -> activate the following line to activate the test bench, @@ -584,33 +632,44 @@ int main ( int argc, char** argv ) // actual client object CClient Client ( iPortNumber, strConnOnStartupAddress, - iCtrlMIDIChannel, + strMIDISetup, bNoAutoJackConnect, - strClientName ); + strClientName, + bMuteMeInPersonalMix ); + + // load settings from init-file (command line options override) + CClientSettings Settings ( &Client, strIniFileName ); + Settings.Load ( CommandLineOptions ); - // load settings from init-file - CSettings Settings ( &Client, strIniFileName ); - Settings.Load(); + // load translation + if ( bUseGUI && bUseTranslation ) + { + CLocale::LoadTranslation ( Settings.strLanguage, pApp ); + CInstPictures::UpdateTableOnLanguageChange(); + } +#ifndef HEADLESS if ( bUseGUI ) { // GUI object CClientDlg ClientDlg ( &Client, &Settings, strConnOnStartupAddress, + strMIDISetup, bShowComplRegConnList, bShowAnalyzerConsole, - nullptr, - Qt::Window ); + bMuteStream, + nullptr ); // show dialog ClientDlg.show(); pApp->exec(); } else +#endif { // only start application without using the GUI - tsConsole << CAboutDlg::GetVersionAndNameStr ( false ) << endl; + tsConsole << GetVersionAndNameStr ( false ) << endl; pApp->exec(); } @@ -620,25 +679,32 @@ int main ( int argc, char** argv ) // Server: // actual server object CServer Server ( iNumServerChannels, - iMaxDaysHistory, strLoggingFileName, iPortNumber, strHTMLStatusFileName, - strHistoryFileName, - strServerName, strCentralServer, strServerInfo, + strServerListFilter, strWelcomeMessage, strRecordingDirName, - bCentServPingServerInList, bDisconnectAllClientsOnQuit, bUseDoubleSystemFrameSize, + bUseMultithreading, + bDisableRecording, eLicenceType ); + +#ifndef HEADLESS if ( bUseGUI ) { - // load settings from init-file - CSettings Settings ( &Server, strIniFileName ); - Settings.Load(); + // load settings from init-file (command line options override) + CServerSettings Settings ( &Server, strIniFileName ); + Settings.Load ( CommandLineOptions ); + + // load translation + if ( bUseGUI && bUseTranslation ) + { + CLocale::LoadTranslation ( Settings.strLanguage, pApp ); + } // update server list AFTER restoring the settings from the // settings file @@ -648,8 +714,7 @@ int main ( int argc, char** argv ) CServerDlg ServerDlg ( &Server, &Settings, bStartMinimized, - nullptr, - Qt::Window ); + nullptr ); // show dialog (if not the minimized flag is set) if ( !bStartMinimized ) @@ -660,9 +725,10 @@ int main ( int argc, char** argv ) pApp->exec(); } else +#endif { // only start application without using the GUI - tsConsole << CAboutDlg::GetVersionAndNameStr ( false ) << endl; + tsConsole << GetVersionAndNameStr ( false ) << endl; // update serverlist Server.UpdateServerList(); @@ -672,9 +738,10 @@ int main ( int argc, char** argv ) } } - catch ( CGenErr generr ) + catch ( const CGenErr& generr ) { // show generic error +#ifndef HEADLESS if ( bUseGUI ) { QMessageBox::critical ( nullptr, @@ -684,10 +751,15 @@ int main ( int argc, char** argv ) nullptr ); } else +#endif { tsConsole << generr.GetErrorText() << endl; } } + +#if defined ( __APPLE__ ) || defined ( __MACOSX ) + activity.EndActivity(); +#endif return 0; } @@ -702,42 +774,40 @@ QString UsageArguments ( char **argv ) "Usage: " + QString ( argv[0] ) + " [option] [optional argument]\n" "\nRecognized options:\n" " -h, -?, --help display this help text and exit\n" - " -i, --inifile initialization file name\n" + " -i, --inifile initialization file name (not\n" + " supported for headless server mode)\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" + " (or 'localhost' to be a central server)\n" + " -f, --listfilter server list whitelist filter in the format:\n" + " [IP address 1];[IP address 2];[IP address 3]; ...\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\n" + " -L, --licence show an agreement window before users can connect\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]; ...\n" - " -R, --recording enables recording and sets directory to contain\n" - " recorded jams\n" + " -o, --serverinfo infos of this server in the format:\n" + " [name];[city];[country as QLocale ID]\n" + " -R, --recording sets directory to contain recorded jams\n" + " --norecord disables recording (when enabled by default by -R)\n" " -s, --server start server\n" + " -T, --multithreading use multithreading to make better use of\n" + " multi-core CPUs and support more clients\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" + " -M, --mutestream starts the application in muted state\n" + " --mutemyown mute me in my personal mix (headless 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"; + "\nExample: " + QString ( argv[0] ) + " -s --inifile myinifile.ini\n"; } bool GetFlagArgument ( char** argv, diff --git a/src/multicolorled.cpp b/src/multicolorled.cpp index 411119c994..4fa772c730 100755 --- a/src/multicolorled.cpp +++ b/src/multicolorled.cpp @@ -21,7 +21,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -29,20 +29,30 @@ /* Implementation *************************************************************/ -CMultiColorLED::CMultiColorLED ( QWidget* parent, Qt::WindowFlags f ) - : QLabel ( parent, f ), - BitmCubeDisabled ( QString::fromUtf8 ( ":/png/LEDs/res/CLEDDisabledSmall.png" ) ), - BitmCubeGrey ( QString::fromUtf8 ( ":/png/LEDs/res/CLEDGreySmall.png" ) ), - BitmCubeGreen ( QString::fromUtf8 ( ":/png/LEDs/res/CLEDGreenSmall.png" ) ), - BitmCubeYellow ( QString::fromUtf8 ( ":/png/LEDs/res/CLEDYellowSmall.png" ) ), - BitmCubeRed ( QString::fromUtf8 ( ":/png/LEDs/res/CLEDRedSmall.png" ) ) +CMultiColorLED::CMultiColorLED ( QWidget* parent ) + : QLabel ( parent ), + BitmCubeDisabled ( QString::fromUtf8 ( ":/png/LEDs/res/CLEDDisabledSmall.png" ) ), + BitmCubeGrey ( QString::fromUtf8 ( ":/png/LEDs/res/CLEDGreySmall.png" ) ), + BitmCubeGreen ( QString::fromUtf8 ( ":/png/LEDs/res/CLEDGreenSmall.png" ) ), + BitmCubeYellow ( QString::fromUtf8 ( ":/png/LEDs/res/CLEDYellowSmall.png" ) ), + BitmCubeRed ( QString::fromUtf8 ( ":/png/LEDs/res/CLEDRedSmall.png" ) ), + BitmIndicatorGreen ( QString::fromUtf8 ( ":/png/LEDs/res/IndicatorGreen.png" ) ), + BitmIndicatorYellow ( QString::fromUtf8 ( ":/png/LEDs/res/IndicatorYellow.png" ) ), + BitmIndicatorRed ( QString::fromUtf8 ( ":/png/LEDs/res/IndicatorRed.png" ) ) { - // init color flags - Reset(); - // set init bitmap setPixmap ( BitmCubeGrey ); eColorFlag = RL_GREY; + + // set default type and reset + eType = MT_LED; + Reset(); +} + +void CMultiColorLED::SetType ( const EType eNType ) +{ + eType = eNType; + Reset(); } void CMultiColorLED::changeEvent ( QEvent* curEvent ) @@ -71,7 +81,15 @@ void CMultiColorLED::SetColor ( const ELightColor eNewColorFlag ) // red if ( eColorFlag != RL_RED ) { - setPixmap ( BitmCubeRed ); + if ( eType == MT_LED ) + { + setPixmap ( BitmCubeRed ); + } + else + { + setPixmap ( BitmIndicatorRed ); + } + setAccessibleDescription ( tr ( "Red" ) ); eColorFlag = RL_RED; } break; @@ -80,7 +98,15 @@ void CMultiColorLED::SetColor ( const ELightColor eNewColorFlag ) // yellow if ( eColorFlag != RL_YELLOW ) { - setPixmap ( BitmCubeYellow ); + if ( eType == MT_LED ) + { + setPixmap ( BitmCubeYellow ); + } + else + { + setPixmap ( BitmIndicatorYellow ); + } + setAccessibleDescription ( tr ( "Yellow" ) ); eColorFlag = RL_YELLOW; } break; @@ -89,7 +115,15 @@ void CMultiColorLED::SetColor ( const ELightColor eNewColorFlag ) // green if ( eColorFlag != RL_GREEN ) { - setPixmap ( BitmCubeGreen ); + if ( eType == MT_LED ) + { + setPixmap ( BitmCubeGreen ); + } + else + { + setPixmap ( BitmIndicatorGreen ); + } + setAccessibleDescription ( tr ( "Green" ) ); eColorFlag = RL_GREEN; } break; @@ -98,7 +132,15 @@ void CMultiColorLED::SetColor ( const ELightColor eNewColorFlag ) // if no color is active, set control to grey light if ( eColorFlag != RL_GREY ) { - setPixmap ( BitmCubeGrey ); + if ( eType == MT_LED ) + { + setPixmap ( BitmCubeGrey ); + } + else + { + // make it invisible if in indicator mode + setPixmap ( QPixmap() ); + } eColorFlag = RL_GREY; } break; @@ -109,6 +151,8 @@ void CMultiColorLED::Reset() { if ( isEnabled() ) { + // set color flag to disable to make sure the pixmap gets updated + eColorFlag = RL_DISABLED; SetColor ( RL_GREY ); } } diff --git a/src/multicolorled.h b/src/multicolorled.h index c72bc8f4b4..6e76fe0741 100755 --- a/src/multicolorled.h +++ b/src/multicolorled.h @@ -25,7 +25,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -50,10 +50,17 @@ class CMultiColorLED : public QLabel RL_RED }; - CMultiColorLED ( QWidget* parent = nullptr, Qt::WindowFlags f = nullptr ); + enum EType + { + MT_LED, + MT_INDICATOR + }; + + CMultiColorLED ( QWidget* parent = nullptr ); void Reset(); void SetLight ( const ELightColor eNewStatus ); + void SetType ( const EType eNType ); protected: ELightColor eColorFlag; @@ -66,8 +73,12 @@ class CMultiColorLED : public QLabel QPixmap BitmCubeGreen; QPixmap BitmCubeYellow; QPixmap BitmCubeRed; + QPixmap BitmIndicatorGreen; + QPixmap BitmIndicatorYellow; + QPixmap BitmIndicatorRed; int iUpdateTime; + EType eType; bool bFlagRedLi; bool bFlagGreenLi; diff --git a/src/multicolorledbar.cpp b/src/multicolorledbar.cpp deleted file mode 100755 index b67efea447..0000000000 --- a/src/multicolorledbar.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/******************************************************************************\ - * Copyright (c) 2004-2020 - * - * Author(s): - * Volker Fischer - * - * Description: - * Implements a multi color LED bar - * - ****************************************************************************** - * - * 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 - * -\******************************************************************************/ - -#include "multicolorledbar.h" - - -/* Implementation *************************************************************/ -CMultiColorLEDBar::CMultiColorLEDBar ( QWidget* parent, Qt::WindowFlags f ) : - QWidget ( parent, f ), - eLevelMeterType ( MT_BAR ) -{ - // initialize LED meter - QWidget* pLEDMeter = new QWidget(); - QVBoxLayout* pLEDLayout = new QVBoxLayout ( pLEDMeter ); - pLEDLayout->setAlignment ( Qt::AlignHCenter ); - pLEDLayout->setMargin ( 0 ); - pLEDLayout->setSpacing ( 0 ); - - // create LEDs - vecpLEDs.Init ( NUM_STEPS_LED_BAR ); - - for ( int iLEDIdx = NUM_STEPS_LED_BAR - 1; iLEDIdx >= 0; iLEDIdx-- ) - { - // 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) - if ( iLEDIdx < NUM_STEPS_LED_BAR - 1 ) - { - pLEDLayout->addStretch(); - } - - pLEDLayout->addWidget ( vecpLEDs[iLEDIdx]->getLabelPointer() ); - } - - // initialize bar meter - pProgressBar = new QProgressBar(); - pProgressBar->setOrientation ( Qt::Vertical ); - pProgressBar->setRange ( 0, 100 * NUM_STEPS_LED_BAR ); - pProgressBar->setFormat ( "" ); // suppress percent numbers - pProgressBar->setStyleSheet ( - "QProgressBar { margin: 1px;" - " padding: 1px; " - " width: 15px; }" - "QProgressBar::chunk { background: green; }" ); - - // setup stacked layout for meter type switching mechanism - pStackedLayout = new QStackedLayout ( this ); - pStackedLayout->addWidget ( pLEDMeter ); - pStackedLayout->addWidget ( pProgressBar ); - - // 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 ( 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) - SetLevelMeterType ( eLevelMeterType ); -} - -CMultiColorLEDBar::~CMultiColorLEDBar() -{ - // clean up the LED objects - for ( int iLEDIdx = 0; iLEDIdx < NUM_STEPS_LED_BAR; iLEDIdx++ ) - { - delete vecpLEDs[iLEDIdx]; - } -} - -void CMultiColorLEDBar::changeEvent ( QEvent* curEvent ) -{ - // act on enabled changed state - if ( curEvent->type() == QEvent::EnabledChange ) - { - // reset all LEDs - Reset ( this->isEnabled() ); - } -} - -void CMultiColorLEDBar::Reset ( const bool bEnabled ) -{ - // update state of all LEDs - for ( int iLEDIdx = 0; iLEDIdx < NUM_STEPS_LED_BAR; iLEDIdx++ ) - { - // different reset behavoiur for enabled and disabled control - if ( bEnabled ) - { - vecpLEDs[iLEDIdx]->setColor ( cLED::RL_GREY ); - } - else - { - vecpLEDs[iLEDIdx]->setColor ( cLED::RL_DISABLED ); - } - } -} - -void CMultiColorLEDBar::SetLevelMeterType ( const ELevelMeterType eNType ) -{ - eLevelMeterType = eNType; - - switch ( eNType ) - { - case MT_LED: - pStackedLayout->setCurrentIndex ( 0 ); - break; - - case MT_BAR: - pStackedLayout->setCurrentIndex ( 1 ); - break; - } -} - -void CMultiColorLEDBar::setValue ( const double dValue ) -{ - if ( this->isEnabled() ) - { - switch ( eLevelMeterType ) - { - case MT_LED: - // update state of all LEDs for current level value - for ( int iLEDIdx = 0; iLEDIdx < NUM_STEPS_LED_BAR; iLEDIdx++ ) - { - // set active LED color if value is above current LED index - if ( iLEDIdx < dValue ) - { - // check which color we should use (green, yellow or red) - if ( iLEDIdx < YELLOW_BOUND_LED_BAR ) - { - // green region - vecpLEDs[iLEDIdx]->setColor ( cLED::RL_GREEN ); - } - else - { - if ( iLEDIdx < RED_BOUND_LED_BAR ) - { - // yellow region - vecpLEDs[iLEDIdx]->setColor ( cLED::RL_YELLOW ); - } - else - { - // red region - vecpLEDs[iLEDIdx]->setColor ( cLED::RL_RED ); - } - } - } - else - { - // we use grey LED for inactive state - vecpLEDs[iLEDIdx]->setColor ( cLED::RL_GREY ); - } - } - break; - - case MT_BAR: - pProgressBar->setValue ( 100 * dValue ); - break; - } - } -} - -CMultiColorLEDBar::cLED::cLED ( QWidget* parent ) : - BitmCubeRoundDisabled ( QString::fromUtf8 ( ":/png/LEDs/res/CLEDDisabledSmall.png" ) ), - BitmCubeRoundGrey ( QString::fromUtf8 ( ":/png/LEDs/res/HLEDGreySmall.png" ) ), - BitmCubeRoundGreen ( QString::fromUtf8 ( ":/png/LEDs/res/HLEDGreenSmall.png" ) ), - BitmCubeRoundYellow ( QString::fromUtf8 ( ":/png/LEDs/res/HLEDYellowSmall.png" ) ), - BitmCubeRoundRed ( QString::fromUtf8 ( ":/png/LEDs/res/HLEDRedSmall.png" ) ) -{ - // create LED label - pLEDLabel = new QLabel ( "", parent ); - - // bitmap defines minimum size of the label - pLEDLabel->setMinimumSize ( BitmCubeRoundGrey.width(), BitmCubeRoundGrey.height() ); - - // set initial bitmap - pLEDLabel->setPixmap ( BitmCubeRoundGrey ); - eCurLightColor = RL_GREY; -} - -void CMultiColorLEDBar::cLED::setColor ( const ELightColor eNewColor ) -{ - // only update LED if color has changed - if ( eNewColor != eCurLightColor ) - { - switch ( eNewColor ) - { - case RL_DISABLED: - pLEDLabel->setPixmap ( BitmCubeRoundDisabled ); - break; - - case RL_GREY: - pLEDLabel->setPixmap ( BitmCubeRoundGrey ); - break; - - case RL_GREEN: - pLEDLabel->setPixmap ( BitmCubeRoundGreen ); - break; - - case RL_YELLOW: - pLEDLabel->setPixmap ( BitmCubeRoundYellow ); - break; - - case RL_RED: - pLEDLabel->setPixmap ( BitmCubeRoundRed ); - break; - } - eCurLightColor = eNewColor; - } -} diff --git a/src/protocol.cpp b/src/protocol.cpp index 4c33607f67..0e94b99c75 100755 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -4,7 +4,25 @@ * Author(s): * Volker Fischer * + ****************************************************************************** + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ +/* Protocol message definition --------------------------- @@ -33,6 +51,20 @@ MAIN FRAME +SPLIT MESSAGE CONTAINER +----------------------- + + +------------+------------------------+------------------+--------------+ + | 2 bytes ID | 1 byte number of parts | 1 byte split cnt | n bytes data | + +------------+------------------------+------------------+--------------+ + +- ID is the message ID of the message being split +- number of parts - total number of parts comprising the whole message +- split cnt - number within number total for this part of the message +- data - subset of the data part of the original message being split + + + MESSAGES (with connection) -------------------------- @@ -144,9 +176,9 @@ MESSAGES (with connection) ... ------------------+-----------------------+ ... ... 4 bytes sam rate | 2 bytes audiocod type | ... ... ------------------+-----------------------+ ... - ... -----------------+----------------------+ - ... 2 bytes version | 4 bytes audiocod arg | - ... -----------------+----------------------+ + ... ---------------+----------------------+ + ... 2 bytes flags | 4 bytes audiocod arg | + ... ---------------+----------------------+ - "base netw size": length of the base network packet (frame) in bytes - "block size fact": block size factor @@ -158,8 +190,9 @@ MESSAGES (with connection) - 1: CELT - 2: OPUS - 3: OPUS64 - - "version": version of the audio coder, if not used this value - shall be set to 0 + - "flags": flags indicating network properties: + - 0: none + - 1: WITH_COUNTER (a packet counter is added to the audio packet) - "audiocod arg": argument for the audio coder, if not used this value shall be set to 0 @@ -169,20 +202,21 @@ MESSAGES (with connection) note: does not have any data -> n = 0 -- PROTMESSID_LICENCE_REQUIRED: Licence required to connect to the server +- PROTMESSID_REQ_SPLIT_MESS_SUPPORT: Request split message support - +---------------------+ - | 1 byte licence type | - +---------------------+ + note: does not have any data -> n = 0 -- PROTMESSID_REQ_CHANNEL_LEVEL_LIST: Opt in or out of the channel level list +- PROTMESSID_SPLIT_MESS_SUPPORTED: Split messages are supported - +---------------+ - | 1 byte option | - +---------------+ + note: does not have any data -> n = 0 - option is boolean, true to opt in, false to opt out + +- PROTMESSID_LICENCE_REQUIRED: Licence required to connect to the server + + +---------------------+ + | 1 byte licence type | + +---------------------+ - PROTMESSID_VERSION_AND_OS: Version number and operating system @@ -198,6 +232,17 @@ MESSAGES (with connection) note: does not have any data -> n = 0 +- PROTMESSID_RECORDER_STATE: notifies of changes in the server jam recorder state + + +--------------+ + | 1 byte state | + +--------------+ + + state is a value from the enum ERecorderState: + - 0 undefined (not used by protocol messages) + - tbc + + CONNECTION LESS MESSAGES ------------------------ @@ -239,9 +284,9 @@ CONNECTION LESS MESSAGES ... ------------------+----------------------------------+ ... ... 2 bytes number n | n bytes UTF-8 string server name | ... ... ------------------+----------------------------------+ ... - ... ------------------+---------------------------------------------+ ... - ... 2 bytes number n | n bytes UTF-8 string server interal address | ... - ... ------------------+---------------------------------------------+ ... + ... ------------------+----------------------------------------------+ ... + ... 2 bytes number n | n bytes UTF-8 string server internal address | ... + ... ------------------+----------------------------------------------+ ... ... ------------------+---------------------------+ ... 2 bytes number n | n bytes UTF-8 string city | ... ------------------+---------------------------+ @@ -253,13 +298,21 @@ CONNECTION LESS MESSAGES - "is permanent" is a flag which indicates if the server is permanent online or not. If this value is any value <> 0 indicates that the server is permanent online. - - "server interal address" represents the IPv4 address as a dotted quad to + - "server internal address" represents the IPv4 address as a dotted quad to be used by clients with the same external IP address as the server. NOTE: In the PROTMESSID_CLM_SERVER_LIST list, this field will be empty as only the initial IP address should be used by the client. Where necessary, that value will contain the server internal address. +- PROTMESSID_CLM_REGISTER_SERVER_EX: Register a server, providing extended server + information + + +--------------------------------+-------------------------------+ + | PROTMESSID_CLM_REGISTER_SERVER | PROTMESSID_CLM_VERSION_AND_OS | + +--------------------------------+-------------------------------+ + + - PROTMESSID_CLM_UNREGISTER_SERVER: Unregister a server note: does not have any data -> n = 0 @@ -277,6 +330,18 @@ CONNECTION LESS MESSAGES of the PROTMESSID_CLM_REGISTER_SERVER message is used +- PROTMESSID_CLM_RED_SERVER_LIST: Reduced server list message (to have less UDP fragmentation) + + for each registered server append following data: + + +--------------------+------------------------------+ ... + | 4 bytes IP address | 2 bytes server internal port | ... + +--------------------+------------------------------+ ... + ... -----------------+----------------------------------+ + ... 1 byte number n | n bytes UTF-8 string server name | + ... -----------------+----------------------------------+ + + - PROTMESSID_CLM_REQ_SERVER_LIST: Request server list note: does not have any data -> n = 0 @@ -329,7 +394,7 @@ CONNECTION LESS MESSAGES n is number of connected clients the values are the maximum channel levels for a client frame converted - to the range of CMultiColorLEDBar in 4 bits, two entries per byte + to the range of CLevelMeter in 4 bits, two entries per byte with the earlier channel in the lower half of the byte where an odd number of clients is connected, there will be four unused @@ -352,6 +417,8 @@ CONNECTION LESS MESSAGES Values of ESvrRegResult: 0 - success 1 - failed due to central server list being full + 2 - your server version is too old + 3 - registration requirements not fulfilled Note: the central server may send this message in response to a PROTMESSID_CLM_REGISTER_SERVER request. @@ -359,25 +426,7 @@ CONNECTION LESS MESSAGES five times for one registration request at 500ms intervals. Beyond this, it should "ping" every 15 minutes (standard re-registration timeout). - - - ****************************************************************************** - * - * 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 - * -\******************************************************************************/ +*/ #include "protocol.h" @@ -385,12 +434,15 @@ CONNECTION LESS MESSAGES /* Implementation *************************************************************/ CProtocol::CProtocol() { + // allocate worst case memory for split part messages + vecbySplitMessageStorage.Init ( MAX_SIZE_BYTES_NETW_BUF ); + Reset(); // Connections ------------------------------------------------------------- - QObject::connect ( &TimerSendMess, SIGNAL ( timeout() ), - this, SLOT ( OnTimerSendMess() ) ); + QObject::connect ( &TimerSendMess, &QTimer::timeout, + this, &CProtocol::OnTimerSendMess ); } void CProtocol::Reset() @@ -398,9 +450,12 @@ void CProtocol::Reset() QMutexLocker locker ( &Mutex ); // prepare internal variables for initial protocol transfer - iCounter = 0; - iOldRecID = PROTMESSID_ILLEGAL; - iOldRecCnt = 0; + iCounter = 0; + iOldRecID = PROTMESSID_ILLEGAL; + iOldRecCnt = 0; + iSplitMessageCnt = 0; + iSplitMessageDataIndex = 0; + bSplitMessageSupported = false; // compatilibity to old versions // delete complete "send message queue" SendMessQueue.clear(); @@ -446,8 +501,19 @@ void CProtocol::SendMessage() vecMessage.Init ( SendMessQueue.front().vecMessage.Size() ); vecMessage = SendMessQueue.front().vecMessage; + // start time-out timer if not active + if ( !TimerSendMess.isActive() ) + { + TimerSendMess.start ( SEND_MESS_TIMEOUT_MS ); + } + bSendMess = true; } + else + { + // no message to send, stop timer + TimerSendMess.stop(); + } } Mutex.unlock(); @@ -455,17 +521,6 @@ void CProtocol::SendMessage() { // send message emit MessReadyForSending ( vecMessage ); - - // start time-out timer if not active - if ( !TimerSendMess.isActive() ) - { - TimerSendMess.start ( SEND_MESS_TIMEOUT_MS ); - } - } - else - { - // no message to send, stop timer - TimerSendMess.stop(); } } @@ -474,22 +529,74 @@ void CProtocol::CreateAndSendMessage ( const int iID, { CVector vecNewMessage; int iCurCounter; + const int iDataLen = vecData.Size(); - Mutex.lock(); + // check if message has to be split because it is too large + if ( bSplitMessageSupported && ( iDataLen > MESS_SPLIT_PART_SIZE_BYTES ) ) { - // store current counter value - iCurCounter = iCounter; + CVector vecNewSplitMessage; + int iStartIndexInData = 0; // init index - // increase counter (wraps around automatically) - iCounter++; + // calculate the number of split parts + const int iNumParts = static_cast ( + std::ceil ( static_cast ( iDataLen ) / MESS_SPLIT_PART_SIZE_BYTES ) ); + + for ( int iSplitCnt = 0; iSplitCnt < iNumParts; iSplitCnt++ ) + { + // the split part size may be smaller for the last part + int iCurPartSize = MESS_SPLIT_PART_SIZE_BYTES; + + if ( iDataLen - iStartIndexInData < MESS_SPLIT_PART_SIZE_BYTES ) + { + iCurPartSize = iDataLen - iStartIndexInData; + } + + GenSplitMessageContainer ( vecNewSplitMessage, + iID, + iNumParts, + iSplitCnt, + vecData, + iStartIndexInData, + iCurPartSize ); + + // increment the start index of the source data by the last part size + iStartIndexInData += iCurPartSize; + + Mutex.lock(); + { + // store current counter value + iCurCounter = iCounter; + + // increase counter (wraps around automatically) + iCounter++; + } + Mutex.unlock(); + + // build complete message + GenMessageFrame ( vecNewMessage, iCurCounter, PROTMESSID_SPECIAL_SPLIT_MESSAGE, vecNewSplitMessage ); + + // enqueue message + EnqueueMessage ( vecNewMessage, iCurCounter, PROTMESSID_SPECIAL_SPLIT_MESSAGE ); + } } - Mutex.unlock(); + else + { + Mutex.lock(); + { + // store current counter value + iCurCounter = iCounter; - // build complete message - GenMessageFrame ( vecNewMessage, iCurCounter, iID, vecData ); + // increase counter (wraps around automatically) + iCounter++; + } + Mutex.unlock(); + + // build complete message + GenMessageFrame ( vecNewMessage, iCurCounter, iID, vecData ); - // enqueue message - EnqueueMessage ( vecNewMessage, iCurCounter, iID ); + // enqueue message + EnqueueMessage ( vecNewMessage, iCurCounter, iID ); + } } void CProtocol::CreateAndImmSendAcknMess ( const int& iID, @@ -523,16 +630,10 @@ void CProtocol::CreateAndImmSendConLessMessage ( const int iID, emit CLMessReadyForSending ( InetAddr, vecNewMessage ); } -bool CProtocol::ParseMessageBody ( const CVector& vecbyMesBodyData, +void CProtocol::ParseMessageBody ( const CVector& vecbyMesBodyData, const int iRecCounter, const int iRecID ) { -/* - return code: false -> ok; true -> error -*/ - bool bRet = false; - bool bSendNextMess; - /* // TEST channel implementation: randomly delete protocol messages (50 % loss) if ( rand() < ( RAND_MAX / 2 ) ) return false; @@ -556,15 +657,20 @@ if ( rand() < ( RAND_MAX / 2 ) ) return false; // special treatment for acknowledge messages if ( iRecID == PROTMESSID_ACKN ) { + // check size + if ( vecbyMesBodyData.Size() != 2 ) + { + return; + } + // extract data from stream and emit signal for received value - int iPos = 0; - const int iData = - static_cast ( GetValFromStream ( vecbyMesBodyData, iPos, 2 ) ); + bool bSendNextMess = false; + int iPos = 0; + const int iData = static_cast ( GetValFromStream ( vecbyMesBodyData, iPos, 2 ) ); Mutex.lock(); { // check if this is the correct acknowledgment - bSendNextMess = false; if ( !SendMessQueue.empty() ) { if ( ( SendMessQueue.front().iCnt == iRecCounter ) && @@ -587,72 +693,155 @@ if ( rand() < ( RAND_MAX / 2 ) ) return false; } else { - // check which type of message we received and do action - switch ( iRecID ) + CVector vecbyMesBodyDataSplitMess; + int iRecIDModified = iRecID; + bool bEvaluateMessage = false; + + // check for special ID first + if ( iRecID == PROTMESSID_SPECIAL_SPLIT_MESSAGE ) + { + // Split message management ------------------------------------ + int iOriginalID; + int iReceivedNumParts; + int iReceivedSplitCnt; + int iCurPartSize; + + if ( !ParseSplitMessageContainer ( vecbyMesBodyData, + vecbySplitMessageStorage, + iSplitMessageDataIndex, + iOriginalID, + iReceivedNumParts, + iReceivedSplitCnt, + iCurPartSize ) ) + { + // consistency checks + if ( ( iSplitMessageCnt != iReceivedSplitCnt ) || + ( iSplitMessageCnt >= iReceivedNumParts ) || + ( iSplitMessageCnt >= MAX_NUM_MESS_SPLIT_PARTS ) ) + { + // in case of an error we reset the split message counter + iSplitMessageCnt = 0; + iSplitMessageDataIndex = 0; + } + else + { + // update counter and message data index since we have received a valid new part + iSplitMessageCnt++; + iSplitMessageDataIndex += iCurPartSize; + + // check if the split part messages was completely received + if ( iSplitMessageCnt == iReceivedNumParts ) + { + // the split message is completely received, copy data for parsing + vecbyMesBodyDataSplitMess.Init ( iSplitMessageDataIndex ); + + std::copy ( vecbySplitMessageStorage.begin(), + vecbySplitMessageStorage.begin() + iSplitMessageDataIndex, + vecbyMesBodyDataSplitMess.begin() ); + + // the received ID is still PROTMESSID_SPECIAL_SPLIT_MESSAGE, set it to + // the ID of the original reconstructed split message now + iRecIDModified = iOriginalID; + + // the complete split message was reconstructed, reset the counter for + // the next split message + iSplitMessageCnt = 0; + iSplitMessageDataIndex = 0; + bEvaluateMessage = true; + } + } + } + } + else { - case PROTMESSID_JITT_BUF_SIZE: - bRet = EvaluateJitBufMes ( vecbyMesBodyData ); - break; + // a non-split message was received, reset split message counter and directly evaluate message + iSplitMessageCnt = 0; + iSplitMessageDataIndex = 0; + bEvaluateMessage = true; + } - case PROTMESSID_REQ_JITT_BUF_SIZE: - bRet = EvaluateReqJitBufMes(); - break; + if ( bEvaluateMessage ) + { + // use a reference to either the original data vector or the reconstructed + // split message to avoid unnecessary copying + const CVector& vecbyMesBodyDataRef = + ( iRecID == PROTMESSID_SPECIAL_SPLIT_MESSAGE ) ? vecbyMesBodyDataSplitMess : vecbyMesBodyData; + + // check which type of message we received and do action + switch ( iRecIDModified ) + { + case PROTMESSID_JITT_BUF_SIZE: + EvaluateJitBufMes ( vecbyMesBodyDataRef ); + break; + + case PROTMESSID_REQ_JITT_BUF_SIZE: + EvaluateReqJitBufMes(); + break; - case PROTMESSID_CLIENT_ID: - bRet = EvaluateClientIDMes ( vecbyMesBodyData ); - break; + case PROTMESSID_CLIENT_ID: + EvaluateClientIDMes ( vecbyMesBodyDataRef ); + break; - case PROTMESSID_CHANNEL_GAIN: - bRet = EvaluateChanGainMes ( vecbyMesBodyData ); - break; + case PROTMESSID_CHANNEL_GAIN: + EvaluateChanGainMes ( vecbyMesBodyDataRef ); + break; - case PROTMESSID_CHANNEL_PAN: - bRet = EvaluateChanPanMes ( vecbyMesBodyData ); - break; + case PROTMESSID_CHANNEL_PAN: + EvaluateChanPanMes ( vecbyMesBodyDataRef ); + break; - case PROTMESSID_MUTE_STATE_CHANGED: - bRet = EvaluateMuteStateHasChangedMes ( vecbyMesBodyData ); - break; + case PROTMESSID_MUTE_STATE_CHANGED: + EvaluateMuteStateHasChangedMes ( vecbyMesBodyDataRef ); + break; - case PROTMESSID_CONN_CLIENTS_LIST: - bRet = EvaluateConClientListMes ( vecbyMesBodyData ); - break; + case PROTMESSID_CONN_CLIENTS_LIST: + EvaluateConClientListMes ( vecbyMesBodyDataRef ); + break; - case PROTMESSID_REQ_CONN_CLIENTS_LIST: - bRet = EvaluateReqConnClientsList(); - break; + case PROTMESSID_REQ_CONN_CLIENTS_LIST: + EvaluateReqConnClientsList(); + break; - case PROTMESSID_CHANNEL_INFOS: - bRet = EvaluateChanInfoMes ( vecbyMesBodyData ); - break; + case PROTMESSID_CHANNEL_INFOS: + EvaluateChanInfoMes ( vecbyMesBodyDataRef ); + break; - case PROTMESSID_REQ_CHANNEL_INFOS: - bRet = EvaluateReqChanInfoMes(); - break; + case PROTMESSID_REQ_CHANNEL_INFOS: + EvaluateReqChanInfoMes(); + break; - case PROTMESSID_CHAT_TEXT: - bRet = EvaluateChatTextMes ( vecbyMesBodyData ); - break; + case PROTMESSID_CHAT_TEXT: + EvaluateChatTextMes ( vecbyMesBodyDataRef ); + break; - case PROTMESSID_NETW_TRANSPORT_PROPS: - bRet = EvaluateNetwTranspPropsMes ( vecbyMesBodyData ); - break; + case PROTMESSID_NETW_TRANSPORT_PROPS: + EvaluateNetwTranspPropsMes ( vecbyMesBodyDataRef ); + break; - case PROTMESSID_REQ_NETW_TRANSPORT_PROPS: - bRet = EvaluateReqNetwTranspPropsMes(); - break; + case PROTMESSID_REQ_NETW_TRANSPORT_PROPS: + EvaluateReqNetwTranspPropsMes(); + break; - case PROTMESSID_LICENCE_REQUIRED: - bRet = EvaluateLicenceRequiredMes ( vecbyMesBodyData ); - break; + case PROTMESSID_REQ_SPLIT_MESS_SUPPORT: + EvaluateReqSplitMessSupportMes(); + break; - case PROTMESSID_REQ_CHANNEL_LEVEL_LIST: - bRet = EvaluateReqChannelLevelListMes ( vecbyMesBodyData ); - break; + case PROTMESSID_SPLIT_MESS_SUPPORTED: + EvaluateSplitMessSupportedMes(); + break; - case PROTMESSID_VERSION_AND_OS: - bRet = EvaluateVersionAndOSMes ( vecbyMesBodyData ); - break; + case PROTMESSID_LICENCE_REQUIRED: + EvaluateLicenceRequiredMes ( vecbyMesBodyDataRef ); + break; + + case PROTMESSID_VERSION_AND_OS: + EvaluateVersionAndOSMes ( vecbyMesBodyDataRef ); + break; + + case PROTMESSID_RECORDER_STATE: + EvaluateRecorderStateMes ( vecbyMesBodyDataRef ); + break; + } } // immediately send acknowledge message @@ -664,96 +853,88 @@ if ( rand() < ( RAND_MAX / 2 ) ) return false; iOldRecCnt = iRecCounter; } } - - return bRet; } -bool CProtocol::ParseConnectionLessMessageBody ( const CVector& vecbyMesBodyData, +void CProtocol::ParseConnectionLessMessageBody ( const CVector& vecbyMesBodyData, const int iRecID, const CHostAddress& InetAddr ) { -/* - return code: false -> ok; true -> error -*/ - bool bRet = false; - /* // TEST channel implementation: randomly delete protocol messages (50 % loss) if ( rand() < ( RAND_MAX / 2 ) ) return false; */ - if ( IsConnectionLessMessageID ( iRecID ) ) + // check which type of message we received and do action + switch ( iRecID ) { - // check which type of message we received and do action - switch ( iRecID ) - { - case PROTMESSID_CLM_PING_MS: - bRet = EvaluateCLPingMes ( InetAddr, vecbyMesBodyData ); - break; + case PROTMESSID_CLM_PING_MS: + EvaluateCLPingMes ( InetAddr, vecbyMesBodyData ); + break; - case PROTMESSID_CLM_PING_MS_WITHNUMCLIENTS: - bRet = EvaluateCLPingWithNumClientsMes ( InetAddr, vecbyMesBodyData ); - break; + case PROTMESSID_CLM_PING_MS_WITHNUMCLIENTS: + EvaluateCLPingWithNumClientsMes ( InetAddr, vecbyMesBodyData ); + break; - case PROTMESSID_CLM_SERVER_FULL: - bRet = EvaluateCLServerFullMes(); - break; + case PROTMESSID_CLM_SERVER_FULL: + EvaluateCLServerFullMes(); + break; - case PROTMESSID_CLM_SERVER_LIST: - bRet = EvaluateCLServerListMes ( InetAddr, vecbyMesBodyData ); - break; + case PROTMESSID_CLM_SERVER_LIST: + EvaluateCLServerListMes ( InetAddr, vecbyMesBodyData ); + break; - case PROTMESSID_CLM_REQ_SERVER_LIST: - bRet = EvaluateCLReqServerListMes ( InetAddr ); - break; + case PROTMESSID_CLM_RED_SERVER_LIST: + EvaluateCLRedServerListMes ( InetAddr, vecbyMesBodyData ); + break; - case PROTMESSID_CLM_SEND_EMPTY_MESSAGE: - bRet = EvaluateCLSendEmptyMesMes ( vecbyMesBodyData ); - break; + case PROTMESSID_CLM_REQ_SERVER_LIST: + EvaluateCLReqServerListMes ( InetAddr ); + break; - case PROTMESSID_CLM_REGISTER_SERVER: - bRet = EvaluateCLRegisterServerMes ( InetAddr, vecbyMesBodyData ); - break; + case PROTMESSID_CLM_SEND_EMPTY_MESSAGE: + EvaluateCLSendEmptyMesMes ( vecbyMesBodyData ); + break; - case PROTMESSID_CLM_UNREGISTER_SERVER: - bRet = EvaluateCLUnregisterServerMes ( InetAddr ); - break; + case PROTMESSID_CLM_REGISTER_SERVER: + EvaluateCLRegisterServerMes ( InetAddr, vecbyMesBodyData ); + break; - case PROTMESSID_CLM_DISCONNECTION: - bRet = EvaluateCLDisconnectionMes ( InetAddr ); - break; + case PROTMESSID_CLM_REGISTER_SERVER_EX: + EvaluateCLRegisterServerExMes ( InetAddr, vecbyMesBodyData ); + break; - case PROTMESSID_CLM_VERSION_AND_OS: - bRet = EvaluateCLVersionAndOSMes ( InetAddr, vecbyMesBodyData ); - break; + case PROTMESSID_CLM_UNREGISTER_SERVER: + EvaluateCLUnregisterServerMes ( InetAddr ); + break; - case PROTMESSID_CLM_REQ_VERSION_AND_OS: - bRet = EvaluateCLReqVersionAndOSMes ( InetAddr ); - break; + case PROTMESSID_CLM_DISCONNECTION: + EvaluateCLDisconnectionMes ( InetAddr ); + break; - case PROTMESSID_CLM_CONN_CLIENTS_LIST: - bRet = EvaluateCLConnClientsListMes ( InetAddr, vecbyMesBodyData ); - break; + case PROTMESSID_CLM_VERSION_AND_OS: + EvaluateCLVersionAndOSMes ( InetAddr, vecbyMesBodyData ); + break; - case PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST: - bRet = EvaluateCLReqConnClientsListMes ( InetAddr ); - break; + case PROTMESSID_CLM_REQ_VERSION_AND_OS: + EvaluateCLReqVersionAndOSMes ( InetAddr ); + break; - case PROTMESSID_CLM_CHANNEL_LEVEL_LIST: - bRet = EvaluateCLChannelLevelListMes ( InetAddr, vecbyMesBodyData ); - break; + case PROTMESSID_CLM_CONN_CLIENTS_LIST: + EvaluateCLConnClientsListMes ( InetAddr, vecbyMesBodyData ); + break; - case PROTMESSID_CLM_REGISTER_SERVER_RESP: - bRet = EvaluateCLRegisterServerResp ( InetAddr, vecbyMesBodyData ); - break; - } - } - else - { - bRet = true; // return error code - } + case PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST: + EvaluateCLReqConnClientsListMes ( InetAddr ); + break; + + case PROTMESSID_CLM_CHANNEL_LEVEL_LIST: + EvaluateCLChannelLevelListMes ( InetAddr, vecbyMesBodyData ); + break; - return bRet; + case PROTMESSID_CLM_REGISTER_SERVER_RESP: + EvaluateCLRegisterServerResp ( InetAddr, vecbyMesBodyData ); + break; + } } @@ -842,7 +1023,7 @@ bool CProtocol::EvaluateClientIDMes ( const CVector& vecData ) return false; // no error } -void CProtocol::CreateChanGainMes ( const int iChanID, const double dGain ) +void CProtocol::CreateChanGainMes ( const int iChanID, const float fGain ) { CVector vecData ( 3 ); // 3 bytes of data int iPos = 0; // init position pointer @@ -852,7 +1033,7 @@ void CProtocol::CreateChanGainMes ( const int iChanID, const double dGain ) PutValOnStream ( vecData, iPos, static_cast ( iChanID ), 1 ); // actual gain, we convert from double with range 0..1 to integer - const int iCurGain = static_cast ( dGain * ( 1 << 15 ) ); + const int iCurGain = static_cast ( fGain * ( 1 << 15 ) ); PutValOnStream ( vecData, iPos, static_cast ( iCurGain ), 2 ); @@ -876,15 +1057,15 @@ bool CProtocol::EvaluateChanGainMes ( const CVector& vecData ) const int iData = static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); // we convert the gain from integer to double with range 0..1 - const double dNewGain = static_cast ( iData ) / ( 1 << 15 ); + const float fNewGain = static_cast ( iData ) / ( 1 << 15 ); // invoke message action - emit ChangeChanGain ( iCurID, dNewGain ); + emit ChangeChanGain ( iCurID, fNewGain ); return false; // no error } -void CProtocol::CreateChanPanMes ( const int iChanID, const double dPan ) +void CProtocol::CreateChanPanMes ( const int iChanID, const float fPan ) { CVector vecData ( 3 ); // 3 bytes of data int iPos = 0; // init position pointer @@ -893,8 +1074,8 @@ void CProtocol::CreateChanPanMes ( const int iChanID, const double dPan ) // channel ID PutValOnStream ( vecData, iPos, static_cast ( iChanID ), 1 ); - // actual gain, we convert from double with range 0..1 to integer - const int iCurPan = static_cast ( dPan * ( 1 << 15 ) ); + // actual pan, we convert from double with range 0..1 to integer + const int iCurPan = static_cast ( fPan * ( 1 << 15 ) ); PutValOnStream ( vecData, iPos, static_cast ( iCurPan ), 2 ); @@ -917,11 +1098,11 @@ bool CProtocol::EvaluateChanPanMes ( const CVector &vecData ) // pan (read integer value) const int iData = static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - // we convert the gain from integer to double with range 0..1 - const double dNewPan = static_cast ( iData ) / ( 1 << 15 ); + // we convert the pan from integer to double with range 0..1 + const float fNewPan = static_cast ( iData ) / ( 1 << 15 ); // invoke message action - emit ChangeChanPan ( iCurID, dNewPan ); + emit ChangeChanPan ( iCurID, fNewPan ); return false; // no error } @@ -1286,8 +1467,8 @@ void CProtocol::CreateNetwTranspPropsMes ( const CNetworkTransportProps& NetTrPr // audio coding type (2 bytes) PutValOnStream ( vecData, iPos, static_cast ( NetTrProps.eAudioCodingType ), 2 ); - // version (2 bytes) - PutValOnStream ( vecData, iPos, static_cast ( NetTrProps.iVersion ), 2 ); + // flags (2 bytes) + PutValOnStream ( vecData, iPos, static_cast ( NetTrProps.eFlags ), 2 ); // argument for the audio coder (4 bytes) PutValOnStream ( vecData, iPos, static_cast ( NetTrProps.iAudioCodingArg ), 4 ); @@ -1307,7 +1488,7 @@ bool CProtocol::EvaluateNetwTranspPropsMes ( const CVector& vecData ) 1 /* num chan */ + 4 /* sam rate */ + 2 /* audiocod type */ + - 2 /* version */ + + 2 /* flags */ + 4 /* audiocod arg */; // check size @@ -1369,9 +1550,9 @@ bool CProtocol::EvaluateNetwTranspPropsMes ( const CVector& vecData ) ReceivedNetwTranspProps.eAudioCodingType = static_cast ( iRecCodingType ); - // version (2 bytes) - ReceivedNetwTranspProps.iVersion = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + // flags (2 bytes) + ReceivedNetwTranspProps.eFlags = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); // argument for the audio coder (4 bytes) ReceivedNetwTranspProps.iAudioCodingArg = @@ -1397,6 +1578,34 @@ bool CProtocol::EvaluateReqNetwTranspPropsMes() return false; // no error } +void CProtocol::CreateReqSplitMessSupportMes() +{ + CreateAndSendMessage ( PROTMESSID_REQ_SPLIT_MESS_SUPPORT, + CVector ( 0 ) ); +} + +bool CProtocol::EvaluateReqSplitMessSupportMes() +{ + // invoke message action + emit ReqSplitMessSupport(); + + return false; // no error +} + +void CProtocol::CreateSplitMessSupportedMes() +{ + CreateAndSendMessage ( PROTMESSID_SPLIT_MESS_SUPPORTED, + CVector ( 0 ) ); +} + +bool CProtocol::EvaluateSplitMessSupportedMes() +{ + // invoke message action + emit SplitMessSupported(); + + return false; // no error +} + void CProtocol::CreateLicenceRequiredMes ( const ELicenceType eLicenceType ) { CVector vecData ( 1 ); // 1 bytes of data @@ -1439,38 +1648,15 @@ void CProtocol::CreateOpusSupportedMes() CreateAndSendMessage ( PROTMESSID_OPUS_SUPPORTED, CVector ( 0 ) ); } -void CProtocol::CreateReqChannelLevelListMes ( const bool bRCL ) +// TODO needed for compatibility to old servers >= 3.4.6 and <= 3.5.12 +void CProtocol::CreateReqChannelLevelListMes() { CVector vecData ( 1 ); // 1 byte of data int iPos = 0; // init position pointer - PutValOnStream ( vecData, iPos, - static_cast ( bRCL ), 1 ); - - CreateAndSendMessage ( PROTMESSID_REQ_CHANNEL_LEVEL_LIST, vecData ); -} - -bool CProtocol::EvaluateReqChannelLevelListMes ( const CVector& vecData ) -{ - int iPos = 0; // init position pointer - - // check size - if ( vecData.Size() != 1 ) - { - return true; // return error code - } - - // extract opt in / out for channel levels - uint32_t val = GetValFromStream ( vecData, iPos, 1 ); - - if ( val != 0 && val != 1 ) - { - return true; // return error code - } - // invoke message action - emit ReqChannelLevelList ( static_cast ( val ) ); + PutValOnStream ( vecData, iPos, static_cast ( true ), 1 ); - return false; // no error + CreateAndSendMessage ( PROTMESSID_REQ_CHANNEL_LEVEL_LIST, vecData ); } void CProtocol::CreateVersionAndOSMes() @@ -1538,6 +1724,46 @@ bool CProtocol::EvaluateVersionAndOSMes ( const CVector& vecData ) return false; // no error } +void CProtocol::CreateRecorderStateMes ( const ERecorderState eRecorderState ) +{ + CVector vecData ( 1 ); // 1 byte of data + int iPos = 0; // init position pointer + + // build data vector + // server jam recorder state (1 byte) + PutValOnStream ( vecData, iPos, static_cast ( eRecorderState ), 1 ); + + CreateAndSendMessage ( PROTMESSID_RECORDER_STATE, vecData ); +} + +bool CProtocol::EvaluateRecorderStateMes(const CVector& vecData) +{ + int iPos = 0; // init position pointer + + // check size + if ( vecData.Size() != 1 ) + { + return true; // return error code + } + + // server jam recorder state (1 byte) + const int iRecorderState = + static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + + // note that RS_UNDEFINED is only internally used + if ( ( iRecorderState != RS_NOT_INITIALISED ) && + ( iRecorderState != RS_NOT_ENABLED ) && + ( iRecorderState != RS_RECORDING ) ) + { + return true; + } + + // invoke message action + emit RecorderStateReceived ( static_cast ( iRecorderState ) ); + + return false; // no error +} + // Connection less messages ---------------------------------------------------- void CProtocol::CreateCLPingMes ( const CHostAddress& InetAddr, const int iMs ) @@ -1757,6 +1983,162 @@ bool CProtocol::EvaluateCLRegisterServerMes ( const CHostAddress& InetAddr, return false; // no error } +void CProtocol::CreateCLRegisterServerExMes ( const CHostAddress& InetAddr, + const CHostAddress& LInetAddr, + const CServerCoreInfo& ServerInfo ) +{ + int iPos = 0; // init position pointer + + // convert server info strings to utf-8 + const QByteArray strUTF8LInetAddr = LInetAddr.InetAddr.toString().toUtf8(); + const QByteArray strUTF8Name = ServerInfo.strName.toUtf8(); + const QByteArray strUTF8City = ServerInfo.strCity.toUtf8(); + const QByteArray strUTF8Version = QString ( VERSION ).toUtf8(); + + // size of current message body + const int iEntrLen = + 2 /* server internal port number */ + + 2 /* country */ + + 1 /* maximum number of connected clients */ + + 1 /* is permanent flag */ + + 2 /* name utf-8 string size */ + strUTF8Name.size() + + 2 /* server internal address utf-8 string size */ + strUTF8LInetAddr.size() + + 2 /* city utf-8 string size */ + strUTF8City.size() + + 1 /* operating system */ + + 2 /* version utf-8 string size */ + strUTF8Version.size(); + + // build data vector + CVector vecData ( iEntrLen ); + + // port number (2 bytes) + PutValOnStream ( vecData, iPos, static_cast ( LInetAddr.iPort ), 2 ); + + // country (2 bytes) + PutValOnStream ( vecData, iPos, static_cast ( ServerInfo.eCountry ), 2 ); + + // maximum number of connected clients (1 byte) + PutValOnStream ( vecData, iPos, static_cast ( ServerInfo.iMaxNumClients ), 1 ); + + // "is permanent" flag (1 byte) + PutValOnStream ( vecData, iPos, static_cast ( ServerInfo.bPermanentOnline ), 1 ); + + // name + PutStringUTF8OnStream ( vecData, iPos, strUTF8Name ); + + // server internal address (formerly unused topic) + PutStringUTF8OnStream ( vecData, iPos, strUTF8LInetAddr ); + + // city + PutStringUTF8OnStream ( vecData, iPos, strUTF8City ); + + // operating system (1 byte) + PutValOnStream ( vecData, iPos, + static_cast ( COSUtil::GetOperatingSystem() ), 1 ); + + // version + PutStringUTF8OnStream ( vecData, iPos, strUTF8Version ); + + CreateAndImmSendConLessMessage ( PROTMESSID_CLM_REGISTER_SERVER_EX, + vecData, + InetAddr ); +} + +bool CProtocol::EvaluateCLRegisterServerExMes ( const CHostAddress& InetAddr, + const CVector& vecData ) +{ + int iPos = 0; // init position pointer + const int iDataLen = vecData.Size(); + QString sLocHost; // temp string for server internal address + CHostAddress LInetAddr; + CServerCoreInfo RecServerInfo; + + // check size (the first 6 bytes) + if ( iDataLen < 6 ) + { + return true; // return error code + } + + // port number (2 bytes) + LInetAddr.iPort = static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + // country (2 bytes) + RecServerInfo.eCountry = static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + // maximum number of connected clients (1 byte) + RecServerInfo.iMaxNumClients = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + + // "is permanent" flag (1 byte) + RecServerInfo.bPermanentOnline = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + + // server name + if ( GetStringFromStream ( vecData, + iPos, + MAX_LEN_SERVER_NAME, + RecServerInfo.strName ) ) + { + return true; // return error code + } + + // server internal address + if ( GetStringFromStream ( vecData, + iPos, + MAX_LEN_IP_ADDRESS, + sLocHost ) ) + { + return true; // return error code + } + + if ( sLocHost.isEmpty() ) + { + // old server, empty "topic", register as local host + LInetAddr.InetAddr.setAddress ( QHostAddress::LocalHost ); + } + else if ( !LInetAddr.InetAddr.setAddress ( sLocHost ) ) + { + return true; // return error code + } + + // server city + if ( GetStringFromStream ( vecData, + iPos, + MAX_LEN_SERVER_CITY, + RecServerInfo.strCity ) ) + { + return true; // return error code + } + + // check size (the next 1 byte) + if ( iDataLen < iPos + 1 ) + { + return true; // return error code + } + + // operating system (1 byte) + const COSUtil::EOpSystemType eOSType = + static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + + // version text + QString strVersion; + if ( GetStringFromStream ( vecData, + iPos, + MAX_LEN_VERSION_TEXT, + strVersion ) ) + { + return true; // return error code + } + + // check size: all data is read, the position must now be at the end + if ( iPos != iDataLen ) + { + return true; // return error code + } + + // invoke message action + emit CLRegisterServerExReceived ( InetAddr, LInetAddr, RecServerInfo, eOSType, strVersion ); + + return false; // no error +} + void CProtocol::CreateCLUnregisterServerMes ( const CHostAddress& InetAddr ) { CreateAndImmSendConLessMessage ( PROTMESSID_CLM_UNREGISTER_SERVER, @@ -1922,6 +2304,103 @@ bool CProtocol::EvaluateCLServerListMes ( const CHostAddress& InetAddr, return false; // no error } +void CProtocol::CreateCLRedServerListMes ( const CHostAddress& InetAddr, + const CVector vecServerInfo ) +{ + const int iNumServers = vecServerInfo.Size(); + + // build data vector + CVector vecData ( 0 ); + int iPos = 0; // init position pointer + + for ( int i = 0; i < iNumServers; i++ ) + { + // convert server list strings to utf-8 + const QByteArray strUTF8Name = vecServerInfo[i].strName.toUtf8(); + + // size of current list entry + const int iCurListEntrLen = + 4 /* IP address */ + + 2 /* port number */ + + 1 /* name utf-8 string size */ + strUTF8Name.size(); + + // make space for new data + vecData.Enlarge ( iCurListEntrLen ); + + // IP address (4 bytes) + // note the Server List manager has put the internal details in HostAddr where required + PutValOnStream ( vecData, iPos, static_cast ( + vecServerInfo[i].HostAddr.InetAddr.toIPv4Address() ), 4 ); + + // port number (2 bytes) + // note the Server List manager has put the internal details in HostAddr where required + PutValOnStream ( vecData, iPos, + static_cast ( vecServerInfo[i].HostAddr.iPort ), 2 ); + + // name (note that the string length indicator is 1 in this special case) + PutStringUTF8OnStream ( vecData, iPos, strUTF8Name, 1 ); + } + + CreateAndImmSendConLessMessage ( PROTMESSID_CLM_RED_SERVER_LIST, + vecData, + InetAddr ); +} + +bool CProtocol::EvaluateCLRedServerListMes ( const CHostAddress& InetAddr, + const CVector& vecData ) +{ + int iPos = 0; // init position pointer + const int iDataLen = vecData.Size(); + CVector vecServerInfo ( 0 ); + + while ( iPos < iDataLen ) + { + // check size (the next 6 bytes) + if ( ( iDataLen - iPos ) < 6 ) + { + return true; // return error code + } + + // IP address (4 bytes) + const quint32 iIpAddr = static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); + + // port number (2 bytes) + const quint16 iPort = static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + // server name (note that the string length indicator is 1 in this special case) + QString strName; + if ( GetStringFromStream ( vecData, + iPos, + MAX_LEN_SERVER_NAME, + strName, + 1 ) ) + { + return true; // return error code + } + + // add server information to vector + vecServerInfo.Add ( + CServerInfo ( CHostAddress ( QHostAddress ( iIpAddr ), iPort ), + CHostAddress ( QHostAddress ( iIpAddr ), iPort ), + strName, + QLocale::AnyCountry, // set to any country since the information is not transmitted + "", // empty city name since the information is not transmitted + 0, // per definition: if max. num. client is zero, we ignore the value in the server list + false ) ); // assume not permanent since the information is not transmitted + } + + // check size: all data is read, the position must now be at the end + if ( iPos != iDataLen ) + { + return true; // return error code + } + + // invoke message action + emit CLRedServerListReceived ( InetAddr, vecServerInfo ); + + return false; // no error +} + void CProtocol::CreateCLReqServerListMes ( const CHostAddress& InetAddr ) { CreateAndImmSendConLessMessage ( PROTMESSID_CLM_REQ_SERVER_LIST, @@ -2314,10 +2793,19 @@ bool CProtocol::EvaluateCLRegisterServerResp ( const CHostAddress& InetAddr, return true; } - ESvrRegResult eResult = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + // server registration result (1 byte) + const int iSvrRegResult = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + + if ( ( iSvrRegResult != SRR_REGISTERED ) && + ( iSvrRegResult != SRR_CENTRAL_SVR_FULL ) && + ( iSvrRegResult != SRR_VERSION_TOO_OLD ) && + ( iSvrRegResult != SRR_NOT_FULFILL_REQIREMENTS ) ) + { + return true; + } // invoke message action - emit CLRegisterServerResp ( InetAddr, eResult ); + emit CLRegisterServerResp ( InetAddr, static_cast ( iSvrRegResult ) ); return false; // no error } @@ -2404,6 +2892,50 @@ bool CProtocol::ParseMessageFrame ( const CVector& vecbyData, return false; // no error } +bool CProtocol::ParseSplitMessageContainer ( const CVector& vecbyData, + CVector& vecbyMesBodyData, + const int iSplitMessageDataIndex, + int& iID, + int& iNumParts, + int& iSplitCnt, + int& iCurPartSize ) +{ + int iPos = 0; // init position pointer + const int iDataLen = vecbyData.Size(); + + // check size (the first 4 bytes) + if ( iDataLen < 4 ) + { + return true; // return error code + } + + // 2 bytes ID + iID = static_cast ( GetValFromStream ( vecbyData, iPos, 2 ) ); + + // 1 byte number of parts + iNumParts = static_cast ( GetValFromStream ( vecbyData, iPos, 1 ) ); + + // 1 byte split cnt + iSplitCnt = static_cast ( GetValFromStream ( vecbyData, iPos, 1 ) ); + + + // Extract actual data ----------------------------------------------------- + iCurPartSize = iDataLen - 4; + + // the memory must be allocated outside this function -> check the size + if ( vecbyMesBodyData.Size() < iSplitMessageDataIndex + iCurPartSize ) + { + return true; // return error code + } + + for ( int i = 0; i < iCurPartSize; i++ ) + { + vecbyMesBodyData[iSplitMessageDataIndex + i] = static_cast ( GetValFromStream ( vecbyData, iPos, 1 ) ); + } + + return false; // no error +} + uint32_t CProtocol::GetValFromStream ( const CVector& vecIn, int& iPos, const int iNumOfBytes ) @@ -2429,21 +2961,22 @@ uint32_t CProtocol::GetValFromStream ( const CVector& vecIn, bool CProtocol::GetStringFromStream ( const CVector& vecIn, int& iPos, const int iMaxStringLen, - QString& strOut ) + QString& strOut, + const int iNumberOfBytsLen ) { /* note: iPos is automatically incremented in this function */ const int iInLen = vecIn.Size(); - // check if at least two bytes are available - if ( ( iInLen - iPos ) < 2 ) + // check if at least iNumberOfBytsLen bytes are available + if ( ( iInLen - iPos ) < iNumberOfBytsLen ) { return true; // return error code } - // number of bytes for utf-8 string (2 bytes) - const int iStrUTF8Len = static_cast ( GetValFromStream ( vecIn, iPos, 2 ) ); + // number of bytes for utf-8 string (1 or 2 bytes) + const int iStrUTF8Len = static_cast ( GetValFromStream ( vecIn, iPos, iNumberOfBytsLen ) ); // (note that iPos was incremented by 2 in the above code!) if ( ( iInLen - iPos ) < iStrUTF8Len ) @@ -2526,6 +3059,39 @@ void CProtocol::GenMessageFrame ( CVector& vecOut, PutValOnStream ( vecOut, iCurPos, static_cast ( CRCObj.GetCRC() ), 2 ); } + +void CProtocol::GenSplitMessageContainer ( CVector& vecOut, + const int iID, + const int iNumParts, + const int iSplitCnt, + const CVector& vecData, + const int iStartIndexInData, + const int iLengthOfDataPart ) +{ + int iPos = 0; // init position pointer + + // total length of message + const int iTotLenByte = 4 + iLengthOfDataPart; + + // init message vector + vecOut.Init ( iTotLenByte ); + + // 2 bytes ID + PutValOnStream ( vecOut, iPos, static_cast ( iID ), 2 ); + + // 1 byte number of parts + PutValOnStream ( vecOut, iPos, static_cast ( iNumParts ), 1 ); + + // 1 byte split cnt + PutValOnStream ( vecOut, iPos, static_cast ( iSplitCnt ), 1 ); + + // data + for ( int i = 0; i < iLengthOfDataPart; i++ ) + { + PutValOnStream ( vecOut, iPos, static_cast ( vecData[iStartIndexInData + i] ), 1 ); + } +} + void CProtocol::PutValOnStream ( CVector& vecIn, int& iPos, const uint32_t iVal, @@ -2547,13 +3113,14 @@ void CProtocol::PutValOnStream ( CVector& vecIn, void CProtocol::PutStringUTF8OnStream ( CVector& vecIn, int& iPos, - const QByteArray& sStringUTF8 ) + const QByteArray& sStringUTF8, + const int iNumberOfBytsLen ) { // get the utf-8 string size const int iStrUTF8Len = sStringUTF8.size(); - // number of bytes for utf-8 string (2 bytes) - PutValOnStream ( vecIn, iPos, static_cast ( iStrUTF8Len ), 2 ); + // number of bytes for utf-8 string (iNumberOfBytsLen bytes) + PutValOnStream ( vecIn, iPos, static_cast ( iStrUTF8Len ), iNumberOfBytsLen ); // actual utf-8 string (n bytes) for ( int j = 0; j < iStrUTF8Len; j++ ) diff --git a/src/protocol.h b/src/protocol.h old mode 100644 new mode 100755 index 962d3f798d..cb0bbd2bce --- a/src/protocol.h +++ b/src/protocol.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -28,6 +28,7 @@ #include #include #include +#include #include "global.h" #include "util.h" @@ -54,11 +55,14 @@ #define PROTMESSID_CHANNEL_INFOS 25 // set channel infos #define PROTMESSID_OPUS_SUPPORTED 26 // tells that OPUS codec is supported #define PROTMESSID_LICENCE_REQUIRED 27 // licence required -#define PROTMESSID_REQ_CHANNEL_LEVEL_LIST 28 // request the channel level list +#define PROTMESSID_REQ_CHANNEL_LEVEL_LIST 28 // OLD (not used anymore) // TODO needed for compatibility to old servers >= 3.4.6 and <= 3.5.12 #define PROTMESSID_VERSION_AND_OS 29 // version number and operating system #define PROTMESSID_CHANNEL_PAN 30 // set channel pan for mix #define PROTMESSID_MUTE_STATE_CHANGED 31 // mute state of your signal at another client has changed #define PROTMESSID_CLIENT_ID 32 // current user ID and server status +#define PROTMESSID_RECORDER_STATE 33 // contains the state of the jam recorder (ERecorderState) +#define PROTMESSID_REQ_SPLIT_MESS_SUPPORT 34 // request support for split messages +#define PROTMESSID_SPLIT_MESS_SUPPORTED 35 // split messages are supported // message IDs of connection less messages (CLM) // DEFINITION -> start at 1000, end at 1999, see IsConnectionLessMessageID @@ -78,6 +82,11 @@ #define PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST 1014 // request the connected clients list #define PROTMESSID_CLM_CHANNEL_LEVEL_LIST 1015 // channel level list #define PROTMESSID_CLM_REGISTER_SERVER_RESP 1016 // status of server registration request +#define PROTMESSID_CLM_REGISTER_SERVER_EX 1017 // register server with extended information +#define PROTMESSID_CLM_RED_SERVER_LIST 1018 // reduced server list + +// special IDs +#define PROTMESSID_SPECIAL_SPLIT_MESSAGE 2001 // a container for split messages // lengths of message as defined in protocol.cpp file #define MESS_HEADER_LENGTH_BYTE 7 // TAG (2), ID (2), cnt (1), length (2) @@ -86,6 +95,10 @@ // time out for message re-send if no acknowledgement was received #define SEND_MESS_TIMEOUT_MS 400 // ms +// message split parameters +#define MESS_SPLIT_PART_SIZE_BYTES 550 +#define MAX_NUM_MESS_SPLIT_PARTS ( MAX_SIZE_BYTES_NETW_BUF / MESS_SPLIT_PART_SIZE_BYTES ) + /* Classes ********************************************************************/ class CProtocol : public QObject @@ -96,12 +109,13 @@ class CProtocol : public QObject CProtocol(); void Reset(); + void SetSplitMessageSupported ( const bool bIn ) { bSplitMessageSupported = bIn; } void CreateJitBufMes ( const int iJitBufSize ); void CreateReqJitBufMes(); void CreateClientIDMes ( const int iChanID ); - void CreateChanGainMes ( const int iChanID, const double dGain ); - void CreateChanPanMes ( const int iChanID, const double dPan ); + void CreateChanGainMes ( const int iChanID, const float fGain ); + void CreateChanPanMes ( const int iChanID, const float fPan ); void CreateMuteStateHasChangedMes ( const int iChanID, const bool bIsMuted ); void CreateConClientListMes ( const CVector& vecChanInfo ); void CreateReqConnClientsList(); @@ -110,10 +124,16 @@ class CProtocol : public QObject void CreateChatTextMes ( const QString strChatText ); void CreateNetwTranspPropsMes ( const CNetworkTransportProps& NetTrProps ); void CreateReqNetwTranspPropsMes(); + void CreateReqSplitMessSupportMes(); + void CreateSplitMessSupportedMes(); void CreateLicenceRequiredMes ( const ELicenceType eLicenceType ); void CreateOpusSupportedMes(); - void CreateReqChannelLevelListMes ( const bool bRCL ); + +// TODO needed for compatibility to old servers >= 3.4.6 and <= 3.5.12 +void CreateReqChannelLevelListMes(); + void CreateVersionAndOSMes(); + void CreateRecorderStateMes ( const ERecorderState eRecorderState ); void CreateCLPingMes ( const CHostAddress& InetAddr, const int iMs ); void CreateCLPingWithNumClientsMes ( const CHostAddress& InetAddr, @@ -123,9 +143,14 @@ class CProtocol : public QObject void CreateCLRegisterServerMes ( const CHostAddress& InetAddr, const CHostAddress& LInetAddr, const CServerCoreInfo& ServerInfo ); + void CreateCLRegisterServerExMes ( const CHostAddress& InetAddr, + const CHostAddress& LInetAddr, + const CServerCoreInfo& ServerInfo ); void CreateCLUnregisterServerMes ( const CHostAddress& InetAddr ); void CreateCLServerListMes ( const CHostAddress& InetAddr, const CVector vecServerInfo ); + void CreateCLRedServerListMes ( const CHostAddress& InetAddr, + const CVector vecServerInfo ); void CreateCLReqServerListMes ( const CHostAddress& InetAddr ); void CreateCLSendEmptyMesMes ( const CHostAddress& InetAddr, const CHostAddress& TargetInetAddr ); @@ -148,11 +173,11 @@ class CProtocol : public QObject int& iRecCounter, int& iRecID ); - bool ParseMessageBody ( const CVector& vecbyMesBodyData, + void ParseMessageBody ( const CVector& vecbyMesBodyData, const int iRecCounter, const int iRecID ); - bool ParseConnectionLessMessageBody ( const CVector& vecbyMesBodyData, + void ParseConnectionLessMessageBody ( const CVector& vecbyMesBodyData, const int iRecID, const CHostAddress& InetAddr ); @@ -196,6 +221,22 @@ class CProtocol : public QObject const int iID, const CVector& vecData ); + void GenSplitMessageContainer ( CVector& vecOut, + const int iID, + const int iNumParts, + const int iSplitCnt, + const CVector& vecData, + const int iStartIndexInData, + const int iLengthOfDataPart ); + + bool ParseSplitMessageContainer ( const CVector& vecbyData, + CVector& vecbyMesBodyData, + const int iSplitMessageDataIndex, + int& iID, + int& iNumParts, + int& iSplitCnt, + int& iCurPartSize ); + void PutValOnStream ( CVector& vecIn, int& iPos, const uint32_t iVal, @@ -203,7 +244,8 @@ class CProtocol : public QObject void PutStringUTF8OnStream ( CVector& vecIn, int& iPos, - const QByteArray& sStringUTF8 ); + const QByteArray& sStringUTF8, + const int iNumberOfBytsLen = 2 ); // default is 2 bytes lenght indicator static uint32_t GetValFromStream ( const CVector& vecIn, int& iPos, @@ -212,7 +254,8 @@ class CProtocol : public QObject bool GetStringFromStream ( const CVector& vecIn, int& iPos, const int iMaxStringLen, - QString& strOut ); + QString& strOut, + const int iNumberOfBytsLen = 2 ); // default is 2 bytes lenght indicator void SendMessage(); @@ -236,9 +279,11 @@ class CProtocol : public QObject bool EvaluateChatTextMes ( const CVector& vecData ); bool EvaluateNetwTranspPropsMes ( const CVector& vecData ); bool EvaluateReqNetwTranspPropsMes(); + bool EvaluateReqSplitMessSupportMes(); + bool EvaluateSplitMessSupportedMes(); bool EvaluateLicenceRequiredMes ( const CVector& vecData ); - bool EvaluateReqChannelLevelListMes ( const CVector& vecData ); bool EvaluateVersionAndOSMes ( const CVector& vecData ); + bool EvaluateRecorderStateMes ( const CVector& vecData ); bool EvaluateCLPingMes ( const CHostAddress& InetAddr, const CVector& vecData ); @@ -247,9 +292,13 @@ class CProtocol : public QObject bool EvaluateCLServerFullMes(); bool EvaluateCLRegisterServerMes ( const CHostAddress& InetAddr, const CVector& vecData ); + bool EvaluateCLRegisterServerExMes ( const CHostAddress& InetAddr, + const CVector& vecData ); bool EvaluateCLUnregisterServerMes ( const CHostAddress& InetAddr ); bool EvaluateCLServerListMes ( const CHostAddress& InetAddr, const CVector& vecData ); + bool EvaluateCLRedServerListMes ( const CHostAddress& InetAddr, + const CVector& vecData ); bool EvaluateCLReqServerListMes ( const CHostAddress& InetAddr ); bool EvaluateCLSendEmptyMesMes ( const CVector& vecData ); bool EvaluateCLDisconnectionMes ( const CHostAddress& InetAddr ); @@ -274,6 +323,11 @@ class CProtocol : public QObject QTimer TimerSendMess; QMutex Mutex; + CVector vecbySplitMessageStorage; + int iSplitMessageCnt; + int iSplitMessageDataIndex; + bool bSplitMessageSupported; + public slots: void OnTimerSendMess() { SendMessage(); } @@ -288,8 +342,8 @@ public slots: void ReqJittBufSize(); void ChangeNetwBlSiFact ( int iNewNetwBlSiFact ); void ClientIDReceived ( int iChanID ); - void ChangeChanGain ( int iChanID, double dNewGain ); - void ChangeChanPan ( int iChanID, double dNewPan ); + void ChangeChanGain ( int iChanID, float fNewGain ); + void ChangeChanPan ( int iChanID, float fNewPan ); void MuteStateHasChangedReceived ( int iCurID, bool bIsMuted ); void ConClientListMesReceived ( CVector vecChanInfo ); void ServerFullMesReceived(); @@ -299,9 +353,11 @@ public slots: void ChatTextReceived ( QString strChatText ); void NetTranspPropsReceived ( CNetworkTransportProps NetworkTransportProps ); void ReqNetTranspProps(); + void ReqSplitMessSupport(); + void SplitMessSupported(); void LicenceRequired ( ELicenceType eLicenceType ); - void ReqChannelLevelList ( bool bOptIn ); void VersionAndOSReceived ( COSUtil::EOpSystemType eOSType, QString strVersion ); + void RecorderStateReceived ( ERecorderState eRecorderState ); void CLPingReceived ( CHostAddress InetAddr, int iMs ); @@ -311,9 +367,16 @@ public slots: void CLRegisterServerReceived ( CHostAddress InetAddr, CHostAddress LInetAddr, CServerCoreInfo ServerInfo ); + void CLRegisterServerExReceived ( CHostAddress InetAddr, + CHostAddress LInetAddr, + CServerCoreInfo ServerInfo, + COSUtil::EOpSystemType eOSType, + QString strVersion ); void CLUnregisterServerReceived ( CHostAddress InetAddr ); void CLServerListReceived ( CHostAddress InetAddr, CVector vecServerInfo ); + void CLRedServerListReceived ( CHostAddress InetAddr, + CVector vecServerInfo ); void CLReqServerList ( CHostAddress InetAddr ); void CLSendEmptyMes ( CHostAddress TargetInetAddr ); void CLDisconnection ( CHostAddress InetAddr ); diff --git a/src/recorder/creaperproject.cpp b/src/recorder/creaperproject.cpp old mode 100755 new mode 100644 index 69b179ce27..e518188696 --- a/src/recorder/creaperproject.cpp +++ b/src/recorder/creaperproject.cpp @@ -1,4 +1,5 @@ /******************************************************************************\ + * Copyright (c) 2020 * * Author(s): * pljones @@ -17,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ diff --git a/src/recorder/creaperproject.h b/src/recorder/creaperproject.h old mode 100755 new mode 100644 index be6f190a4f..a2a6ac8893 --- a/src/recorder/creaperproject.h +++ b/src/recorder/creaperproject.h @@ -1,4 +1,5 @@ /******************************************************************************\ + * Copyright (c) 2020 * * Author(s): * pljones @@ -17,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -32,21 +33,6 @@ namespace recorder { -struct STrackItem -{ - STrackItem(int numAudioChannels, qint64 startFrame, qint64 frameCount, QString fileName) : - numAudioChannels(numAudioChannels), - startFrame(startFrame), - frameCount(frameCount), - fileName(fileName) - { - } - int numAudioChannels; - qint64 startFrame; - qint64 frameCount; - QString fileName; -}; - class CReaperItem : public QObject { Q_OBJECT @@ -59,12 +45,6 @@ class CReaperItem : public QObject const QUuid iguid = QUuid::createUuid(); const QUuid guid = QUuid::createUuid(); QString out; - - inline QString secondsAt48K( const qint64 frames, - const int frameSize ) - { - return QString::number( static_cast( frames * frameSize ) / 48000, 'f', 14 ); - } }; class CReaperTrack : public QObject diff --git a/src/recorder/cwavestream.cpp b/src/recorder/cwavestream.cpp old mode 100755 new mode 100644 index dadbdee343..048483caee --- a/src/recorder/cwavestream.cpp +++ b/src/recorder/cwavestream.cpp @@ -1,4 +1,5 @@ /******************************************************************************\ + * Copyright (c) 2020 * * Author(s): * pljones @@ -17,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -122,26 +123,32 @@ void CWaveStream::waveStreamHeaders() void CWaveStream::finalise() { - static const uint32_t hdrRiffChunkSize = sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t); - static const uint32_t fmtSubChunkSize = sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t); + static const uint64_t hdrRiffChunkSize = sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t); + static const uint64_t fmtSubChunkSize = sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t); - static const uint32_t hdrRiffChunkSizeOffset = sizeof(uint32_t); - static const uint32_t dataSubChunkHdrChunkSizeOffset = hdrRiffChunkSize + fmtSubChunkSize + sizeof (uint32_t); + static const uint64_t hdrRiffChunkSizeOffset = sizeof(uint32_t); + static const uint64_t dataSubChunkHdrChunkSizeOffset = hdrRiffChunkSize + fmtSubChunkSize + sizeof (uint32_t); const int64_t currentPos = this->device()->pos(); - const uint32_t fileLength = static_cast(currentPos - initialPos); - - QDataStream& out = static_cast(*this); - - // Overwrite hdr_riff.chunkSize - this->device()->seek(initialPos + hdrRiffChunkSizeOffset); - out << static_cast(fileLength - (hdrRiffChunkSizeOffset + sizeof (uint32_t))); - - // Overwrite dataSubChunkHdr.chunkSize - this->device()->seek(initialPos + dataSubChunkHdrChunkSizeOffset); - out << static_cast(fileLength - (dataSubChunkHdrChunkSizeOffset + sizeof (uint32_t))); - - // and then restore the position and byte order - this->device()->seek(currentPos); + const uint64_t fileLengthRiff = static_cast(currentPos - initialPos - (hdrRiffChunkSizeOffset + sizeof (uint32_t))); + const uint64_t fileLengthData = static_cast(currentPos - initialPos - (dataSubChunkHdrChunkSizeOffset + sizeof (uint32_t))); + + // check if lengths are within the range of the WAV file format + if (fileLengthRiff < 0x100000000ULL && fileLengthData < 0x100000000ULL) + { + QDataStream& out = static_cast(*this); + + // Overwrite hdr_riff.chunkSize + this->device()->seek(initialPos + hdrRiffChunkSizeOffset); + out << static_cast(fileLengthRiff); + + // Overwrite dataSubChunkHdr.chunkSize + this->device()->seek(initialPos + dataSubChunkHdrChunkSizeOffset); + out << static_cast(fileLengthData); + + // And restore the position + this->device()->seek(currentPos); + } + // restore the byte order setByteOrder(initialByteOrder); } diff --git a/src/recorder/cwavestream.h b/src/recorder/cwavestream.h old mode 100755 new mode 100644 index f3ceab660b..54b91633c6 --- a/src/recorder/cwavestream.h +++ b/src/recorder/cwavestream.h @@ -1,4 +1,5 @@ /******************************************************************************\ + * Copyright (c) 2020 * * Author(s): * pljones @@ -17,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -27,13 +28,38 @@ namespace recorder { +inline QString secondsAt48K( const qint64 frames, + const int frameSize ) +{ + return QString::number( static_cast( frames * frameSize ) / 48000, 'f', 14 ); +} + +struct STrackItem +{ + STrackItem ( int numAudioChannels, + qint64 startFrame, + qint64 frameCount, + QString fileName ) : + numAudioChannels ( numAudioChannels ), + startFrame ( startFrame ), + frameCount ( frameCount ), + fileName ( fileName ) + { + } + + int numAudioChannels; + qint64 startFrame; + qint64 frameCount; + QString fileName; +}; + class HdrRiff { public: HdrRiff() {} static const uint32_t chunkId = 0x46464952; // RIFF - static const uint32_t chunkSize = 0xffffffff; // (will be overwritten) Size of file in bytes - 8 = size of data + 36 + static const uint32_t chunkSize = 0x00000000; // unknown static const uint32_t format = 0x45564157; // WAVE }; @@ -62,7 +88,7 @@ class DataSubChunkHdr DataSubChunkHdr() {} static const uint32_t chunkId = 0x61746164; // "data" - static const uint32_t chunkSize = 0xffffffff; // (will be overwritten) Size of data + static const uint32_t chunkSize = 0x7ffff000; // magic for unspecified length }; class CWaveStream : public QDataStream diff --git a/src/recorder/jamcontroller.cpp b/src/recorder/jamcontroller.cpp new file mode 100755 index 0000000000..75e2b4568a --- /dev/null +++ b/src/recorder/jamcontroller.cpp @@ -0,0 +1,188 @@ +/******************************************************************************\ + * Copyright (c) 2020 + * + * Author(s): + * pljones + * + ****************************************************************************** + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#include "jamcontroller.h" + +using namespace recorder; + +CJamController::CJamController() : + bRecorderInitialised ( false ), + bEnableRecording ( false ), + strRecordingDir ( "" ), + pthJamRecorder ( nullptr ) +{ +} + +void CJamController::RequestNewRecording() +{ + + if ( bRecorderInitialised && bEnableRecording ) + { + emit RestartRecorder(); + } +} + +void CJamController::SetEnableRecording ( bool bNewEnableRecording, bool isRunning ) +{ + + if ( bRecorderInitialised ) + { + + // message only if the state appears to change + if ( bEnableRecording != bNewEnableRecording ) + { +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) +// TODO we should use the ConsoleWriterFactory() instead of qInfo() + qInfo() << "Recording state" << ( bNewEnableRecording ? "enabled" : "disabled" ); +#endif + } + + // note that this block executes regardless of whether + // what appears to be a change is being applied, to ensure + // the requested state is the result + bEnableRecording = bNewEnableRecording; + + if ( !bEnableRecording ) + { + emit StopRecorder(); + } + else if ( !isRunning ) + { + // This dirty hack is for the GUI. It doesn't care. + emit StopRecorder(); + } + } +} + +void CJamController::SetRecordingDir ( QString newRecordingDir, + int iServerFrameSizeSamples, + bool bDisableRecording ) +{ + if ( bRecorderInitialised && pthJamRecorder != nullptr ) + { + // We have a thread and we want to start a new one. + // We only want one running. + // This could take time, unfortunately. + // Hopefully changing recording directory will NOT happen during a long jam... + emit EndRecorderThread(); + pthJamRecorder->wait(); + pthJamRecorder = nullptr; + } + + if ( !newRecordingDir.isEmpty() ) + { + pJamRecorder = new recorder::CJamRecorder ( newRecordingDir, iServerFrameSizeSamples ); + strRecorderErrMsg = pJamRecorder->Init(); + bRecorderInitialised = ( strRecorderErrMsg == QString::null ); + bEnableRecording = bRecorderInitialised && !bDisableRecording; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) +// TODO we should use the ConsoleWriterFactory() instead of qInfo() + qInfo() << "Recording state" << ( bEnableRecording ? "enabled" : "disabled" ); +#endif + } + else + { + // This is the only time this is ever true - UI needs to handle it + strRecorderErrMsg = QString::null; + bRecorderInitialised = false; + bEnableRecording = false; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) +// TODO we should use the ConsoleWriterFactory() instead of qInfo() + qInfo() << "Recording state not initialised"; +#endif + } + + if ( bRecorderInitialised ) + { + strRecordingDir = newRecordingDir; + + pthJamRecorder = new QThread(); + pthJamRecorder->setObjectName ( "JamRecorder" ); + + pJamRecorder->moveToThread ( pthJamRecorder ); + + // QT signals + QObject::connect ( pthJamRecorder, &QThread::finished, + pJamRecorder, &QObject::deleteLater ); + + QObject::connect( QCoreApplication::instance(), &QCoreApplication::aboutToQuit, + pJamRecorder, &CJamRecorder::OnAboutToQuit, + Qt::ConnectionType::BlockingQueuedConnection ); + + // from the controller to the recorder + QObject::connect( this, &CJamController::RestartRecorder, + pJamRecorder, &CJamRecorder::OnTriggerSession ); + + QObject::connect( this, &CJamController::StopRecorder, + pJamRecorder, &CJamRecorder::OnEnd ); + + QObject::connect( this, &CJamController::EndRecorderThread, + pJamRecorder, &CJamRecorder::OnAboutToQuit, + Qt::ConnectionType::BlockingQueuedConnection ); + + // from the server to the recorder + QObject::connect( this, &CJamController::Stopped, + pJamRecorder, &CJamRecorder::OnEnd ); + + QObject::connect( this, &CJamController::ClientDisconnected, + pJamRecorder, &CJamRecorder::OnDisconnected ); + + qRegisterMetaType> ( "CVector" ); + QObject::connect( this, &CJamController::AudioFrame, + pJamRecorder, &CJamRecorder::OnFrame ); + + // from the recorder to the server + QObject::connect ( pJamRecorder, &CJamRecorder::RecordingSessionStarted, + this, &CJamController::RecordingSessionStarted ); + + pthJamRecorder->start ( QThread::NormalPriority ); + + } + else + { + strRecordingDir = ""; + } +} + +ERecorderState CJamController::GetRecorderState() +{ + // return recorder state + if ( bRecorderInitialised ) + { + if ( bEnableRecording ) + { + return RS_RECORDING; + } + else + { + return RS_NOT_ENABLED; + } + } + else + { + return RS_NOT_INITIALISED; + } +} diff --git a/src/recorder/jamcontroller.h b/src/recorder/jamcontroller.h new file mode 100755 index 0000000000..82b714f430 --- /dev/null +++ b/src/recorder/jamcontroller.h @@ -0,0 +1,76 @@ +/******************************************************************************\ + * Copyright (c) 2020 + * + * Author(s): + * pljones + * + ****************************************************************************** + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#pragma once + +#include + +#include "jamrecorder.h" + +namespace recorder { + +class CJamController : public QObject +{ + Q_OBJECT +public: + explicit CJamController(); + + bool GetRecorderInitialised() { return bRecorderInitialised; } + QString GetRecorderErrMsg() { return strRecorderErrMsg; } + bool GetRecordingEnabled() { return bEnableRecording; } + void RequestNewRecording(); + void SetEnableRecording ( bool bNewEnableRecording, bool isRunning ); + QString GetRecordingDir() { return strRecordingDir; } + void SetRecordingDir ( QString newRecordingDir, int iServerFrameSizeSamples, bool bDisableRecording ); + ERecorderState GetRecorderState(); + +private: + CServer* pServer; + + bool bRecorderInitialised; + bool bEnableRecording; + QString strRecordingDir; + QThread* pthJamRecorder; + + CJamRecorder* pJamRecorder; + QString strRecorderErrMsg; + +signals: + void RestartRecorder(); + void StopRecorder(); + void RecordingSessionStarted ( QString sessionDir ); + void EndRecorderThread(); + void Stopped(); + void ClientDisconnected ( int iChID ); + void AudioFrame ( const int iChID, + const QString stChName, + const CHostAddress RecHostAddr, + const int iNumAudChan, + const CVector vecsData ); + +}; + +} + +Q_DECLARE_METATYPE(int16_t) diff --git a/src/recorder/jamrecorder.cpp b/src/recorder/jamrecorder.cpp index 0fe05134c2..06577f3f72 100755 --- a/src/recorder/jamrecorder.cpp +++ b/src/recorder/jamrecorder.cpp @@ -1,4 +1,5 @@ /******************************************************************************\ + * Copyright (c) 2020 * * Author(s): * pljones @@ -17,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -153,7 +154,7 @@ void CJamSession::DisconnectClient(int iChID) } /** - * @brief CJamSession::Frame Process a frame emited for a client by the server + * @brief CJamSession::Frame Process a frame emitted for a client by the server * @param iChID the client channel id * @param name the client name * @param address the client IP and port number @@ -300,52 +301,44 @@ QMap> CJamSession::TracksFromSessionDir(const QString /** * @brief CJamRecorder::Init Create recording directory, if necessary, and connect signal handlers - * @param server Server object emiting signals + * @param server Server object emitting signals + * @return QString::null on success else the failure reason */ -void CJamRecorder::Init( const CServer* server, - const int _iServerFrameSizeSamples ) +QString CJamRecorder::Init() { - QFileInfo fi(recordBaseDir.absolutePath()); - fi.setCaching(false); + QString errmsg = QString::null; + QFileInfo fi ( recordBaseDir.absolutePath() ); + fi.setCaching ( false ); - if (!fi.exists() && !QDir().mkpath(recordBaseDir.absolutePath())) + if ( !fi.exists() && !QDir().mkpath ( recordBaseDir.absolutePath() ) ) { - throw std::runtime_error( (recordBaseDir.absolutePath() + " does not exist but could not be created").toStdString() ); + errmsg = recordBaseDir.absolutePath() + " does not exist but could not be created"; +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) +// TODO we should use the ConsoleWriterFactory() instead of qCritical() + qCritical() << errmsg; +#endif + return errmsg; } if (!fi.isDir()) { - throw std::runtime_error( (recordBaseDir.absolutePath() + " exists but is not a directory").toStdString() ); + errmsg = recordBaseDir.absolutePath() + " exists but is not a directory"; +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) +// TODO we should use the ConsoleWriterFactory() instead of qCritical() + qCritical() << errmsg; +#endif + return errmsg; } if (!fi.isWritable()) { - throw std::runtime_error( (recordBaseDir.absolutePath() + " is a directory but cannot be written to").toStdString() ); + errmsg = recordBaseDir.absolutePath() + " is a directory but cannot be written to"; +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) +// TODO we should use the ConsoleWriterFactory() instead of qCritical() + qCritical() << errmsg; +#endif + return errmsg; } - QObject::connect( (const QObject *)server, SIGNAL ( RestartRecorder() ), - this, SLOT( OnTriggerSession() ), - 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 ); - - qRegisterMetaType> ( "CVector" ); - QObject::connect( (const QObject *)server, SIGNAL ( AudioFrame( const int, const QString, const CHostAddress, const int, const CVector ) ), - this, SLOT( OnFrame (const int, const QString, const CHostAddress, const int, const CVector ) ), - Qt::ConnectionType::QueuedConnection ); - - QObject::connect( QCoreApplication::instance(), SIGNAL ( aboutToQuit() ), - this, SLOT( OnAboutToQuit() ) ); - - iServerFrameSizeSamples = _iServerFrameSizeSamples; - - thisThread = new QThread(); - moveToThread ( thisThread ); - thisThread->start(); + return errmsg; } /** @@ -355,57 +348,41 @@ void CJamRecorder::Start() { // Ensure any previous cleaning up has been done. OnEnd(); - currentSession = new CJamSession( recordBaseDir ); - isRecording = true; + // needs to be after OnEnd() as that also locks + ChIdMutex.lock(); + { + currentSession = new CJamSession( recordBaseDir ); + isRecording = true; + } + ChIdMutex.unlock(); emit RecordingSessionStarted ( currentSession->SessionDir().path() ); } /** - * @brief CJamRecorder::OnEnd Finalise the recording and emit the Reaper RPP file - * - * Emits RecordingSessionEnded with the Reaper project file name, - * or null if was not recording or a problem occurs + * @brief CJamRecorder::OnEnd Finalise the recording and write the Reaper RPP file */ void CJamRecorder::OnEnd() { - QString reaperProjectFileName = QString::Null(); - - if ( isRecording ) + ChIdMutex.lock(); // iChId used in currentSession->End() { - isRecording = false; - currentSession->End(); + if ( isRecording ) + { + isRecording = false; + currentSession->End(); - QString reaperProjectFileName = currentSession->SessionDir().filePath(currentSession->Name().append(".rpp")); - const QFileInfo fi(reaperProjectFileName); + ReaperProjectFromCurrentSession(); + AudacityLofFromCurrentSession(); - if (fi.exists()) - { - qWarning() << "CJamRecorder::OnEnd():" << fi.absolutePath() << "exists and will not be overwritten."; - reaperProjectFileName = QString::Null(); - } - else - { - QFile outf (reaperProjectFileName); - if ( outf.open(QFile::WriteOnly) ) - { - QTextStream out(&outf); - out << CReaperProject( currentSession->Tracks(), iServerFrameSizeSamples ).toString() << endl; - qDebug() << "Session RPP:" << reaperProjectFileName; - } - else - { - qWarning() << "CJamRecorder::OnEnd():" << fi.absolutePath() << "could not be created, no RPP written."; - reaperProjectFileName = QString::Null(); - } + delete currentSession; + currentSession = nullptr; } - - delete currentSession; - currentSession = nullptr; } + ChIdMutex.unlock(); } + /** * @brief CJamRecorder::OnTriggerSession End one session and start a new one */ @@ -425,7 +402,67 @@ void CJamRecorder::OnAboutToQuit() { OnEnd(); - thisThread->exit(); + QThread::currentThread()->exit(); +} + +void CJamRecorder::ReaperProjectFromCurrentSession() +{ + QString reaperProjectFileName = currentSession->SessionDir().filePath(currentSession->Name().append(".rpp")); + const QFileInfo fi(reaperProjectFileName); + + if (fi.exists()) + { + qWarning() << "CJamRecorder::ReaperProjectFromCurrentSession():" << fi.absolutePath() << "exists and will not be overwritten."; + } + else + { + QFile outf (reaperProjectFileName); + if ( outf.open(QFile::WriteOnly) ) + { + QTextStream out(&outf); + out << CReaperProject( currentSession->Tracks(), iServerFrameSizeSamples ).toString() << endl; + qDebug() << "Session RPP:" << reaperProjectFileName; + } + else + { + qWarning() << "CJamRecorder::ReaperProjectFromCurrentSession():" << fi.absolutePath() << "could not be created, no RPP written."; + } + } +} + +void CJamRecorder::AudacityLofFromCurrentSession() +{ + QString audacityLofFileName = currentSession->SessionDir().filePath(currentSession->Name().append(".lof")); + const QFileInfo fi(audacityLofFileName); + + if (fi.exists()) + { + qWarning() << "CJamRecorder::AudacityLofFromCurrentSession():" << fi.absolutePath() << "exists and will not be overwritten."; + } + else + { + QFile outf (audacityLofFileName); + if ( outf.open(QFile::WriteOnly) ) + { + QTextStream sOut(&outf); + + foreach ( auto trackName, currentSession->Tracks().keys() ) + { + foreach ( auto item, currentSession->Tracks()[trackName] ) { + QFileInfo fi ( item.fileName ); + sOut << "file " << '"' << fi.fileName() << '"'; + sOut << " offset " << secondsAt48K( item.startFrame, iServerFrameSizeSamples ) << endl; + } + } + + sOut.flush(); + qDebug() << "Session LOF:" << audacityLofFileName; + } + else + { + qWarning() << "CJamRecorder::AudacityLofFromCurrentSession():" << fi.absolutePath() << "could not be created, no LOF written."; + } + } } /** @@ -465,20 +502,25 @@ void CJamRecorder::SessionDirToReaper(QString& strSessionDirName, int serverFram */ void CJamRecorder::OnDisconnected(int iChID) { - if ( !isRecording ) - { - qWarning() << "CJamRecorder::OnDisconnected: channel" << iChID << "disconnected but not recording"; - } - if ( currentSession == nullptr ) + ChIdMutex.lock(); { - qWarning() << "CJamRecorder::OnDisconnected: channel" << iChID << "disconnected but no currentSession"; - return; + if ( !isRecording ) + { + qWarning() << "CJamRecorder::OnDisconnected: channel" << iChID << "disconnected but not recording"; + } + if ( currentSession == nullptr ) + { + qWarning() << "CJamRecorder::OnDisconnected: channel" << iChID << "disconnected but no currentSession"; + return; + } + + currentSession->DisconnectClient ( iChID ); } - currentSession->DisconnectClient(iChID); + ChIdMutex.unlock(); } /** - * @brief CJamRecorder::OnFrame Handle a frame emited for a client by the server + * @brief CJamRecorder::OnFrame Handle a frame emitted for a client by the server * @param iChID the client channel id * @param name the client name * @param address the client IP and port number @@ -495,5 +537,10 @@ void CJamRecorder::OnFrame(const int iChID, const QString name, const CHostAddre Start(); } - currentSession->Frame( iChID, name, address, numAudioChannels, data, iServerFrameSizeSamples ); + // needs to be after Start() as that also locks + ChIdMutex.lock(); + { + currentSession->Frame ( iChID, name, address, numAudioChannels, data, iServerFrameSizeSamples ); + } + ChIdMutex.unlock(); } diff --git a/src/recorder/jamrecorder.h b/src/recorder/jamrecorder.h index 3713162a93..a42fe0c457 100755 --- a/src/recorder/jamrecorder.h +++ b/src/recorder/jamrecorder.h @@ -1,4 +1,5 @@ /******************************************************************************\ + * Copyright (c) 2020 * * Author(s): * pljones @@ -17,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -26,6 +27,7 @@ #include #include #include +#include #include "../util.h" #include "../channel.h" @@ -138,17 +140,19 @@ class CJamRecorder : public QObject Q_OBJECT public: - CJamRecorder ( const QString recordingDirName ) : - recordBaseDir ( recordingDirName ), - isRecording ( false ) + CJamRecorder ( const QString strRecordingBaseDir, + const int iServerFrameSizeSamples ) : + recordBaseDir ( strRecordingBaseDir ), + iServerFrameSizeSamples ( iServerFrameSizeSamples ), + isRecording ( false ) { } /** * @brief Create recording directory, if necessary, and connect signal handlers - * @param server Server object emiting signals + * @param server Server object emitting signals */ - void Init( const CServer* server, const int _iServerFrameSizeSamples ); + QString Init(); /** * @brief SessionDirToReaper Method that allows an RPP file to be recreated @@ -159,47 +163,44 @@ class CJamRecorder : public QObject private: void Start(); + void ReaperProjectFromCurrentSession(); + void AudacityLofFromCurrentSession(); - QDir recordBaseDir; - + QDir recordBaseDir; + int iServerFrameSizeSamples; bool isRecording; CJamSession* currentSession; - int iServerFrameSizeSamples; - - QThread* thisThread; + QMutex ChIdMutex; signals: void RecordingSessionStarted ( QString sessionDir ); -private slots: +public slots: /** - * @brief Raised when last client leaves the server, ending the recording. + * @brief Handle last client leaving the server, ends the recording. */ void OnEnd(); /** - * @brief Raised to end one session and start a new one. + * @brief Handle request to end one session and start a new one. */ void OnTriggerSession(); /** - * @brief Raised when application is stopping + * @brief Handle application stopping */ void OnAboutToQuit(); /** - * @brief Raised when an existing client leaves the server. + * @brief Handle an existing client leaving the server. * @param iChID channel number of client */ void OnDisconnected ( int iChID ); /** - * @brief Raised when a frame of data is available to process + * @brief Handle a frame of data to process */ void OnFrame ( const int iChID, const QString name, const CHostAddress address, const int numAudioChannels, const CVector data ); }; } - -Q_DECLARE_METATYPE(int16_t) -Q_DECLARE_METATYPE(CVector) diff --git a/src/res/HLEDBlack.png b/src/res/HLEDBlack.png new file mode 100644 index 0000000000..a2b423b0f2 Binary files /dev/null and b/src/res/HLEDBlack.png differ diff --git a/src/res/HLEDBlackSmall.png b/src/res/HLEDBlackSmall.png new file mode 100644 index 0000000000..1ecfe9bd3b Binary files /dev/null and b/src/res/HLEDBlackSmall.png differ diff --git a/src/res/IndicatorGreen.png b/src/res/IndicatorGreen.png new file mode 100644 index 0000000000..0f90294c95 Binary files /dev/null and b/src/res/IndicatorGreen.png differ diff --git a/src/res/IndicatorRed.png b/src/res/IndicatorRed.png new file mode 100644 index 0000000000..5c93f8b880 Binary files /dev/null and b/src/res/IndicatorRed.png differ diff --git a/src/res/IndicatorYellow.png b/src/res/IndicatorYellow.png new file mode 100644 index 0000000000..e66146b51e Binary files /dev/null and b/src/res/IndicatorYellow.png differ diff --git a/src/res/VLEDBlack.png b/src/res/VLEDBlack.png deleted file mode 100755 index 1b92044f11..0000000000 Binary files a/src/res/VLEDBlack.png and /dev/null differ diff --git a/src/res/VLEDBlackSmall.png b/src/res/VLEDBlackSmall.png deleted file mode 100755 index 0dbb6f085f..0000000000 Binary files a/src/res/VLEDBlackSmall.png and /dev/null differ diff --git a/src/res/VLEDDisabledSmall.png b/src/res/VLEDDisabledSmall.png deleted file mode 100755 index 428436c377..0000000000 Binary files a/src/res/VLEDDisabledSmall.png and /dev/null differ diff --git a/src/res/VLEDGreen.png b/src/res/VLEDGreen.png deleted file mode 100755 index fb86baca5b..0000000000 Binary files a/src/res/VLEDGreen.png and /dev/null differ diff --git a/src/res/VLEDGreenSmall.png b/src/res/VLEDGreenSmall.png deleted file mode 100755 index c2b2a2a6ed..0000000000 Binary files a/src/res/VLEDGreenSmall.png and /dev/null differ diff --git a/src/res/VLEDGrey.png b/src/res/VLEDGrey.png deleted file mode 100755 index b1846bf99c..0000000000 Binary files a/src/res/VLEDGrey.png and /dev/null differ diff --git a/src/res/VLEDGreySmall.png b/src/res/VLEDGreySmall.png deleted file mode 100755 index 68dcbbf715..0000000000 Binary files a/src/res/VLEDGreySmall.png and /dev/null differ diff --git a/src/res/VLEDRed.png b/src/res/VLEDRed.png deleted file mode 100755 index 4fc8152d61..0000000000 Binary files a/src/res/VLEDRed.png and /dev/null differ diff --git a/src/res/VLEDRedSmall.png b/src/res/VLEDRedSmall.png deleted file mode 100755 index 3b22146f92..0000000000 Binary files a/src/res/VLEDRedSmall.png and /dev/null differ diff --git a/src/res/VLEDYellow.png b/src/res/VLEDYellow.png deleted file mode 100755 index 9b0638b80f..0000000000 Binary files a/src/res/VLEDYellow.png and /dev/null differ diff --git a/src/res/VLEDYellowSmall.png b/src/res/VLEDYellowSmall.png deleted file mode 100755 index 63c7687dd7..0000000000 Binary files a/src/res/VLEDYellowSmall.png and /dev/null differ diff --git a/src/res/VRLEDBlack.png b/src/res/VRLEDBlack.png deleted file mode 100755 index 878a4b49df..0000000000 Binary files a/src/res/VRLEDBlack.png and /dev/null differ diff --git a/src/res/VRLEDBlackSmall.png b/src/res/VRLEDBlackSmall.png deleted file mode 100755 index a6921f5264..0000000000 Binary files a/src/res/VRLEDBlackSmall.png and /dev/null differ diff --git a/src/res/VRLEDGreen.png b/src/res/VRLEDGreen.png deleted file mode 100755 index 846c7dbf15..0000000000 Binary files a/src/res/VRLEDGreen.png and /dev/null differ diff --git a/src/res/VRLEDGreenSmall.png b/src/res/VRLEDGreenSmall.png deleted file mode 100755 index 2c0f205d7f..0000000000 Binary files a/src/res/VRLEDGreenSmall.png and /dev/null differ diff --git a/src/res/VRLEDGrey.png b/src/res/VRLEDGrey.png deleted file mode 100755 index 4915fafb53..0000000000 Binary files a/src/res/VRLEDGrey.png and /dev/null differ diff --git a/src/res/VRLEDGreySmall.png b/src/res/VRLEDGreySmall.png deleted file mode 100755 index cdd3acd3ed..0000000000 Binary files a/src/res/VRLEDGreySmall.png and /dev/null differ diff --git a/src/res/VRLEDRed.png b/src/res/VRLEDRed.png deleted file mode 100755 index 4bad9d1d35..0000000000 Binary files a/src/res/VRLEDRed.png and /dev/null differ diff --git a/src/res/VRLEDRedSmall.png b/src/res/VRLEDRedSmall.png deleted file mode 100755 index 475d00e8e1..0000000000 Binary files a/src/res/VRLEDRedSmall.png and /dev/null differ diff --git a/src/res/VRLEDYellow.png b/src/res/VRLEDYellow.png deleted file mode 100755 index 7d945849fa..0000000000 Binary files a/src/res/VRLEDYellow.png and /dev/null differ diff --git a/src/res/VRLEDYellowSmall.png b/src/res/VRLEDYellowSmall.png deleted file mode 100755 index 93bd195dbc..0000000000 Binary files a/src/res/VRLEDYellowSmall.png and /dev/null differ diff --git a/src/res/banner.xcf b/src/res/banner.xcf deleted file mode 100755 index 9586f87b48..0000000000 Binary files a/src/res/banner.xcf and /dev/null differ diff --git a/src/res/fronticon.png b/src/res/fronticon.png old mode 100755 new mode 100644 index 21ead66cfb..a90d7ae535 Binary files a/src/res/fronticon.png and b/src/res/fronticon.png differ diff --git a/src/res/fronticonserver.png b/src/res/fronticonserver.png new file mode 100644 index 0000000000..87fb5c8eff Binary files /dev/null and b/src/res/fronticonserver.png differ diff --git a/src/res/homepage/ES/audiochannels.png b/src/res/homepage/ES/audiochannels.png new file mode 100644 index 0000000000..4d2e9a94e2 Binary files /dev/null and b/src/res/homepage/ES/audiochannels.png differ diff --git a/src/res/homepage/ES/audiofader.png b/src/res/homepage/ES/audiofader.png new file mode 100644 index 0000000000..734f3890e5 Binary files /dev/null and b/src/res/homepage/ES/audiofader.png differ diff --git a/src/res/homepage/ES/audioquality.png b/src/res/homepage/ES/audioquality.png new file mode 100644 index 0000000000..98f4c737a0 Binary files /dev/null and b/src/res/homepage/ES/audioquality.png differ diff --git a/src/res/homepage/ES/bufferdelay.png b/src/res/homepage/ES/bufferdelay.png new file mode 100644 index 0000000000..47437aa019 Binary files /dev/null and b/src/res/homepage/ES/bufferdelay.png differ diff --git a/src/res/homepage/ES/bufferdelaydependency.png b/src/res/homepage/ES/bufferdelaydependency.png new file mode 100644 index 0000000000..cb7670e058 Binary files /dev/null and b/src/res/homepage/ES/bufferdelaydependency.png differ diff --git a/src/res/homepage/ES/bufferdelaywindows.png b/src/res/homepage/ES/bufferdelaywindows.png new file mode 100644 index 0000000000..70d11f2adb Binary files /dev/null and b/src/res/homepage/ES/bufferdelaywindows.png differ diff --git a/src/res/homepage/ES/centralserveraddress.png b/src/res/homepage/ES/centralserveraddress.png new file mode 100644 index 0000000000..bb031fceec Binary files /dev/null and b/src/res/homepage/ES/centralserveraddress.png differ diff --git a/src/res/homepage/ES/channelmapping.png b/src/res/homepage/ES/channelmapping.png new file mode 100644 index 0000000000..60764e7483 Binary files /dev/null and b/src/res/homepage/ES/channelmapping.png differ diff --git a/src/res/homepage/ES/chat.png b/src/res/homepage/ES/chat.png new file mode 100644 index 0000000000..855a44021c Binary files /dev/null and b/src/res/homepage/ES/chat.png differ diff --git a/src/res/homepage/ES/connect.png b/src/res/homepage/ES/connect.png new file mode 100644 index 0000000000..49ab875f63 Binary files /dev/null and b/src/res/homepage/ES/connect.png differ diff --git a/src/res/homepage/ES/faders.png b/src/res/homepage/ES/faders.png new file mode 100644 index 0000000000..ab306f3769 Binary files /dev/null and b/src/res/homepage/ES/faders.png differ diff --git a/src/res/homepage/ES/fadertagtooltip.png b/src/res/homepage/ES/fadertagtooltip.png new file mode 100644 index 0000000000..326590ecff Binary files /dev/null and b/src/res/homepage/ES/fadertagtooltip.png differ diff --git a/src/res/homepage/ES/fancyskin.png b/src/res/homepage/ES/fancyskin.png new file mode 100644 index 0000000000..7e1e727bfe Binary files /dev/null and b/src/res/homepage/ES/fancyskin.png differ diff --git a/src/res/homepage/ES/indicators.png b/src/res/homepage/ES/indicators.png new file mode 100644 index 0000000000..aef0563f3a Binary files /dev/null and b/src/res/homepage/ES/indicators.png differ diff --git a/src/res/homepage/ES/inputlevel.png b/src/res/homepage/ES/inputlevel.png new file mode 100644 index 0000000000..705be237e8 Binary files /dev/null and b/src/res/homepage/ES/inputlevel.png differ diff --git a/src/res/homepage/ES/jamulusbannersmall.png b/src/res/homepage/ES/jamulusbannersmall.png new file mode 100644 index 0000000000..49c981183e Binary files /dev/null and b/src/res/homepage/ES/jamulusbannersmall.png differ diff --git a/src/res/homepage/ES/jitterbuffer.png b/src/res/homepage/ES/jitterbuffer.png new file mode 100644 index 0000000000..358240bceb Binary files /dev/null and b/src/res/homepage/ES/jitterbuffer.png differ diff --git a/src/res/homepage/ES/led.png b/src/res/homepage/ES/led.png new file mode 100644 index 0000000000..a76396da82 Binary files /dev/null and b/src/res/homepage/ES/led.png differ diff --git a/src/res/homepage/ES/main.png b/src/res/homepage/ES/main.png new file mode 100644 index 0000000000..409a569e01 Binary files /dev/null and b/src/res/homepage/ES/main.png differ diff --git a/src/res/homepage/ES/manual-es.md b/src/res/homepage/ES/manual-es.md new file mode 100644 index 0000000000..282a2c3a6c --- /dev/null +++ b/src/res/homepage/ES/manual-es.md @@ -0,0 +1,5 @@ + +Ayuda para Jamulus (Manual del Software) +============================== + +## Movido a https://jamulus.io/es/wiki/Software-Manual diff --git a/src/res/homepage/ES/mediawikisidebarlogo.png b/src/res/homepage/ES/mediawikisidebarlogo.png new file mode 100644 index 0000000000..3b232523aa Binary files /dev/null and b/src/res/homepage/ES/mediawikisidebarlogo.png differ diff --git a/src/res/homepage/ES/newclientlevel.png b/src/res/homepage/ES/newclientlevel.png new file mode 100644 index 0000000000..94a12c471a Binary files /dev/null and b/src/res/homepage/ES/newclientlevel.png differ diff --git a/src/res/homepage/ES/profile.png b/src/res/homepage/ES/profile.png new file mode 100644 index 0000000000..ae7dba169e Binary files /dev/null and b/src/res/homepage/ES/profile.png differ diff --git a/src/res/homepage/ES/reverberation.png b/src/res/homepage/ES/reverberation.png new file mode 100644 index 0000000000..af8493a17d Binary files /dev/null and b/src/res/homepage/ES/reverberation.png differ diff --git a/src/res/homepage/ES/settings.png b/src/res/homepage/ES/settings.png new file mode 100644 index 0000000000..54fe2a3299 Binary files /dev/null and b/src/res/homepage/ES/settings.png differ diff --git a/src/res/homepage/ES/soundcarddevicemac.png b/src/res/homepage/ES/soundcarddevicemac.png new file mode 100644 index 0000000000..0ae84cfa61 Binary files /dev/null and b/src/res/homepage/ES/soundcarddevicemac.png differ diff --git a/src/res/homepage/ES/soundcarddevicewindows.png b/src/res/homepage/ES/soundcarddevicewindows.png new file mode 100644 index 0000000000..23f699a0e2 Binary files /dev/null and b/src/res/homepage/ES/soundcarddevicewindows.png differ diff --git a/src/res/homepage/audiofader.jpg b/src/res/homepage/audiofader.jpg deleted file mode 100644 index b819e76817..0000000000 Binary files a/src/res/homepage/audiofader.jpg and /dev/null differ diff --git a/src/res/homepage/audiofader.png b/src/res/homepage/audiofader.png new file mode 100644 index 0000000000..f06b3bed88 Binary files /dev/null and b/src/res/homepage/audiofader.png differ diff --git a/src/res/homepage/bufferdelaydependency.jpg b/src/res/homepage/bufferdelaydependency.jpg deleted file mode 100644 index 9270feaf7c..0000000000 Binary files a/src/res/homepage/bufferdelaydependency.jpg and /dev/null differ diff --git a/src/res/homepage/bufferdelaydependency.png b/src/res/homepage/bufferdelaydependency.png new file mode 100644 index 0000000000..3cee1074a4 Binary files /dev/null and b/src/res/homepage/bufferdelaydependency.png differ diff --git a/src/res/homepage/bufferdelaywindows.jpg b/src/res/homepage/bufferdelaywindows.jpg deleted file mode 100644 index 1b78a3d1d9..0000000000 Binary files a/src/res/homepage/bufferdelaywindows.jpg and /dev/null differ diff --git a/src/res/homepage/bufferdelaywindows.png b/src/res/homepage/bufferdelaywindows.png new file mode 100644 index 0000000000..95e8834230 Binary files /dev/null and b/src/res/homepage/bufferdelaywindows.png differ diff --git a/src/res/homepage/centralserveraddress.png b/src/res/homepage/centralserveraddress.png index d6d69ef4b6..360bc51770 100644 Binary files a/src/res/homepage/centralserveraddress.png and b/src/res/homepage/centralserveraddress.png differ diff --git a/src/res/homepage/chat.jpg b/src/res/homepage/chat.jpg deleted file mode 100644 index e55da21b56..0000000000 Binary files a/src/res/homepage/chat.jpg and /dev/null differ diff --git a/src/res/homepage/chat.png b/src/res/homepage/chat.png new file mode 100644 index 0000000000..4b3b7f8b51 Binary files /dev/null and b/src/res/homepage/chat.png differ diff --git a/src/res/homepage/connect.jpg b/src/res/homepage/connect.jpg deleted file mode 100644 index b39d43cee3..0000000000 Binary files a/src/res/homepage/connect.jpg and /dev/null differ diff --git a/src/res/homepage/connect.png b/src/res/homepage/connect.png new file mode 100644 index 0000000000..d44ec3707a Binary files /dev/null and b/src/res/homepage/connect.png differ diff --git a/src/res/homepage/displaychannellevels.png b/src/res/homepage/displaychannellevels.png deleted file mode 100644 index a759d43365..0000000000 Binary files a/src/res/homepage/displaychannellevels.png and /dev/null differ diff --git a/src/res/homepage/faders.jpg b/src/res/homepage/faders.jpg deleted file mode 100644 index 9621b9e523..0000000000 Binary files a/src/res/homepage/faders.jpg and /dev/null differ diff --git a/src/res/homepage/faders.png b/src/res/homepage/faders.png new file mode 100644 index 0000000000..8de577a938 Binary files /dev/null and b/src/res/homepage/faders.png differ diff --git a/src/res/homepage/fadertagtooltip.jpg b/src/res/homepage/fadertagtooltip.jpg deleted file mode 100644 index 163bb581e9..0000000000 Binary files a/src/res/homepage/fadertagtooltip.jpg and /dev/null differ diff --git a/src/res/homepage/fadertagtooltip.png b/src/res/homepage/fadertagtooltip.png new file mode 100644 index 0000000000..a1494ece5c Binary files /dev/null and b/src/res/homepage/fadertagtooltip.png differ diff --git a/src/res/homepage/inputlevel.jpg b/src/res/homepage/inputlevel.jpg deleted file mode 100644 index 7ee049c08b..0000000000 Binary files a/src/res/homepage/inputlevel.jpg and /dev/null differ diff --git a/src/res/homepage/inputlevel.png b/src/res/homepage/inputlevel.png new file mode 100644 index 0000000000..0fac889d5e Binary files /dev/null and b/src/res/homepage/inputlevel.png differ diff --git a/src/res/homepage/jamulusbannersmall.png b/src/res/homepage/jamulusbannersmall.png index b6f4d6ca61..49c981183e 100644 Binary files a/src/res/homepage/jamulusbannersmall.png and b/src/res/homepage/jamulusbannersmall.png differ diff --git a/src/res/homepage/led.png b/src/res/homepage/led.png index 08e7efcd6c..f31fb2eacd 100644 Binary files a/src/res/homepage/led.png and b/src/res/homepage/led.png differ diff --git a/src/res/homepage/main.jpg b/src/res/homepage/main.jpg deleted file mode 100644 index 2a860b722f..0000000000 Binary files a/src/res/homepage/main.jpg and /dev/null differ diff --git a/src/res/homepage/main.png b/src/res/homepage/main.png new file mode 100644 index 0000000000..12be74a6d0 Binary files /dev/null and b/src/res/homepage/main.png differ diff --git a/src/res/homepage/manual.md b/src/res/homepage/manual.md index 96f801a72a..9e4afe4b1d 100644 --- a/src/res/homepage/manual.md +++ b/src/res/homepage/manual.md @@ -1,244 +1,5 @@ Jamulus Help (Software Manual) ============================== -Main Window ------------ - -![Main window](main.jpg) - -### Status LEDs - -![LEDs](led.png) - -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 large for jamming. - -The Buffer 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: - -- The network jitter buffer is not large enough for the current network/audio interface jitter. -- The sound card buffer delay (buffer size) is set to too small a value. -- The upload or download stream rate is too high for the current available internet bandwidth. -- The CPU of the client or server is at 100%. - -### Input level - -![Input level](inputlevel.jpg) - -The input level indicators show the input level of the two stereo channels of the current selected audio input. -Make sure not to clip the input signal to avoid distortions of the audio signal. - -### Chat button opens the Chat dialog - -![Chat dialog](chat.jpg) - -Press the Chat button to open the Chat dialog. The chat text entered in that dialog is transmitted to -all connected clients. If a new chat message arrives and the Chat dialog is not already open, it will -be opened automatically at all clients. - -### My Profile button opens the Musician Profile dialog - -![My profile dialog](profile.jpg) - -Press the My Profile button to open the Musician Profile dialog. In this dialog you can set your Alias/Name -which is displayed below your fader in the server audio mixer board. If an instrument and/or country is set, -icons for these selections will also be shown below your fader. The skill setting changes the background of -the fader tag and the city entry shows up in the tool tip of the fader tag. This tool tip is shown in the following picture. - -![Fader tag tool tip](fadertagtooltip.jpg) - -### Connect/disconnect button - -Push this button to connect to a server. A dialog where you can select a server will open. If you are connected, -pressing this button will end the session. - -![Connect dialog](connect.jpg) - -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. - -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. - -Alternatively, you can enter an IP address or URL of the server running the Jamulus server in the server address -field. An optional port number can be added after the IP address or URL using a colon as a separator, e.g, -jamulus.dyndns.org:22124. A list of the most recent used server IP addresses or URLs is available for selection. - -### Reverberation effect - -![Reverberation](reverberation.jpg) - -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. - -The reverberation effect requires significant CPU so 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 switched off and does -not cause any additional CPU usage. - -### Local audio pan / balance control - -![Local audio pan / balance control](audiofader.jpg) - -With the balance control, 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 L -x, where x is the current attenuation indicator. - -### Server audio mixer - -![Audio faders](faders.jpg) - -In the audio mixer frame, a fader is shown for each connected client at the server, including yourself. -The faders allow you to adjust the level of what you hear without affecting what others hear. -The VU meter shows the input level at the server - that is, what you are sending. - -Using the Mute checkbox prevents the indicated channel being heard in your local mix. - -The solo checkboxes allow you to hear only one, or several, channels, with those not soloed being muted. - -Settings Window ---------------- - -![Settings](settings.jpg) - -### Sound card device - -![Sound card device Windows](soundcarddevicewindows.jpg) -![Sound card device Mac](soundcarddevicemac.png) - -The ASIO driver (sound card) can be selected using Jamulus under the Windows operating system. If the selected ASIO -driver is not valid an error message is shown and the previous valid driver is selected. Under the Mac operating -system the input and output hardware can be selected. - -### Input/output channel mapping - -![Channel mapping](channelmapping.png) - -If the selected sound card device offers more than one input or output channel, the _Input Channel Mapping -and Output Channel Mapping_ settings are visible. For each Jamulus input/output channel (left and right channel) -a different actual sound card channel can be selected. - -### Enable Small Network Buffers - -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 128 samples. The smaller the network buffers, the -lower the audio latency. But at the same time the network load increases and the probability of audio dropouts -also increases. - -### Buffer delay - -![Buffer delay](bufferdelay.png) - -The buffer delay setting is a fundamental setting of the Jamulus software. This setting has influence on many -connection properties. Three buffer sizes are supported: - -- 64 samples: This is the preferred setting since it provides the lowest latency but does not work with all sound cards. -- 128 samples: This setting should work for most available sound cards. -- 256 samples: This setting should only be used if only a very slow computer or a slow internet connection is available. - -Some sound card drivers do not allow the buffer delay to be changed from within the Jamulus 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. - -![Buffer delay Windows](bufferdelaywindows.jpg) - -On Linux, use the Jack configuration tool to change the buffer size. - -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 a red light in the status indicator (drop outs) and the -higher the upload rate and the lower the overall delay. - -![Buffer delay dependencies](bufferdelaydependency.jpg) - -The buffer setting is therefore a trade-off between audio quality and overall delay. - -### Jitter buffer with buffer status indicator - -![Jitter buffer](jitterbuffer.png) - -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). - -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 below the jitter buffer size faders. -If the light turns to red, a buffer overrun/underrun took place and the audio stream is interrupted. - -The jitter buffer setting is therefore a trade-off between audio quality and overall delay. - -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 Auto check is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). - -### Audio channels - -![Audio channels](audiochannels.png) - -Select the number of audio channels to be used for communication between client and server. There are three modes -available. The mono and stereo modes use one and two audio channels respectively. In mono-in/stereo-out mode -the audio signal which is sent to the server is mono but the return signal is stereo. This is useful if the sound card has 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. - -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. - -In 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. - -### Audio quality - -![Audio quality](audioquality.png) - -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 upload rate does not exceed the available bandwidth -of your internet connection. - -### New client level - -![New client level](newclientlevel.png) - -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 of -that client was already stored. - -### Fancy skin - -![Fancy skin](fancyskin.png) - -If enabled, a fancy skin will be applied to the main window. - -### Display channel levels - -![Display channel levels](displaychannellevels.png) - -If enabled, the channel input level for each connected client will be displayed in the mixer. - -### Central server address - -![Central server address](centralserveraddress.png) - -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. - -### Current connection status parameter - -![Indicators](indicators.png) - -The ping time is the time required for the audio stream to travel from the client to the server and back again. -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. - -The overall delay is calculated from the current ping time and the delay which is introduced by the current buffer settings. - -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 your internet connection by, e.g., using -[speedtest.net](http://speedtest.net)). +## The manual has moved to https://jamulus.io/wiki/Software-Manual diff --git a/src/res/homepage/mediawikisidebarlogo.png b/src/res/homepage/mediawikisidebarlogo.png index 6d34be850d..3b232523aa 100644 Binary files a/src/res/homepage/mediawikisidebarlogo.png and b/src/res/homepage/mediawikisidebarlogo.png differ diff --git a/src/res/homepage/profile.jpg b/src/res/homepage/profile.jpg deleted file mode 100644 index 7a7063f1d0..0000000000 Binary files a/src/res/homepage/profile.jpg and /dev/null differ diff --git a/src/res/homepage/profile.png b/src/res/homepage/profile.png new file mode 100644 index 0000000000..f52d203b9a Binary files /dev/null and b/src/res/homepage/profile.png differ diff --git a/src/res/homepage/reverberation.jpg b/src/res/homepage/reverberation.jpg deleted file mode 100644 index 900804414f..0000000000 Binary files a/src/res/homepage/reverberation.jpg and /dev/null differ diff --git a/src/res/homepage/reverberation.png b/src/res/homepage/reverberation.png new file mode 100644 index 0000000000..51307adddf Binary files /dev/null and b/src/res/homepage/reverberation.png differ diff --git a/src/res/homepage/settings.jpg b/src/res/homepage/settings.jpg deleted file mode 100644 index 47597a7349..0000000000 Binary files a/src/res/homepage/settings.jpg and /dev/null differ diff --git a/src/res/homepage/settings.png b/src/res/homepage/settings.png new file mode 100644 index 0000000000..fd7c08f609 Binary files /dev/null and b/src/res/homepage/settings.png differ diff --git a/src/res/homepage/soundcarddevicewindows.jpg b/src/res/homepage/soundcarddevicewindows.jpg deleted file mode 100644 index 43bcc0f3d9..0000000000 Binary files a/src/res/homepage/soundcarddevicewindows.jpg and /dev/null differ diff --git a/src/res/homepage/soundcarddevicewindows.png b/src/res/homepage/soundcarddevicewindows.png new file mode 100644 index 0000000000..b3957e380b Binary files /dev/null and b/src/res/homepage/soundcarddevicewindows.png differ diff --git a/src/res/icon.xcf b/src/res/icon.xcf deleted file mode 100755 index bd6d11e5df..0000000000 Binary files a/src/res/icon.xcf and /dev/null differ diff --git a/src/res/installerbackground.png b/src/res/installerbackground.png index 230d2fb60f..e6f885c71b 100644 Binary files a/src/res/installerbackground.png and b/src/res/installerbackground.png differ diff --git a/src/res/installerbackground.xcf b/src/res/installerbackground.xcf deleted file mode 100644 index a57764678c..0000000000 Binary files a/src/res/installerbackground.xcf and /dev/null differ diff --git a/src/res/instruments/aguitar.png b/src/res/instruments/aguitar.png index 713902e717..94125e6af1 100644 Binary files a/src/res/instruments/aguitar.png and b/src/res/instruments/aguitar.png differ diff --git a/src/res/instruments/bassukulele.png b/src/res/instruments/bassukulele.png new file mode 100644 index 0000000000..537ff9c0ec Binary files /dev/null and b/src/res/instruments/bassukulele.png differ diff --git a/src/res/instruments/bassukulele.svg b/src/res/instruments/bassukulele.svg new file mode 100644 index 0000000000..bd4c9d3cf6 --- /dev/null +++ b/src/res/instruments/bassukulele.svg @@ -0,0 +1,386 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/res/instruments/mountaindulcimer.png b/src/res/instruments/mountaindulcimer.png new file mode 100644 index 0000000000..ef8104cebf Binary files /dev/null and b/src/res/instruments/mountaindulcimer.png differ diff --git a/src/res/instruments/rapping.png b/src/res/instruments/rapping.png new file mode 100644 index 0000000000..f7d662ff8e Binary files /dev/null and b/src/res/instruments/rapping.png differ diff --git a/src/res/instruments/recorder.png b/src/res/instruments/recorder.png index 514148cc34..d9a07e48da 100644 Binary files a/src/res/instruments/recorder.png and b/src/res/instruments/recorder.png differ diff --git a/src/res/instruments/scratching.png b/src/res/instruments/scratching.png new file mode 100644 index 0000000000..514148cc34 Binary files /dev/null and b/src/res/instruments/scratching.png differ diff --git a/src/res/instruments/ukulele.png b/src/res/instruments/ukulele.png new file mode 100644 index 0000000000..3e8b2d9f8d Binary files /dev/null and b/src/res/instruments/ukulele.png differ diff --git a/src/res/instruments/ukulele.svg b/src/res/instruments/ukulele.svg new file mode 100644 index 0000000000..7ed588944e --- /dev/null +++ b/src/res/instruments/ukulele.svg @@ -0,0 +1,402 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/res/instruments/vocalbaritone.png b/src/res/instruments/vocalbaritone.png new file mode 100644 index 0000000000..b37dfa47d8 Binary files /dev/null and b/src/res/instruments/vocalbaritone.png differ diff --git a/src/res/instruments/vocallead.png b/src/res/instruments/vocallead.png new file mode 100644 index 0000000000..95a7eedec6 Binary files /dev/null and b/src/res/instruments/vocallead.png differ diff --git a/src/res/jamulus-icon-2020.ai b/src/res/jamulus-icon-2020.ai new file mode 100644 index 0000000000..0dde0fe54d --- /dev/null +++ b/src/res/jamulus-icon-2020.ai @@ -0,0 +1,1377 @@ +%PDF-1.6 % +1 0 obj <>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream + + + + + application/pdf + + + icon + + + Adobe Illustrator 24.2 (Macintosh) + 2020-06-30T20:13:54+02:00 + 2020-06-30T20:13:54+02:00 + 2020-06-30T20:13:54+02:00 + + + + 256 + 256 + JPEG + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 FXYq7FXYq7FXYq7FXYqsnuILeJpp5FiiTdpHIVR8ycIBOwRKQAs7Bierfmf5esyyWvO/lH++xxjr /rt/AHMqGimeezrsvamOPL1MS1D81PMM5ItEhs0/ZKr6j/SXqv8AwuZcNFAc93XZO1cp5VFIbrzT 5juj++1K4IPVVkZF/wCBXiMvjggOQDiS1WWXORS6WeaU1lkaQ+LEn9eWgANJkTzU8WKtDd3UJBhm eIjpwYr+o4DEHmyEyORTO084+aLUj0tSnIHQSN6o+6TllUtPA9HIhrMseUj97INO/NjWYaLfW0V0 g6stYn+8cl/4XKJ6GJ5GnMx9rTH1AH7GX6R+YvlrUCEeY2Ux/YuKKv0OCV+8jMPJpJx83Y4e0cU+ vCfNkysrKGUhlO4I3BGYzn23irsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdiriQoLM aAbknoBirB/Mn5nWFkWttJVby5GxnP8Acqfam7/Rt75m4dGZby2DqtT2pGO0PUfseb6rrmratN6t /cvMa1VCaIv+qg+EZsoYow5B0mXPPIbkbQGTaXYqmFnoGuXgBtbCeVT0dY24/wDBUpkJZYjmQ3Q0 +SXKJKaw/l15vkFfqIQHpzliH4cq5UdXj73IHZ2Y9PtCIP5Yeaqf3cJ/56jI/ncbP+S83l80NP8A l55uiFfqPMeKSRt+HKuSGrxnqwl2dmH8P3JRe6Lq9jX65ZTQAftPGwX6GpTLY5Iy5Fxp4Zw+oEIL JtTsVTjQ/Net6K4+p3BMFatbSfFEf9j2+YplWTBGfMOTg1eTF9J27uj03y1+YWk6uUt7illfGgEb n4HP+Q/8D+OazNpJQ3G4d7pu0IZNj6ZMqzFdg7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY qhtR1Ky060ku72UQwRj4mPc9gB3J8MlCBkaDDJkjCPFI0HkXmzz3qGts1vBW202u0IPxSe8hH/Ee nzzb4NMIbncvOavXyy7DaP45sWzJdeitO0zUNSuBb2MD3Ex/ZQdB4seij3ORnMRFktmPFKZqIss9 0X8piQsusXNO5trfr8mkI/UPpzAya7+aHb4Oyesz8AzTTfLGgaaB9TsYkcf7tYc5P+DarZhzzTlz Ls8Wlxw+kBNMqch2KuxV2KuIBFD0xVJdU8m+W9SqbiyRZT/u6L929fElaV+muXQ1E48i4uXRYp8w wfXPypvoA0ukzfWoxv6EtFl+htlb8MzseuB+rZ1WfsqQ3gb+9g9za3NrO0FzE0MyGjxuCrD6DmaJ Aiw6qUTE0RRUsLFnPlD8xrmxKWWrs09l9lLg1aSMe/dl/EfhmFqNIJbx5u20faRh6Z7x7+56nBPD PCk0DrJFIOSSKQVIPcEZqyCNi7+MgRY5L8CXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqhdT1Oz0y xlvbx/TgiFSe5PZVHcnJQgZGg15csYRMpcnivmnzTe6/e+rLWO1jJFtbA7KPE+LHuc3WDAMY83l9 VqpZpWeXQJJlzisx8pfl7easEvL/AJWunmjIOkko/wAmvRfc/RmJn1YhsNy7PSdnSyeqW0fveqab pWn6ZbC2sYFghHZRuT4sTux9zmrnMyNl3+LFGAqIoIrINjsVdirsVdirsVdirsVdiqWa55c0nWrf 0b6EMwH7uddpE/1W/gdstx5pQOzRn00MoqQeS+afJepaDIZD/pFgxpHdKOlegcfsnNrg1EZ+953V aKWI3zj3sdzIcJlXkrzrPoc4trkmTS5D8adTET+2n8RmLqNOJix9TsNFrTiNH6PuexQTwzwpPC4k ikUNG67gqRUEZqCKNF6WMgRY5L8CXYq7FXYq7FXYq7FXYq7FXYq7FXYq07pGjSOwVEBZmJoABuST iAgmni3nfzZJruoFISV063JFunTkehkYeJ7eAzdabBwDfm8xrtWcstvpHL9bGsyHBei+Q/IIkEeq 6xFWM0a1tHHXweQeHgM1+q1VemLutB2ffrmPcHpWa13jsVdirsVdirsVdirsVdirsVdirsVWTQQz xPDMiyRSAq8bCqkHsQcINbhEogii8j88+R30ZzfWIL6Y5oyndoWPY+KnsfoPvttNqePY/U85rtD4 Xqj9P3MPzLdazn8ufN5sbldIvX/0KdqW7t0jkY9P9Vj9x+nMLV6fiHEObtuzdZwHgl9J5eT1bNU9 A7FXYq7FXYq7FXYq7FXYq7FXYq7FWAfmh5mMEC6JavSWcB7xh1Ef7Kf7Lqfb55n6LDZ4i6ftTU0P DHXm8vzZuhZt+XflAalcfpS+SthbtSKNhtLIP+NV7+PTxzC1eo4Rwjm7Ts7R8Z45fSPtes5qnonY q7FXYq8+81/mXcWOoyWGlRRubdik1xKCwLj7SqoK9DtU5sMGjEhcnTavtMxlwwHJFeTPzCk1e9Gn ajEkV1ICYJYqhWKipUqSaGgr1yGo0nALHJs0XaJyS4ZDdm+YTtXYq7FXYq7FXYq7FXYqsnhinheG ZBJFIpWRGFQVOxBwg1uESiCKLxXzp5Wk0HUqRgtYXFWtZDvTxQnxXNzp8/HHzeX1ulOKW30nkx3M hwnsv5e+ZTq+k/V7h631kAkhJ3dP2H/gf7c0+rw8ErHIvTdn6nxIUfqiyrMV2DsVdirsVdirsVdi rsVWySRxIZJGCIu7OxAAHuThAtBIG5Y/qP5geVrElTdi5kH7FuPU/wCGHwf8Nl8NLkl0pw8naGGP W/cx67/N62BIs9Odx2aaQJ/wqh/15kR0B6lw59sD+GLzzUr+41C/nvbg1muHLt4CvQD2A2GbCERE UHTZMhnIyPMq+haPPq+q29hDsZW+N/5UG7N9AwZcghEkssGE5JiIe8WNlb2NnDaWy8IIFCRr7Dx9 z3zRSkZGy9bCAhERHIK+RZuxV2Kpb5k1ddJ0S6vj9uNKQg95G+FPxOWYcfHIBo1Obw8Zk8Ed2di7 kszElmO5JPU5vnkSbZJ+XenS3nmm2dNo7XlPK3gFFAPpYjMbVzqB83N7OxmWUeW72nNM9Q7FXYq7 FXYq7FXYq7FXYqlnmPQ4Na0maxloGYcoJD+xIPst/A+2W4chhK2jU4BlgYl4Rc201rcS206lJoWK SIeoZTQ5vIkEWHkpRMSQeYTPyprjaLrdveVPoE+ncqO8Tfa+77X0ZXnx8cSG/SZ/CyCXTq92VlZQ ymqsKgjoQc0T1obxV2KuxV2KuxVQvb6zsbdrm7mSCBPtSOaD5e59slGJkaDCc4xFyNBgOvfmuilo dFh59vrcwIH+xj6/8F92Z+LQ9ZOo1Ha3TGPiWB6nrerapJzv7qSc9QrH4B/qoKKPoGZ0McY8g6nL nnkPqNoHJtLsVdir1X8rNA+radJq0y0mvPggr1EKnr/smH4DNXrctnhHR6DsrT8MeM8z9zOswXbO xV2KuxV5n+bOs857bSIz8MQ9e4/1mqEH0LU/Tmy0OPYydF2tmsiA97zzNg6Z65+V+jfU9Ea+kWk1 +3JSeoiTZfvNT92anW5LlXc9H2Xh4cfEecvuZnmG7N2KuxV2KuxV2KuxV2KuxV2KvLfzW0MQX0Or RLSO6/dz06eqg+E/7JR+GbTQ5LHD3Og7VwVITHX72BZnOoe0fl1q51Dy1Cjms1kfq7/6qgFD/wAC QPozTavHwz970/Z2bjxDvjsyfMZz3Yq7FXYqxrzX5407QlMCUudRI+G3U7LXoZD2+XXMnBpjPfkH B1euji25y7nkus69qmsXJuL+YyEfYjGyIPBV7ZtceKMBQedzZ55Dcil+WNKO0vRNW1WX07C2ecg0 ZlFEX/Wc0UfSchPJGPMt2LBPIaiLZppf5SXTgPqd4sPcwwDm30u1AD9BzDnrx/CHZ4uyCfrNe5k9 l+XHlS2A5WzXLD9uZ2P4LxX8MxZavIetOdDs3DHpfvTi30HQ7f8AuNPtoz/MsSA/fSuVHLI8yXJj p8ceUR8kcAFAAFAOgGVtzsVdirsVU7i4it7eW4mbjFCjSSN4KoqThAs0xlIRBJ5B4Bq+pS6lqdzf y/buJC9PAfsr/sVoM3+OHDEB5DNkM5mR6u0fTZdT1S2sIvt3EgSvgvVm/wBitTgyT4YkrhxmcxEd Xv8AbwRW9vHbwrwihVUjUdlUUAzQk2bL2EYiIocgqYEuxV2KuxV2KuxV2KuxV2KuxVJfOWljUvLd 7ABWVE9aHx5x/EKfMCn05dp58MwXF1uLjxEPCs3jybOfyn1H0dZubFjRLqLko/y4jUf8KzZha6Fx B7nbdk5KmY94+56tmqegdirsVYR558+rpvPTdMYPfkETTDcQ17Dxf9WZum0vF6pcnVa7X8Hph9X3 fteUySSSyNJIxeRyWd2NSSdySTm1Ap58kk2VS1tbm7uEt7aNpp5DRI0FSTglIAWUwgZGhuXpHlv8 rYIglzrjerJ1Fmh+Af67D7XyG3zzXZtaTtF3em7LA3yfJntvbW9tCsNvEsMKCiRoAqgewGYBJJsu 3jERFDYKmBk7FXYq7FXYq7FXYqwz80dZ+p6IthG1Jr9qNQ7iJKFvvNB9+Zmix3K+51nambhx8I5y +55Hm2eceh/lNo3Oe51eRfhjH1e3J/mahc/QKD6c1+uybCLuuycO5mfcHpma13jsVULy/srKEzXk 8dvEP25GCj6K5KMTLYMJ5IxFyNMQ1f8ANTRrbkmnxPeyDo5/dxfeRyP/AAOZePQyPPZ1ubtWEfpH EwnV/P3mXUuSG5+qwH/dVv8AAKe7bufvzNx6WEelurzdoZZ9aHkh9C826zpN9HOtzJLByHr27sWV 177E7GnQ5LLgjMVTDBq545Xe3c9zRg6hl+ywBHyOaN6wFvFXYq7FXYq4gEUPTFXz7rNl9R1e9swK LBNJGv8AqqxC/hm/xy4ogvHZocMzHuKM8nXZtfNGmy1oDOsZPtL+7P8AxLIaiNwLbo58OWJ8/ve7 Zo3rHYqxHz95xGjW31Kzb/cncLUMKH0kO3M/5R/Z+/MvS6fjNn6XXa/WeEOGP1H7Hj7MzMWYlmY1 ZjuSTm3ebR2i6Lf6xfpZ2acpG3dz9lF7sx7AZDJkEBZbcGCWSXDF7N5Z8qaboNtwgX1LpxSe6YfE 3sP5V9s02bPLId+T02l0kcI2596dZS5TsVdirsVdirsVdirsVdirxHz1rP6V8x3Do3K3t/8AR4PD ihNT9LVObvTY+GAeW1+bxMp7hskCIzuqICzMQFUdSTsBl7hgW968vaVHo+h21kSAYY+U7125n4nN fCp+7NFlnxyJeu0+IY8Yj3Jdq/5g+WtO5ILj63OP91W/x7+77J+OWY9LOXSmjN2hih1s+TCdX/NP W7rklhGljEdg395LT/WYcR9C/Tmbj0URz3dXm7VyS+n0sRu728vJjNdzPPKeryMWP45lxiBsHXTn KRsm1DCwdiqceVNCfWtags6H0QfUuXH7Ma9fv6D55Vny8EbcnSYPFyCPTq93AAAAFANgBmietdir sVdirsVdirxP8w4BF5uvwOj+m4/2Uak/jXN1pDeMPLdoxrNL8dEitJjDdQzA0MTq9f8AVIOXyFin EgaIL6JznntEu8wa3b6LpU1/PvwHGKPu8h+yv+fbLMWMzlQaNRnGKBkXhN/fXN/eS3l0/OeZizt8 +w9h2zeRiIig8nkyGcjI8yu03TrvUr6KytE5zzGijsO5JPYAbnGcxEWU4sZnIRjzL2/y15cstC09 baABpWobi4pRpH8fYDsM0mbMZmy9VptNHFGhz6ptlTkOxV2KuxV2KuxV2KuxV2KpL5x1n9EeX7m5 VuM7j0bfx9STYEf6oq30Zdp8fHMBxdZm8PGT16PCs3jyaJ069exvYbyNEkkgbnGsgJXkPskgEdDv kZx4hTPHPgkJDoitV8ya5qpP167eVDv6QPGMf7BaLkYYYx5Bsy6nJk+opZljQ7FU10nyxrurEGys 3eM/7uYcI/8Ag2oD9GVZM0Icy5GLS5Mn0hB6jZGxvZrRpEleBuEjx1Kch9oAkCtDtk4S4hbXkhwy Me5DZJrex/lx5e/Rmii6mWl5f0kavVY/91r+PI/P2zUavLxSocg9L2bp+CFnnJlmYjsXYq7FXYq7 FXYq8d/M8Aea5Kd4Yq/dm40X92812p/ffAMSzKdc+j8517Z5B+ZXmA6jrJsYWraWBKbdGl/bP0fZ +/Nvo8XDG+peb7T1HHPhHKP3sPzLda9g/Lvyuul6aL+5T/T7xQ2/WOI7qvzPU/2ZqNXm4pUOQek7 O0vhx4j9UvuZfmI7J2KuxV2KuxV2KuxV2KuxV2KvLPzY1SSXU7fTRtFbR+q3u8n9FH45tNDComXe 6DtbLcxHuYHmc6h2KoizsL69l9GzgkuJf5I1LH6aYJSEdyWcMcpGoi2YaR+VWsXNH1GVLKM9UH72 T7geI/4LMPJrojlu7LD2VOX1HhZtpHkLy1pnFltvrM4/3dcUkNfZacB92YWTVTl1p2mHQYodLPmu 866+uiaFJJEQt1N+5tFHUMRuw/1Bv86Y6fFxy8k63UeFjsczsHiBJJqdyepzdPLMg8j+XzrOuRpI tbO2pNcmmxAPwp/sjt8q5j6nLwR8y5mh0/i5N/pHN7cAAKDpmlepdirsVdirsVdirsVeLfmNMJPN 96B0jESD6I1J/E5udIP3YeX7SN5j8PuY7BEZZ44h1kYKKe5pmSTQcOIs09380auNI0K7vgR6qJxg B7yP8KfcTXNFhx8cgHrNVm8PGZPBWZmYsxJYmpJ3JJzevJMj8h6ANX16MSrytLX99cA9DQ/Ch/1m /CuY+qy8EPMuboNP4mTfkNy9rzSvUOxV2KuxV2KuxV2KuxV2KuxV2KsF/MHyTfarcJqWmgSXCoI5 rckKWCn4WUmgrvvU5naXUiI4ZOp7Q0Msh4o82Jad+XHmi7kpJAtpGOskzAfcq8mP3Zlz1cB1t12P s3LI7ivezPSPys0S14vfyPfSjqp/dxf8Cp5H6WzDya2R5bOzw9lY4/V6mXWtnaWkQhtYUgiHRI1C r9wzDlIncuyhARFAUrYGTsVeKefPMP6Z1x/SblZWtYrbwND8b/7Ij7qZutLi4I+ZeX1+o8TJt9I5 MczIcF7b5G8vfobQ41lWl5c0lufEEj4U/wBiPxrml1OXjl5B6nQ6fwse/wBR5shzHc12KuxV2Kux V2KuxV4D5ivBea9qFyDVZJ5Ch/yQxC/gM32KNQA8nkNTPiySPmqeVrU3XmPTYeoNxGzD/JRuTfgu DPKoE+SdLHiyxHmzP83NTP8AoOmK229zKv3on/G2Yeghzk7PtfL9MPi83zYukexflnpIsvLq3LLS a/Yyse/AfCg+74vpzUazJxTruel7Mw8OK+smW5iOxdirsVdirsVdirsVdirsVdirsVdirsVdirsV dirFvzD8w/orRGghal5fViiod1Snxv8AdsPc5laTFxSvoHX9o6jw8dD6pPGc3DzLK/y68vfpTWhc TLWzsaSSA9Gf/da/eKn5Zi6vLwxocy7Ds7T+Jks/TF7JmnemdirsVdirsVdirsVSzzNqY0zQb29r R44yIv8AjI/wp/wxGW4YcUwGjVZeDGZPA83ryDNPyr08z+YXuyPgs4WIP+XJ8A/4XlmHrZ1Cu92n ZWO8nF/NCXfmBem6813pr8MJWFB4cFAP/DVyzSxrGGjtCfFmPlskNtA9xcxW8f8AeTOsafNjQfry 8mhbiRjZAHV9DWtvHbW0VvGKRwosaD/JUUH6s5+Rs29lGIiAB0VMDJ2KuxV2KuxV2KuxV2KuxV2K uxV2KuxV2KsAsPzOW31Cew1mKiwyvGt3CCdlYj40/iv3Zny0dgGLp8fadSMZjkeYZva6jY3dr9bt p0ltqVMqsCoAFTXwp3rmFKBBojd2sMkZCwdniXm7Xm1vW5roE/Vk/dWq9KRqdjT/ACvtZusGLgjX V5bWajxchPTok6I8jqiKWdyFVRuSTsAMuJcYC3uvlPQk0TRILSn79v3ly3jKwFf+B+yPlmjz5eOV vWaTB4WMR69U4ylyXYq7FXYq7FXYq7FXm/5s62D9W0eJun+kXIH0iNT+J+7NjocfOTpO1s/KA95e cZsXSPYPyw0k2fl76060lv3Mnv6a/Cg/WfpzUa3Jc67npOy8XDjvrJ5Vq05uNVvJzuZp5ZP+CcnN rjFRA8nn80rmT3kpl5Ithcea9NjIrxl9T/kUpk/41yvUmsZb9DHizRHn9z3LNG9W7FXYq7FXYq7F XYq7FXYq7FXYq7FXYq7FXYq+f9fUpruooeq3UwPzEjZv8X0D3PH6gfvJe8/eh7a+vLZZUt53iSdT HMqMQHUihDAdckYg82Ecko3R5qGFgzb8sPL313U21Sda21iR6VejTHcf8AN/nTMLW5eGPCOZdr2X p+KfGeUfves5qnoXYq7FXYq7FXYq7FUJqup22mafPfXJpFAvIjuT0Cj3Y7ZOEDI0GvLlGOJkejwX U9RuNS1Ce+uDWa4cu3gPBR7KNhm9hARAAeRy5DORkeZVtC0mbV9WtrCKo9ZvjcfsoN3b6FwZcghE llgwnJMRHV75BBFBBHBEoSKJQkaDoFUUA+7NCTZt6+MQBQfOhJJqeudC8Um3lK8Fn5l06cmiidUY +CyfAT9zZVnjcCHJ0k+HLE+b3jNE9a7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq8E8zqV8yaqD 1+tzn6DISM32H6B7g8jqh+9l/WP3pXljjuxVNNE8yaxosvOwnKIxq8LfFG3zU/rG+V5MMZ8w5GDU zxH0l6V5e/MvSNQ4w6gBYXR25MawsfZ/2f8AZffmty6OUdxuHd6ftOE9pek/YzEEEVHTMN2bsVdi rsVdirsVeQfmF5tGr3v1G0eunWrH4gdpZBsW/wBUdF+/NvpMHALPMvN9oavxJcMfpH2sPzLda9Y/ LLy2bGwbVblKXN4oEIPVYeoP+zO/ypmq1mbiPCOQeh7M03DHjPOX3M3zCdq+cWUqxUihBoR7jOie KcCQQQaEbgjFXv3l7U11TRbO+Bq00YMlO0g+Fx/wQOaHLDhkQ9fp8viYxLvTDK252KuxV2KuxV2K uxV2KuxV2KuxV2KuxV2KvCPN6svmjUwdj9Yc/QTUZvcH0D3PJawfvZe9J8tcZ2Kpjo2garrFx6Fh AZCPtyHZEHizdBleTLGAst2HTzyGoh6j5Z/LvStK4XF5S9vhvyYfukP+Qp7+5/DNZm1cpbDYO/03 Z0Me8vVJluYjsXYq7FXYq7FWC/mZ5omsbcaRa8kmuk5TzUIpESRxU+LU39sztHhEjxHo6ntPVGI4 BzP3PKs2jz6deT7DTL7zBbW+oyiO3JqFbpI4+zHXty/synUSlGBIcrR44zyASO33+T3UAAAAUA2A GaN6x2Kvn3W7c22s31uRT0riVB8g5Azf4zcQfJ47PHhnIeZQWTanof5U6+I5ptFmb4ZazWtf5gPj X6QK/Qc1+uxfxB3PZWoonGfeHpma13rsVdirsVdirsVdirsVdirsVdirsVdirsVeGedlK+a9SBFD 6tfoKgjN5pv7sPKa7++l70mjiklkWOJDJI5oiKCWJPYAZcTTigEmgz7y1+V083C51tjDFsVs0P7w /wCu37PyG/yzAza0DaLt9N2WTvk28npFlY2llbrbWkKwQJ9mNBQfP5++a6UjI2XdwhGIqIoK2RZu xV2KuxV2KuxVLfMHl+w1ywa0u1oRvDMPtxt4r/Ed8txZTA2GjUaeOWNF4r5g8v3+h37Wl2tQd4Zh 9mRfEfxHbNziyiYsPL6jTyxSopaCQajYjocsaXrf5e+cDqtt+jr166hbrVJGO8sY2r/rL3+/xzU6 vT8J4hyei7O1niDhl9Q+1meYbs3i/wCY9ibXzXctSiXKpOn+yXi3/DKc3OkleMeTzHaUOHMfPdjG ZLgKtrcz2tzFcwOUmhYPG47MpqMEogiiyjIxII5h7p5Y8w22u6VHdx0WYfBcw90kHX6D1GaPNiMJ U9ZpdQMsOIc+qbZU5DsVdirsVdirsVdirsVdirsVdirsVdiry/WvJWs635w1B40+r2XqJyu5B8P9 2v2B1c/Lb3zZ49TGGMd7oc2inlzyraPf8GbeXvKOj6HGPq0fqXJFHupKGQ+NP5R7DMLLnlPnydpp 9JDENufenWUuU7FXYq7FXYq7FXYq7FXYql2vaFY61p72d2vXeKUD4o3psy/575ZiymBsNGowRyx4 S8N1bS7vS9QmsbpaTQtQkdGHUMvsRvm8xzEhYeVzYjjkYnmFmnX9zp99De2zcZ4GDoe23UH2I2OM 4iQosceQwkJDmHvWj6pb6pplvfwf3c68uPdW6Mp91O2aLJAxkQXrsOUZICQ6sK/NvSy9rZ6mi/3L GCYj+V/iSvsCD9+ZugnuYur7XxWBPu2eY5snROxVN/LXmO80HUVuoPjib4biAmgdP4EdjlWbCJii 5Om1MsUrHxe26Vq1jqtkl5ZSCSJ+virU3Vh2IzSzxmJovU4ssckeKPJF5BsdirsVdirsVdirsVdi rsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirCPzQ8vrd6Wuqwr/AKTZbSkdWhY7/wDAk1+/ M3RZalw9C6rtTT8UOMc4/c8nzavPPSPyl1g/6XpEjbD/AEi3B+hZB/xE/fmu12PlJ3fZGbnA+9nO u6XHqukXVg9P36EIx7ON0b6GAzCxT4ZAu1z4hkgY97wGaGSGZ4ZVKSxsUdT1DKaEZvgb3eQkCDRW YodiqbeXfMuo6FeevaNyjanr27fYkA8fAjscqy4RMUXI0+plilY+T2Hy75q0rXYOdq/C4UVltXI9 Rf6j3GajLglA7vS6bVQyjbn3JxlLkuxV2KuxV2KuxV2KuxV2KuxV2KqF7fWdjbtcXkyQQr1dyAPk PE+2SjEyNBhPJGAuRoNafqFnqFnHeWcglt5RVHAI6Gh2NCN8ZwMTRXHkjOPFHkiMizdirsVdirsV dirsVdirsVdiqnc28Vzby28o5RTI0ci+KsKHCDRtjKIkCD1fPd7bNa3k9qxDNBI8TEdCUYr/AAzo ImwC8dOPDIjuTXyVfGy80afLWivKIX8KS/Bv/wAFXKtTG4FyNFk4csT5183ueaN6t5V+aPl02uoL q8C/6PdnjPToswHX/ZgffXNpostjhPR5/tTT8MuMcj97BcznUuxV2Kqtvc3FtMk9vI0U0ZqkiEqw PsRgIBFFlGRibHNn3l/81ZowkGtReqo2+txAB/8AZpsD9FPlmBl0I5xdvp+1SNsgvzZ/pmtaVqkX q2FylwvVgp+Jf9ZTRl+kZgTxyjzDuMWaGQXE2jcg2uxV2KuxV2KuxV2KqF7qFjYwme8nS3iH7cjB R8hXqclGJkaAYTyRgLkaYNr/AOattFyh0aH136fWpQVQf6qbMfppmdi0JO8nVajtUDaAvzeeanq+ panOZ7+4eeTtyOy17Ko2UfLNhDHGIoB02XNKZuRtl35YeZfql8dIuHpb3bVtyeizdKf7MbfOmYet w2OIcw7HsvU8MuA8jy971XNW9A7FXYq7FXYq7FXYq7FXYq7FUt8x6zHo+jXN89Oca0hU/tSNsg+/ r7Zbhx8cgGjU5hjgZPBHdnYuxqzEliepJzevIk2ut5jDcRTL9qJ1cfNTXARYpMTRBfRec89ohNV0 y11PT57G5FYZ14k9weoYe4O4ycJmJsNeXEMkTE8i8J1rSLvSNSmsLofvIj8Ljo6H7Lr7EZvMeQTj YeTz4TjkYlA5NpdirsVdiq+GaaGRZYXaORd1dCVYH2IxIvmkSINhkmnfmN5psgFa4W7jHRbheR/4 McX+85jT0mM9Kc7H2llj1v3sis/zeSgF5pxB7vDJX/hWA/4lmPLQdxc2HbH86Kaw/mr5ZcDmlzEe /KNT/wARZsqOhn5N47VxHvVx+ZnlIrU3EgP8piev4CmR/J5O5n/KeHv+xRl/NPyugqouJPZYwP8A iTLkhop+TE9q4h3pbd/m9aAEWmnSOezSuE/BQ/68sjoD1LRPtgfwxY/qP5neZrqqwvHZof8AfKVa n+s/L8KZkQ0cBz3cPJ2nlly9LGLq8u7uUzXUzzynq8jFz95zJjEDYODOcpGybUcLB2KrlZkYMpKs pqrDYgjuMUgvafJPmuLXdPCSsBqVuALmPpyHQSL7Hv4H6M02pwcB8np9Dqxljv8AUOf62SZjOc7F XYq7FXYq7FXYq7FXMyqpZiAoFSTsABirxvz95sGtX4t7Vq6dakiI/wC/H6GT5dl/tzcaXBwCzzLz PaGr8WVD6QxTMp17sVfR+c69s7FWPec/KcOv2H7uiahACbaU7A+KN7H8MyNPnOM+Tha3SDNH+kOT xa5tp7aeS3uEMc0TFJI26hh1GbkEEWHmJRMTR5qWFi7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY qidO1G8068jvLOUxTxmqsPxBHcHwyM4CQotmPJKEuKJ3eveVPPmna0iW85W11LoYWNFkPjGT1/1e vzzU59KYbjcPR6TXxy7HaX45MozFc92KuxV2KuxV2KqN5eWtnbvc3UqwwRirSOaAYYxJNBjOYiLJ oPKvOn5gS6qHsNO5Q6d0kkOzzfP+Vfbv38M2un0vDvLm8/re0Dk9Mdo/ewvMx1bsVRmjWxutXsrY f7unjT6GcDIZDUSfJtwx4pgd5D6DzQPYuxV2KsX85eSbbXYjcQUh1ONaJJ0WQDosn8D2zK0+pMNj 9Lga3QjKLG0nj97ZXdjdSWt3E0M8Ro8bChH9QexzbxkJCw83OBiaIoqGFg7FXYq7FXYq7FXYq7FX Yq7FXYq7FXYq7FXYq2CQajrirKtD/MfX9NVYp2F9bLsEmJ5ge0g3++uYuTSQly2LsMHaWSGx9Q82 Z6d+aXly4AF0JbKTvzUyJX2ZKn/hcw56KY5bu0x9q4pc7incPm3yxMvJNUtgP8uRUP3OVyg4JjoX Kjq8R/iHzV38waCgq+pWqg9Kzxj9bYPCn3FkdRjH8UfmEvu/PnlO2B5agkhHRYg0hP0qCPxyyOly Ho0z1+GP8TGdW/NuIBk0qzLN2muDQf8AAKTX/gsyYaD+cXBy9rj+AfNgur67qurzerqFw0xH2E6I tf5VGwzOx4owGwdTmzzyG5G0vybS7FXYqy38s9NN35mjnK1iskaVj25EcFH3tX6MxdZOoV3ux7Mx cWW/5r2LNO9K7FXYq7FUo8xeV9L1239O6TjOgpDcpTmn9R7HLsWaUDs42p0sMoo8+95J5j8naxoc hM6etaVol3GCUPhy/lPsc2uHURny5vO6nRzxHfcd6RZe4jsVdirsVdirsVdirsVdirsVdirdDQNT Y7A9qj/bxS1ih2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV7F+WuhnTtBFzKvG4vyJWr1EY2jH3Et9Oa jWZOKddA9L2bg4MdnnL8BluYjsXYq7FXYq7FWnRHQo6hkYUZSKgg9iDigi2G69+WOj33KbTm+oXB 34KOUJP+r+z/ALHb2zMxa2Uee7rdR2XCe8fSfsYDq/kjzHpZZprUzQr/ALvg/eJTxNPiH0jM/HqY S5F0+bQ5cfMWPJIsvcRrFXYq7FXYq7FXYqm+k+VNf1Ug2lm5iP8Au9xwjp/rNQH6MqnnhHmXJxaT Jk+kM70L8qrKArNq831qQb/V4qrF9LbM34Zg5dcT9OztsHZURvM35J15r8pWup6D9Ts4khntQXsl QBVBpum1Nn/XvlGDOYzs9eblavSDJj4YiiOTxWSN45GjkUpIhKujChBGxBBzdAvLkVsVuKHYq7FX Yq7FXYq7FXYq7FXYq7FWR+SPLD65qq+qp+oWxD3Tdj/LH/sv1Zj6nNwR8y5uh0viz3+kc3tgAAAA oBsAM0r1LsVdirsVdirsVdirsVdiqX6j5d0PUSTe2UUznrIVAf8A4NaN+OWQyyjyLTk0+Of1AFj1 3+VnlqYkwtPbHsqOGX/hwx/HMiOtmOdFw59lYjysJZL+T8JP7rVGUeDwhv1OuWDXnucc9jjpL7P2 of8A5VBc/wDVzT/kUf8AmvJfnx3Mf5HP877FaL8n12Muqk+KpBT8S5/VgOv8mQ7H75fZ+1MrX8qP L0RBnmuLg9wWVFP0KtfxyuWumeVN0OycY5klPtP8p+XNPobWwiV16SOPUcf7J+RzHnnnLmXMx6TF DlEJtlTkOxV2KsC/MLyQ14H1fTY63Sit1AvWQAfbUfzDuO/z65+k1NemXJ1HaGh4vXDn1eXZs3QN Yq7FXYq7FXYq7FXYq7FXYqmWg6Df61fpaWiVrQyykfDGvdmP+dcry5RAWW/Bglllwxe3aJotlo2n R2NotETd3P2nc9Wb3OaXJkMzZepwYY448IR+VtzsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV dirsVdirsVYZ5w/Ly31VnvtN42+oGrSIdo5T4n+Vvfv38czNPqzHaXJ1ms7OGT1R2l97yu/0+9sL lra9haCdOqOKfSOxHuM2kZiQsPP5McoGpCih8kwdirsVdirsVdirsVZH5Y8karrjrLxNtYV+K6cd R/xWv7X6sx82pjDzLm6XQzy78o9713RdE07RrJbSxj4IN3c7u7fzMe5zU5MhmbL0eHBHHGoo/K25 2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KoPVNH0zVIPQv7dJ4/wBnkPiU nurD4l+jJwySibBasuGGQVIWwTWPylNWk0i727QXH8JFH61+nM7Hrv5wdTm7I6wPzYnfeSvNFkT6 unyuo/bhHqinj8HL8cy46mB6uvyaLLHnE/DdJ5re4hPGaJ4m8HUqfxy0EHk4xiRzCnhYoy20bV7o 0trKeb/Ujdh+AyByRHMhtjhnLkCfgyHTfyz8zXZUzxpZRHq0rAtT2VOR++mUT1kBy3czF2Zllz9L NdD/AC10HTistyDf3C78pRSMH2jFR/wROYWTWTly2DtMHZuOG59R/HRloAAAAoBsAMxHYuxV2Kux V//Z + + + + proof:pdf + uuid:65E6390686CF11DBA6E2D887CEACB407 + xmp.did:0fd80808-9f7c-4f9d-a485-1f8bf7c87813 + uuid:6bbc8c84-715f-d94e-a3ae-24a7383faa54 + + xmp.iid:4dd58ca3-0986-47ff-8023-cd572bb485c8 + xmp.did:4dd58ca3-0986-47ff-8023-cd572bb485c8 + uuid:65E6390686CF11DBA6E2D887CEACB407 + default + + + + + saved + xmp.iid:4dd58ca3-0986-47ff-8023-cd572bb485c8 + 2020-06-30T20:11:53+02:00 + Adobe Illustrator 24.2 (Macintosh) + / + + + saved + xmp.iid:0fd80808-9f7c-4f9d-a485-1f8bf7c87813 + 2020-06-30T20:13:54+02:00 + Adobe Illustrator 24.2 (Macintosh) + / + + + + Web + Document + Adobe Illustrator + 1 + False + False + + 1024.000000 + 1024.000000 + Pixels + + + + Cyan + Magenta + Yellow + Black + + + + + + Standard-Farbfeldgruppe + 0 + + + + Weiß + RGB + PROCESS + 255 + 255 + 255 + + + Schwarz + RGB + PROCESS + 0 + 0 + 0 + + + RGB Rot + RGB + PROCESS + 255 + 0 + 0 + + + RGB Gelb + RGB + PROCESS + 255 + 255 + 0 + + + RGB Grün + RGB + PROCESS + 0 + 255 + 0 + + + RGB Cyan + RGB + PROCESS + 0 + 255 + 255 + + + RGB Blau + RGB + PROCESS + 0 + 0 + 255 + + + RGB Magenta + RGB + PROCESS + 255 + 0 + 255 + + + R=193 G=39 B=45 + RGB + PROCESS + 193 + 39 + 45 + + + R=237 G=28 B=36 + RGB + PROCESS + 237 + 28 + 36 + + + R=241 G=90 B=36 + RGB + PROCESS + 241 + 90 + 36 + + + R=247 G=147 B=30 + RGB + PROCESS + 247 + 147 + 30 + + + R=251 G=176 B=59 + RGB + PROCESS + 251 + 176 + 59 + + + R=252 G=238 B=33 + RGB + PROCESS + 252 + 238 + 33 + + + R=217 G=224 B=33 + RGB + PROCESS + 217 + 224 + 33 + + + R=140 G=198 B=63 + RGB + PROCESS + 140 + 198 + 63 + + + R=57 G=181 B=74 + RGB + PROCESS + 57 + 181 + 74 + + + R=0 G=146 B=69 + RGB + PROCESS + 0 + 146 + 69 + + + R=0 G=104 B=55 + RGB + PROCESS + 0 + 104 + 55 + + + R=34 G=181 B=115 + RGB + PROCESS + 34 + 181 + 115 + + + R=0 G=169 B=157 + RGB + PROCESS + 0 + 169 + 157 + + + R=41 G=171 B=226 + RGB + PROCESS + 41 + 171 + 226 + + + R=0 G=113 B=188 + RGB + PROCESS + 0 + 113 + 188 + + + R=46 G=49 B=146 + RGB + PROCESS + 46 + 49 + 146 + + + R=27 G=20 B=100 + RGB + PROCESS + 27 + 20 + 100 + + + R=102 G=45 B=145 + RGB + PROCESS + 102 + 45 + 145 + + + R=147 G=39 B=143 + RGB + PROCESS + 147 + 39 + 143 + + + R=158 G=0 B=93 + RGB + PROCESS + 158 + 0 + 93 + + + R=212 G=20 B=90 + RGB + PROCESS + 212 + 20 + 90 + + + R=237 G=30 B=121 + RGB + PROCESS + 237 + 30 + 121 + + + R=199 G=178 B=153 + RGB + PROCESS + 199 + 178 + 153 + + + R=153 G=134 B=117 + RGB + PROCESS + 153 + 134 + 117 + + + R=115 G=99 B=87 + RGB + PROCESS + 115 + 99 + 87 + + + R=83 G=71 B=65 + RGB + PROCESS + 83 + 71 + 65 + + + R=198 G=156 B=109 + RGB + PROCESS + 198 + 156 + 109 + + + R=166 G=124 B=82 + RGB + PROCESS + 166 + 124 + 82 + + + R=140 G=98 B=57 + RGB + PROCESS + 140 + 98 + 57 + + + R=117 G=76 B=36 + RGB + PROCESS + 117 + 76 + 36 + + + R=96 G=56 B=19 + RGB + PROCESS + 96 + 56 + 19 + + + R=66 G=33 B=11 + RGB + PROCESS + 66 + 33 + 11 + + + TEST + PROCESS + 100.000000 + RGB + 77 + 170 + 203 + + + + + + Graustufen + 1 + + + + R=0 G=0 B=0 + RGB + PROCESS + 0 + 0 + 0 + + + R=26 G=26 B=26 + RGB + PROCESS + 26 + 26 + 26 + + + R=51 G=51 B=51 + RGB + PROCESS + 51 + 51 + 51 + + + R=77 G=77 B=77 + RGB + PROCESS + 77 + 77 + 77 + + + R=102 G=102 B=102 + RGB + PROCESS + 102 + 102 + 102 + + + R=128 G=128 B=128 + RGB + PROCESS + 128 + 128 + 128 + + + R=153 G=153 B=153 + RGB + PROCESS + 153 + 153 + 153 + + + R=179 G=179 B=179 + RGB + PROCESS + 179 + 179 + 179 + + + R=204 G=204 B=204 + RGB + PROCESS + 204 + 204 + 204 + + + R=230 G=230 B=230 + RGB + PROCESS + 230 + 230 + 230 + + + R=242 G=242 B=242 + RGB + PROCESS + 242 + 242 + 242 + + + + + + Webfarben + 1 + + + + R=63 G=169 B=245 + RGB + PROCESS + 63 + 169 + 245 + + + R=122 G=201 B=67 + RGB + PROCESS + 122 + 201 + 67 + + + R=255 G=147 B=30 + RGB + PROCESS + 255 + 147 + 30 + + + R=255 G=29 B=37 + RGB + PROCESS + 255 + 29 + 37 + + + R=255 G=123 B=172 + RGB + PROCESS + 255 + 123 + 172 + + + R=189 G=204 B=212 + RGB + PROCESS + 189 + 204 + 212 + + + + + + + Adobe PDF library 15.00 + 21.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/Properties<>>>/Thumb 11 0 R/TrimBox[0.0 0.0 1024.0 1024.0]/Type/Page>> endobj 8 0 obj <>stream +Hln' S`0MFը>@fL_wT"%?}||^˧ג~5]߯T?n??Wɭx*yX~|^~/\ߓ#WaۥoG59SD^ekʥ>"]w7ut32S_koVWͫ߮H}qj}jjdK n#W⣡e wM;/,D9oގN"i^OWBXxcFkkZ-©:Z^UȄ2Vna=VP[<+:Sv3Fe]eI?Y[n6=-ʇ Zp~k+SD AZPT{62-aMlh‡$)ԥF.åf* +*"C6j < +0 cS5C=Tu|M'CSR`ۮ *I(AQ9 VDz^>L:Ld +{H.T# EdP{_]3OyXӅQ$xw, |݆\ +25gF]zg,w +nzU]`D~Lv)p +Qa&K^'VR..648UEAR@981; q HNWuKԐ5 +~[pl|<Ι8waO9Q*Ps0,#5-oe{}]M\qyРBH:;k_qLVZκmpzZjwr_l> endstream endobj 11 0 obj <>stream +8;Xu\=`WUX#Wu19(k:Ym^45j@8 +/Y3$3=iZ(;Z!duM0pNWl;NI_HAl`7WN5`@Hl(5:3(<0h>T;\Bgs6reKH@ViiA3FWH +bXlq?<'QXXn=:7$ak;W$X2MA)'5LR-'m)pV'D/lQC@(QXm<*agiY2a@mS]?Y[K4Y^s;que:8&W!qBC*!8Q=sk`d?X1m8%u@ZRCLl_ +KrLD8%BlsJYh)SA:*TQ:.2cG6W\^-m?GVmp7IM4B#r0+kl@EieB@`o.iZfXcFZf8% +\cP+)g-3[-\9GdMH-CGXEdn*;63 +q1b+O/!#R6V5$C\V@9oo*nuH5>[T_c*nj++>rBI[VRG/NN(X@ZJ%m +4dN[h\,P'daV*_d_5M;9F,b(N@Tal`5t=Zn_EtML`"Mf:%ge@):WX\%qN[BbRcH-k +%)tO@r>P_pmSmW~> endstream endobj 12 0 obj [/Indexed/DeviceRGB 255 13 0 R] endobj 13 0 obj <>stream +8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 +b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` +E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn +6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( +l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 5 0 obj <> endobj 14 0 obj [/View/Design] endobj 15 0 obj <>>> endobj 10 0 obj <> endobj 9 0 obj <> endobj 16 0 obj <> endobj 17 0 obj <>stream +%!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 24.0 %%AI8_CreatorVersion: 24.2.1 %%For: (Martin Schilde) () %%Title: (icon.ai) %%CreationDate: 30.06.20 20:13 %%Canvassize: 16383 %%BoundingBox: 0 -1024 1024 0 %%HiResBoundingBox: 0 -1023.99999999999 1024 0 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 14.0 %AI12_BuildNumber: 496 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%RGBProcessColor: 0.303952783346176 0.669295310974121 0.797335088253021 (TEST) %%+ 0 0 0 ([Passermarken]) %AI3_Cropmarks: 0 -1024 1024 0 %AI3_TemplateBox: 206.5 -273.5 206.5 -273.5 %AI3_TileBox: 232.5 -892 791.5 -109 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 6 %AI24_LargeCanvasScale: 1 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: -382.195936561518 341.839054681202 0.525464021782669 1268 658 18 1 0 96 133 0 0 0 1 1 0 1 1 0 0 %AI5_OpenViewLayers: 7 %%PageOrigin:-194 -573 %AI7_GridSettings: 72 8 72 8 1 0 0.800000011920929 0.800000011920929 0.800000011920929 0.899999976158142 0.899999976158142 0.899999976158142 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 18 0 obj <>stream +%AI24_ZStandard_Data(/XܝƳ 5qBzH^PVբAMxJ)ęV^%l o򫡓QT)gҧUQHRX M&}mFxŸ_‘g:lWD*>.u Tx.j}$p$bve}ld;}|~r;\w*-qH7exTwc,zku2oV͑vzfH:L6*h"ɘ#j,pHa*T̝I+j&Θ9t150 ŢơQff4 OaK@b0 ;4rqc1QvhpaK"cX|`,≆)qðB85r \ hHÈh h:4 y#hAH ~xV Ca {ZbY,@PV 944ܰX긣c8Q1jqK2axs<:;5 G&P l@l:)+-/1'"E6C{^_DF8"q@( DaL '(Q K\xH$E!EV+ EA1-n #_ bX4,7PC5X5`C6: ¡aSS)Paa*;# c|ƍrK$ cj*k00H ; Ţ(2hH4  lkkj7a bX(FbX _E-hY8( BP\ +T(E)HA ++C!H$00q KXDp@, +D@D 17|qGslE*2|fb^ZVR:؀ T AsS\;כs-,ajPRT^/,**Wkʁ""ӐEYӆYL`:áaQD,a^VXbFXLB199sc P0#F':gٰ";ԡ mC""Z5֡A 1y\Q8q!QcFTp! +PqA +2p!ǕȠB{hPH@A + Tx8@a,8NÃ6 4aB20 AP! TpXPB L 2TXq!B$P$s@\&d C + 0@BTpXAC<#L`! Pat`B*LР +040!C $P pA 4@ TCC +l`AA  3P@p+%aᧉ +ɍ2D@ @`p` 6C l9a0h@G=d#£QSw2am!D&p *LȐ "lA*x`ƒ6PAD 6`C"PH$@C*3֟#%Li.ɼV/oi+2ŽMeNweG1]5}e/eKtsznḍxfg$Σu{Rk>dhQw=ӕ3S4wOHBKlF仫˩ǡt7g>ه$)˩gs2tYiI]蘷Hv4vGʘ×>kNҋb `?,IҭףnI=5g5bj˺]~r{*^ikp|lelFt+;Qz e!X1ʒtNUsO}Yij]kͩ#KTmFxg}lj4M迡:m}cC,ik*.Cr UwOjfʫKWkȭ&NxwWf9ƈn26yϷ7RDf֩d'6-+8)!Ppz=Y^O:{>+w<ݽQ|C*WEמIHlߨ-'6Rv'g +NdY=BTI6k'j3ߩ.:j5^;)^ Z; ^S)|"wWvw̒ +ၱ8Nh7yFv;V{N]^9=qo>$Q!|toʲ{P+2}Xy61N{Tc)<9ȳRM:y&#h2KW2i]{͌EO$ϸ*f$:NDjub6L<)iTdNu3ܸb&V4%b`oHm}d5RY rXbnV0n7\w7gV"ѭzӽ)"M-H^]]]Nղg YY9snW +ai7rl&~Ke%tN,KIBY`k~J{O:HM;UQZ)w5!eI9=*\aѴ{#kK6/KcɽI>sT>t'u[':^+ ړYLIL< »q)O )ut[Rc%Ϫ?ɤ~}Ho +2qJ'JJ܇Wg8Iu<k'!̜koӒkv?ylǵ=cKkWީ%}L֑9&M9KHHþ LNU1{kKQsUV=ycӯ#(,ܫ' pO']naӿOޤ;DGlj-w:5ץb\+ fF'$DwTq6%崗Į_؜vt&G6,j iQ{V^({`?DݧNX?(B +NZ/{2wzqNr3dhp$LJ3q)3[`{6L{vO<{/ٲ[Yx=6fv+=Iǝk$dޏ,[lI&"KSJakg twN:v?D.DDhc3-{x.EBWԏ;)uG.z$lDTgZիa3&fǝ߫^~G;Y2V{c* GeZ6#&u8>%tśR&}ͬd{i^ ^xH֤l5Z냦eo]L9&É {rvw)nlЮr.o ++Ǭ2i,J%P>WQIZݩ{zt.y!"݋IB39~RwTv:-Ks $0y$q8#뮩'KSJ)QX-#*3I:tv%G>bt$I1Ē)V'y{/S]{Htrdc(|4S :٤[/O7$J PqBҤG֩^z9b=3=%w>Mc,C2reݔu,Ur6z7:oKY3ϒIJiN5 K3c'i/ݩe“ԓ\(隩 kx~ʆ +=SҧdV}j&&V5û}6I貰h&K%C&F/IRTҝIG9,C'I}BmH'+UMVQDBsSbnO0fդrzq3g4HJUM']I {2-V `')hl*;n})p)ee %Dlսl)S-R}؍fw-G=dEht%2֩U6E2'ȈEd|=2tT:Lw7wYlJGW4 $VYj2Wřt/#<|2it޽H|TeFWѐٱKqU˓rџsڒJNq1+IcK҉ylkaw"D,*|ќ\eRwgFK*JՁXXQy%k4ug-Iqh4VG:91CMIN3>(4I)yK!jzj]먐oty9F]=?voZ%XGԺј8:˗Gs2ցU4{J[WHSF&iyc%Ji<;]cdGoǹ伥ٔMVNǗv&/M0U&i&Ҳܔ4Մh%u5MU|{Mn #:mJ{IQ 4a/{? {zNJI^i"4n=bGs%^6KniJ{%uXRHIVGn0>#v'9+^ϊQVtR,ݕQ0Q%e>4 %Ju{M^ZG >F$O9n=dR<>f%e397;^fʡAj]j߁|h':/h!%MWxB4A<1;*i#oΌ7bfě1f~\+zH>#9X)Uj?X9I:Ow>1QX5i} %M5CqjK}J(7s'>Phoe2Si?פL#_hO1Ŗo Oă+R(8w5Su&ÞԚjxw3I4FsY?:VyCsoY(j/>H<^QL$Gef1᧬$ 8E  T ƒ Ř Ag\Dbt9pWɺY+FK_7{,u'Q$\#º3n,9%<~}H'S[I\Q ;r Ze(is9Pfq2wLe,/KX#ձvy3x×drYL&F˲3KTA^c?{LJFcvSiO7g 1g=!&M2Z H"KjJmitTv![~˛ӐٿOXi{J ZGu&qJ +eU%MyXYACě#ܳ|I6shXvx>?˓;׳D詟ZCJU]/Z2ӳlIU6u55ͫc:Wf8IesWO?E'iV2^_xkvHEwP팮J"K;jW>{;6gӹq2jHy՜{5giȏs>tT6{gD)Y껬i<Oۈi/ ?fԝJ#+(#Oeg6\n,3˰4Ycռ~|9:~!GK*5ͦ&ޥ\|ۤj4E:ܝ=mL߮N-U|J)D{j +qyixΎ}WvԦft^w{XY%Hz49 [?<<iޮ2kIG{"t!4h:tri/E(Lɤe3T$Ice&Ό#vMNhÎNM +x=8sv%YX!'UnI]/Br]Ulr/h|!؁GsG+*:TH(Ĩ>J;2kF@ޮBvGJ&D +UV=,sH|t&F7Xw92w$Z'厶)vkE"UZS6xc +dW۫ʾ+thZO|&itSZZ/J*Uu:Rh4eTBֵIIeS|R YDɩcLnW&&2JV+zVLjSIlOI -+*UʦF4]|'\wiƐn%u* k^76Ye{'t"<2-yDRcrBW(:7h>eIK3ȹ#A5)c;u>Ju5IG}ԋdaHE9:"bqiSU:INN5$"n0Z[VD#9;ScAšd\HDad+CGT̐MaY7UR%sLRFjqBת[vYсfS)ykoBi* ER5&T}qMf%tV+"$˴;zIF5QDB?<݄:x%D.sO892aEy2He29sTL:giY݈.?A/582;.u/\<2;fGtT>Z]e?{ eV?czaYVC[򎣔6kr: +dvJ8ūnw>o4;Z8sV:YliCDUt]|PhD)+-фxO2ʼ݊N{FR\j8XGWšO8t+#T*}#Ω(-<|ChXZY6gf<挌DH|.Z˗KfNF7« iFO1zq.f>RLF"eS+W.K+-eYɮGf3lҴz2jwE7IEL?^Gv`x`O+yTGzݤG0Ų^5_=)9߂&S4}JPS]竘OyuU/Mx 힘:n񝼦HiJUC<bs0P›$za`{wf!m6t⡛{J(;}`՜Ўsh.1]]rWsJҺ:ɧThl;LKKXSU[3.R Ō7~~),wbdY4_e:DC8IUvw,0Ɛ ?o$woI"͆6 ĕiC Iʸ vD]7a.Me9,հhB}X^//b7 ,7'ۍ`pv!VF<2NHv&vB0*S)ڦ2()kq52uKIk9u:ՔlM+ ,..:D"(*l?6$m'dPk EHVFqPD/%܊[]@jN*fT@] Ӻ72b`?Nq$ŲMM1L.|8EXֿG ́D l506~Y(S +B>GD6Wd;,! dY6}1'WKBր ?#[W ^**.(P N^AbEm +)e5R[|t4xX6P-]e!a%ZT_`4Tt%hӜGM >Ca9 %GœObPz5Vvo]1;^6x +?H&?$Qg$D%^O#G!B7T TpC24Ⱥ wVY19V7POB!Cß9Wcc(bo5gI|GƏ69ܲ ?L.PAV[] +3 Hɍ&NtK~KRTwQG屛kοrI+u 3Κ)bޯfbobCdc b&T֋?/NJJ= xsف]0CGŠHozZI)fIC3AIAb 2zױ4 BӒmtNH',IʉzB!D?fJH *a-&|Vw/Y q?v V SzLg#y["ppS8>}ΆO. @GkB?8p +n,{> jGF~Z(qD'H$-T2^'; ~6#1LjP{Ge+M`,? 1Mh~S~G +T!:3%%LBi:|.γEp'3 L~/ \n/$< +2{򐩽14QL{^͉1G6QB0smȣetB)*S&q"pn,Q$ +<?p6-% W'n"}InK6E0uk &]]RAa!WӬ!X{R+*~B8#D4kқha"{Q0 G_54+2H*Шy}c};qȤh\cc%BBB+$wѯ/J xo񐬍Oj׀W%RfiFMӸh! XjJC:o|C"䏌ٯa`,cwS K$ * 2)Ee0(L6h)ekDd6|K8='ܲ@i跠487[tuJ!~O^&z?' KRJѼ9~oӭ~om†``HMZ*p&B:f'[2.X+cf4/abzB*dSmR@c5+D9 iAJA}t<>idi;9/Ψ-7tT}Jˆ>[6Tr`1oA\K/h9@'&d|2Š̑GOXCsnU12ر,(A@=j [3$]O#ͅЈfZ5W}WNz>MbWuWug6~l^qٙkT(8e8E\DL-j[Q߇hGIBY>%kxTADA rK{]0%܆ϨXkr3YgmlB9PE3䕇D"1bgO]L/3ҤM3O@s҃)"JI]̞>_VzDbl0\zrqGYIUl"`)))0 It0sZ| ȭ_.Z x*^yx;b2a=WӆdX$9y.O.OUp|$ː. +?(&bQR{+C MIJas!nq]Kr*P +/lA},[6 :dFzS*\r tY+W E[Da$-`$UHjᷭ( +dR-3K7m9\i7M68N}-y6 +Ӳdd#GRXC k8=<9 +}!pQ4ɏa¼^p&`vLڋt/"Dbfȴv Ev*@`Uax!r>\5xpz/Ne `gdA~=PEK]qZ^&/,e2LC2?cgC+yK*1 ~G|0"$tnvw{$W〄cޯݢ,VUrA4$RZ@U!U}ڽ5VIHd%xTׅ|Ď팡yÒxw\6ھkz@`{ԝH+GeQ>PO@x_vJhTcϕqj +hHDŎ3--C\4hp(@a# R}eô#!&ZXoNVMw||3j oP6kbNz="{s?~eݘ(#wH6YQC)77>ɹǷiȷ sLTR.^ isCliK߰.aࡗ\ eUUpl6&>v ;~ q[s` E%d^MBZ>y. l +;M8sSJ[2aSs0oB=-9+NbVgD! + iqjb-ﮦ2NYA,@ A2)C sGZ#Pp;|uox2Sb:DZ6UgUklT-޽k $ڣw&!PNpkT/59fP|MWf:zdvD!F1Șx2`w3h\Q:0=܊2Zx=;@׻ͪ^[bӑ} +Dy(B|]Qz M,٩Wj^QtնNxM'GP;J '1Wќ +h`(Cfo;5tW:)7mͭ%$ +,{w/A*q {EmA5BdN2i1[}΀dc v'C}h |7.BU8oWQSPJ@ZiGupkr®u7WMlv0vDP\A*_0@yV.>ǮHI57)'k)n=>{}9@>#J]@F=%cB6AAx*swjxy>8^Y_0@o֠bJ ?i}`Zp 1P0Ә-@KOU)_F5ɠV%GvM6撨m'A?m ɱc}&}b*ejFllܺ2l|ԲfY{>T'4rN%upd@d~:v;*, =SӣvSJ>Բ@he grLi⭟HeVASc#hMhG!!oQv۱z,{&T4p#X2sxl"dG@aל +^\r=>[`[9ӟD"W2rhAvоDSMnշO6\%ULUSVuo ·4"L戈 + ,ZF1m jT|#ΒV/ցsj@t#(Ⱥ =zdd5>(-=lybHbcN$E neΛ>8ⶇX7W ܎A!"}`V2y;'B׋UXq[Rןw~>O|3st 5hO+"փWgKThMR݂ )gLok AMuZ+6EY쬃q|׻lp,oLzx0~'N+&ˠ[܌ ǁ +&і8, +M0Ɋ:~莅^%, cpu$^L&:@K˸1n9G{喩 Clh$fPNBU<(WS𧌩OhR8M$)``w}3+ sX\pl=kOŐټ5›Pxli?*-;DeH@ ˆ5LscfQَmKj6"qѮjh7tΏDpt!}H#rc3 $Ǹb( +1ߛ]7_k }1GQUB"?Hg"C|&IUaGp bsHUNJ z$r2?eaN74U zuHx1'P[Q"sc}cC>ⵗ<9L49!WHcʾSi %*G%ofy 6*H.g>W'mR5r̆r Ǖ4[tgUG_y`Zov9ERalbz{EŠrx⬳f-8ZZ[TK] l ݒF[҃;`BoK8EK_, GEy$UO5C XHz\ V1̢1ZV0x"ƕcq:dK =C0r0b#R+s~KPW2,+0$ez9f`ҍ'|f EÙ:)gpIj 3E1fhj"w( + -XL .N3 #LTjrjb@f[3i ?PrCVfVȍ2ëfz|Z}~8=YN@.x5GL-I*ᔠeuSp[z w+ gMjT1Q[Gܮ.j*X +3z7"GSHKD3 ),P6㵓i Ƙ +Ґd?t][DG`\zE1Zak^ТڐnE^#ґ4;z]^X=MhYQDLłwWk?gO1x#{FFb9<0-tQY8'uKh5ep<"yb:@QBg:mMjn +D fiW:\ p3q;do cA%C-#d#?a.%f _V:DC K1AwtD +yE'@AGjNƨʥ0 +JpmƁAi}Ĩ`15T]Y԰ZQ-`+J^ k> 8G+viQG`fY㶒ϓx?Q"c@m )F{.Bls2,e/vH@1#}!zEB8#} ́@]`p ;L$,uњX Itf蠉S|C`-u@K TxdȮP YTnyw?B_:'4qUr  7NAStMfp);Tpy *6HJBpj%̖pLؘ.nv@>PGH'ßx\x?LKfڄyPg;y|̟e?}|:((cGp6 +Y>Og~U~=n +ǜ yhE+!㣔7=Te>%74 +TA\PvSaz0Q702Ps Yaв/ ; [f. `kuJ91CBhj@Wa8% N*zӑj]EOHhjԄ]|6wlA' +v\s PrzNK$xa /?|um4Ε`w:7L*lNx$8Nw4%N5%@NvKv:$uQtŨ6V`5i7܍ZM +nP: *`e?."1U|QZdʤPH(4KcDJŜш$w*r 1Ilxݸզ1\H壊AV}&8]l}]|Z>jZuiW{2@x[W?MC,J7: ]Ӗ6(R q? \G|MYR`3^iO\[uְJQЂR&v4 cOFO29C`MesԔPg `*s)1[H-0B`@' ^ bT-Yk"sMȲ&v}X.*W@k nLg`k5Klm +793ɧPD Rg?HHMv> ыYvdJ,ɛWПeqꯁ"&^ {BR; wԗu!{a4.VզU\Ю=8 +8|)=~Ba`.dSrrX=QG:2 ӡ.&2FjtJJ:QMiS`~Vp;H]B= &k2 +d+`"3;ʏtj + ײ+9 ;ZnzcAꮇ6P܏ʽi##jl 'ܟf[f()X&4pzcYoUӋKf+j?L4xzoP1 lzikm1(1{4bfKLE *q?:@w'?k@p5]6"KCV:P΃{r@kl:ٝ$x<[ +UdVpst΁4O.G_E!$bq*zOi>HBWIM=grY `Sd  +8Y׭sɴy1]|*j&vOaڅeio!y2.7 +U)\_KUŬ Qx>iw$faBUk^Ѕ +#O;l'T4JK 93un-$f\Q(XqSR.єU1J2HT'/"i=mo֢I +g^r&zV 49&q谚71 c8 TSB Y2r +KX/*)FXx@ո[cH9v'MBo&@:AЌ h)EKǀRn^ٞ^') ^6ta1ii'weDJ8jI]X][ەܭ:YDG> +~H`鶤>k,": 3 Ej&plZH_85vkˈvuh Zñz@f`*z*p\yG-p?(gFpAkźZ )lB +RNtZ/FZ^ض>[uַpx~ܕ17ЕS,K.nBٿC tlUGAT +\TvSڤoѮNn`z'lu.cvMhǞ."W90ׁ׾iua_5 t7W a,oF!]:I͵zc-^ϤW$u<ϽW,dgl37#F}C94̕vlw]/QygN"VH53n2@'H[38,\JPHI'R&+NlD(fA^ni#jiu!XP| [15eG .jՐ7q3]9iq{=q#Wj_j魆o*0?jOY /_R ^F'(Sy7ז=M_41- S`@8YJ [Q>K)KJ-ql$x^ +ғñCP#G?Om> Pn֛ݍu2G'6,#FW}Y %NQ@3a[0NҫL paYE/>ȗ,uptZ%L ɭ7ͣ❤fӎ"?ˎv\ub|C{Bdj[ +y5϶1dbt#iS#|'ݗ L\[պkq]+@+:p!xtcM/⅑6U?}0hs5EU}_Q`U^%8{|}+JRRfֳnn1PX]Ze,SGpYsGѸ^ +vIW`|$ KДI ":n& TNVUCT7%3c;)h/6 &4lPtrP9u)|п2 (ܕ**4l^L C.lՕ xP5$ o146ϥKm;3 f/DQa[(^g0:Ի<ӫeZ2fc9-`.}Td%צy +gxkq4C_ёpF@eEU,үC~| RH$L$SlDp!g<a-;Zْ1bE_On2XFL$b%΢p 6D+)0fZ)8ڲÎ]F,)OJhl=srIA^c<1lm~4u_.O.Pq⿴m۽/=t؋s{͍_p +A}W8 54H㲔D#} +GcGpTI9e#xہ7*'S,U~Z6: Cac0jCMH~D]f +_aZ cEs]Ajd۾n[1(y֥HlخIK]Hi] |md(T ^$L{_ JXpܛԞ$H9\PI@GWݢzÄg0ܮ:U?Qr'PXN&ZmŸ婏 }ßeioj$\K(3"J 4`Wnu 6!yUϚ?^) +cY>GM_)h?HyJaQBevA.m.+O_I ԌreFWe"Va" +T+DZ| u`D&S:>47>1<8DZ *CIh1g +%QƃUYb$="Y.yܚWmUPly<~4nv d`tl;,y~'6zmcCڰ۸DV֮HG$q \Zho{f6ݫʯ"!(=?Ջꕵx +RfTT^Np؎OᎧtǶVt\%/91@k|kt|U@S?ErPX('P шeB8:1ɒ|7RO k*7R=F |FX8jr.*H[ȎDTn]A}Lϫ8~m|P P|Hq]HH+pP=Kģ|X>NhJ(^5 up&ʹҦQ%6\0 P %i.^$={e]K 7t1O6Ly;*aP,z| `>91ɧȪ-V20攠͉x^@xcԅ^)W +ȾoUWCӿe,9x4rX3zK){h@w*L?D`(\2: \N(mY3 "̠3fq?bh"Nx~1f:Vc9~F? ТO V' +D?֧ D˻gǤ쟪=JD&D, 94@agQHx6T9srg ؋L)ykYV2@^y2+eX7 M5@)MK-/XAnP9QDu*vؖgǣ_ Q/ks7a#(9bj#WγAHDlc/ޅ+B@,)l^hns[h\q]'w#;.7W= Z>qP}`!5я|'i軾Ք4kZ*{PLO3cnW.Ď=RvI:yX3GYm6MEqXAE;rG*&3zI/2o8vOeG>>`gp m=UA䂓M.]aSҖKٝE ث͠>TWHK.*'z@3pEV +4q3‚zlmG1ZBP)dDB܎y.t9kYWSPZLިl!tZЂp\]tDl@;CE[mGeBkvԜjXQ' cڥ́=t*Vo"tq3A򄖥0QtHqf+y.G6Vr}-N:Xn9e3`pnE#dI7x^=.$'²0,1gILFYnx Upwx/-6L06iijuNk~A>/.r_\u)aT?S,{W-Jjd5b{`21ɀM, +̎VQQ4l%J p,%RnnGB1qg%NrP)Ո## `ށڢuK7B*VY7/"H jG"Fu# lon-8</2 +@%4MW\ + /!&~/`}!Q$1 CLZKmU 7~#M/ +Q78GǶt<`7|!S^C xqRFȪbuwOUN=՚uR,-z 6H)`-* +jYj\(frb9ؙ!@<7@Js@RY(yqB U7x +4rS\ hB]'z$0,j݁VhzXQ^JA5M&Z(GR4:qw!A!>&X ;>B$Ύӌwe*]}(HzvN@JLa^Sj)rY9 igG$}J!NZE?3Nz& 3艔EUeoNǧ9{MdoI(4 <\5.SW+c)L$\J;v׉)HJp@RRXa >Ɠ6Hヂeb"}<_@LD!k>@Go/h…gm2, Yqӽ)~԰JّLcE-K >Zv#N9|q= O-MK(xតA so(aDJWөsD3DqpgF_G-揣rPGUZ ]Ia=x8ֺ-nTMFʢMg>nu\=-#J†X, LZ3EL>`혆~ËN8P3Z@RIC_ qPL3G%[J9Lkew\B86ܨZv5J[O0Xq|T0~bBJt%؇EV6-Z,KkX⾾߫Z^Dq 5\(\\/pm [O깬G6L%bM"M1@zI?9O(j[ `'y1!GbouǵR ~ gat317'ܐڈ_-7/&_VMrH^ͮAdںgS~󬆛:N$ҌGMM͌e!É8#5GH6hJ1a7fLˠ]2,3*MH ,R9 zrBqms"5Vo/ˤz\ lQ#>ñQO>Kڣ { qu89!..;&ansv͎QDXƯwO/2-.=IAW;ZAr-:a{?ZD%CWV1:V!jyn u!@-6dd磢lg`KqғK6iz=ݢo;a|FPL|Z,3Quk;}_D˛YŌ:Ur^ـ&djC}*0_FUPsZRc ?'Ѝ߁FmYUG(Z4Ym@ 4j)^Z hydM댈{z]L1J`_~==3 ~z4^:DA /(5iዲ;O j(v*Tk0@26H҄>ȃ[stIWո#eJaԃ0蹼CEM;Hy!IXi]Zcط@&j5!-hwmYȝo!wA) JVXb'M{47~z2k0#lG +َ/83g&KMoƁ]wIxf>P8֥p;+CC6NFCFz`XN`'$vq Luq2N.VnIU+# 4 +[Jig 㤅mVQbrjk= 22Kz .˸40%m55h&L^F4yWuo}o5#2h8>25H.fwD PvrZHI+^(TsQ'~> ^`ʡTuA9Hm舫f}߭ -gOE3j|m;fEyS>rS6V֭*A9 + V'Sh=(4| +n Yd{ `9L*RtRF QKBS$Dk0JPz'%Le%i(vl8d'ʱDJ`UV Ẉ*/lŋ) `@Hc +hƉ'ޠ qB\;A˟ZVhe`juw^Cxd=FCV;"Hɝ;OH3#ƒCts@uN@]M)аʴ4-}IUX;(#qZ˚B :[g$K@Yݻ0r^ 'x(2~K;~in-!*he@pCI&${A@/ϪYiZ ^N ϹF&׸ug+Qu,JXx;۞e?|Sd8AM{RD D(8CbiOopu>N v&r]8A\ AO_-a>)hwAĹbI6a `Dk)=F'%=X"'c8}pust(=)wwcP9Vl}*yb| +EZd7]qjESͷbP$;! +6wXKcOÀ#FRJRJRK^b.  ?wHo"roclcևfe1 Cc AfJJjCPLLa.4?D,C2a>R|^BNE5RapjB%*LȂ*$|$2Z)l& 60Z̻⩚ UE0MeS}~bn&H)N$3F"d~a_gO {$n>oLR}Uzq>И)SH12/1ZֳXPrg_N{U0D.R03p&]%LNд~΄$L($@0PĴ@iI +cbb6Ra6cD:G"RԠ ·PE8l6hb42%.U:4d2jdu3 + +XƘcZ[D$1'' +~bs'5)Ws3E݌F$ɹ\T_c1) 괇{Ej9KV:!-Hz"Ijߌ bl-GO6 %QB1q[a(.&Bd +C_dƉØ:6nN +I2nS+:%1bF=K +!SN6*hS3FNpRE-%UPhEX=i""F(Q(5VʝbS@,+^/Ͷ ߺr ΢dSP::HJj{\E*PU?1Hv}:TJ>ed,u1CMM tk,5U'󁭊ˉ +mXWRb A!MrX"B4(H!C.cՋAf +EB0P:D <%L%D! !!b ‡EBEH7B4J!a6;avpg9!;ᗒ'<]*)O>g¿~l);E0p8³kJO6ЃObNCB5%!0y +R5uXajhHU3b%S=VpUPĘPUQo1 K;-,=2jB)U&Ea:jeτUU*B+P)rq5Щ<2%=4;jp^%4CM4 P40>d2 +’f g$d{Vxx bh%1njob3|Rr)<:!Ib2_lJ܌ M$ϡ0D^e\H_)ט̤j`-FjTjM{|/Wm,"i M$Ar=yGzK=b);"c 46v Ic]Q[F#uZuɐ&*A 'רIָ4cPYD9EnTg=E:3L|0\y#pkGjeZcVS$=V|pE%+5dED#@%xe&-&rh$]&19ӘOW:3U55R2rhB4!##R)ҕ"(cAqhXc Rƛ TC"oGPCjCz Aۅa/Fclx}52GBgAW)MĐ-٬Ih>*!rQYT[\i=RטJJ*ŻC)`7򉴣 D!u2Jr4BQ"HQDH&[X7B) +z_T+#D'Z#VԂ|Ѹ;D3>HpДtU*e/k/~l5XVf5bDSj̸hibbSUH(|O&D+%8s{,SlKH;Gn2ѴvcƒhB\0':-Xr5$f1Ǎc"cUc4;kdjQhy#POhiV6dF(1pD$&CLS"0P@ZGQ58jj3EdzNO!?OQI)fHbjʵjVSd|}s},D0D'hNG:Dm4FZ%"P:6xy8 yA(DaE*(UU*%>uQN_\f4 E/p8$up,^!v r*j8l۶xuIWa(NpBP8 DCD"jRTTm#%F6iU$&."$/ZقDn&fHt]QJBD+ur tZ71TKލdycµ\#U+N]%):l.TgZ: EQbiF+=6I#I W\p/l*7en%+9blՖMUZ8ZTp$fjX pb/QF&&aKLi&:UUUUPb\UwB4 4%Y +dIF(=B’ lˁ t =~+BhԪ(^VI'aX!+.2Uap Xb~#"DP(Z!0ŨőB(@#0"Ã0]0*A `" L` 0yJ"Є"$3|IUB%\214E^ٗ#BU,z"gU50hEǔ#Ѩb|F&ԷnYoo'%d{ 3O]ȸk.-s eU)9-,% +`X +)#M$ wQa/G +7%i*QQ^;kK/rǚ +ye5>#2ZJ6Bf~5 k3S""!aTaJUg/9t2ϗs&=Cc$iȌȼ.## xuLȼU8adQG0װCl*t\gRIeް[ vc%D6`HG$dj!Bg͜$$TtFrVHŘm'R.2C[X(U%{31*Eij&ތʛ$1It- h9R~Ҷ57I[$.UB\C,:m.jkݱKUm~0 qbjrD#QB:ͥjbP*++9zCv +OQ#.kEޚ8ˬh_)حaqxWfoJfEp*\dh"=jTC地ܞiTK rXXL k'˦B=Š{m7 + 2aO9-b55v$+mDQKUiZuuQ*kqעM3I\TS4MX&! +ALJX {'ݣoK+ya|kuw ѹHLp&rGWRBl)< b"Xf,Ցn;tE/x"E*GQLj<ݓ[JŨy kx n9m-;^sx Ri\jxP"pnXsjD +t'Bq톺BɈAP`n). \&A܏xqThܞqIh-&q({{.w7~?hUDLW<q6R|!;iT!8X^*D͊B, [HXR;L +W"*H PUE끪@*j#PpĶP𠗇(,lٸQ"ybp8`,82qmih'̉QX31 Lj0O>f"eE8B3"91EKJ%صDa+ȔyIޗWɜ?vSO(7xxs[%~}(jY|PB2 %P bYAJA6~ᦨg\n|e +\'S2oh5,E,qbD>$H Q7"{PBJ( ]T +ݺ`@ Cvx8DCBA(*2@/sڲƘ5NS8MBلg:EV̰,OԈk[dGIWR:'DGZdJk 3'liTA.ktWʤsL%썑\3S;{ +N ד՜ME=ǜe]RB'Ux؀v8=#@N #dϲ[$_lu13`P1 +FkZ'&%e 7^b@N$|Qm!1j-4ef#,=h&ekI0^#_^^ P].4Ag1 +< +Ԏɻnf` +a + iGk_ᔜ;f"7 +k.\BX9'`MY:;6[F3gW}1 yO"Y?D\噂+BȂ҅K<\ǧFg&pJɰ*X!EM|SаS{!F;VFUq`즽 ST}j&~1?UPWk&kVPt~iˆ]E7fg(0ctE*LzG(DDc/98:faJyEoZ̅-t W>CdK@JD?!n,}r{pe29jPE oj0a݉ ?|aM8YH3hc\xQ0id98!?$TܿC#>^N\/m9Hs&=.ݻ#C})eІyPv SWrJ׶ê$a1wG+d:nWO/M`;Ee C#lx< N]NdfeL$JnB,$KzFGۇ>[-IoS!ܬQa@HRXz4ڗOfe+8kC YWOav3ڥc(`..xܧ ̺V<h2^,_`yC "hHr)Kg*cQ0ylow TyZ_wiٞnܭ}'2ΕF~ٲnJԕv9'b N`S2svʹdQm5[։bsTH3ޠXIy~Y@>|?L~j ˨0adk`hUۂ'XQ +R8˱h ?~WGޣMg0y.md 8%7JAH #mIǀY&9 n`6O:UhƣSBjCxU:ݤ=1z֥'E"T6Qm#3.d^+"GXryuUs)XR \|&ΗJQ 0'FayZ#9D`޹ R!x=@!S ܂quŎJ:zȨ/l\  +]- I1kZҐFr7ح#kv1s1ƀ݂]XBRҀ+K_#m&>[/fI]K 3o0dLRuT"*# f'0&V(֏;2SK-ћ?Rkv8^FSLq()l*s.~jk/3>L[tu{`BP@ Uj|Q %2*uOAXx (A BL>/$au$p4?}WJoKѵLpuA)>#Pz>WP&iTA/ͱDAVBe詢FOFet;GXȝ5rMkpH7DS %b%Rez͗Dx`HS%bsxx_[]JzuX :%w)^0/X7=퐅#G\rd{@1åIBX&~7b9"x.^E)w~~f#UZk +(Tү* +0-{De^d= +Lx.`|.('M`M{1s˗U_w +"4.m`[nޫ@æ9lP֕&ن|!c!9e* Zan\GiW& TY>tƑI=LR>tnepiP!k)85:? sA?vbw,%@ +rɫ±erJC/1 Ŗ-_SBŠ80+A;xH~nCzW;Y=j!72I7x xgKd<ʔ `;?%\g'o~p7жr'Djl_o-ٷ7d ַi 9ѱed <\E*]`'S1J˻tٴGf,'#ӟ`A`KsO ʣVOxe@XmЇ=6,v)f-V1+R1?:&pq%*Hyij{"[Q}~X9l~Q%j$Wq)^Vojѥȿ$]nm7PAn;̆&5QV_(pVV\i>ƷDǏ xZ!0DE9$A,if.e|]cTav8C44<1_`5V62* QYİ) )%N B឵n՝ԐTy =w55,ը$qرavb(i!J8/![pEe·RH ҍHRaU]8ǠϓӧHB oX+QNlczӺUʉfܔeш1@w@Pe (bڸ7Azp F4sX4Dnv@μ/4]Ϳj]S5|m ƀnóIL9nF|424eύ1)fgrv=Z9QpI9~;5K[k˫zKIK^3]۴ +^1}\Rݫe'`S0+.Bi@a}Y&&YLz#z&YLZz58t)KmْF 1@J4A0ݣ9zI҉ hߦYxA3KkiⅰBă)o% 9$(<֥I"hKVSe\GKͬ3|.]B@c/qԻPnws +8H^0!xo-LdD0_  -*'~!K}'ҽ0b9J pVCgT?($u@u,8 e{{ad-b 6w2fUTeEkO$`m2fgó#n zW8R.b=w،!%C'ga8aXYڮ%"<p$U#@2 V`xK/:DS rtbHNJ+F>R ((N<$mΩ3.5ńg/:d_'I/[ N4p`C*b1^|־[,Xk OM>d."X'ru}[“b7} PUʦL.Oʞ-DPF3+t~V^P`kڼhC醞R wQcIcrfp̠((QDb V@< +}WAu!Rݱ%q( 7d@| bX6aiv|LsTe.;ccZ#b+tWŮ_MP΄Y!Nmv5wUu4FrHڠe+v* ?xd,6Z} a&ױjoRPjVAC&%ag9mrU`ya +1座) rnYrS-A<9B)~-gI_'XUɊu}ר7f^t„j??^kE72O ,H +j&\g,<^oND2}M=WWTh! @9NDa{EZ)z;xE Y!̋;8 UjRxL1]\0eS -9)E2UOKHðԿD(,KwZ߷%c4ldz!(_ d4kp2<דF[〬~7ahk!k|i쭧[ h6DxfconK?>lgHoH'`˃,o rVbj9u {^݄~|P+&jI)j]Ox0Y'H0X|@ d8Sw`Y/zξ+k/ϒ`MSp膌 s @M7#d*cEk>B6 /*شd!~ss'} +yĐ) +{ηW(-D]~?趁^xޜw_~I V^x `s?{o@d1b6V^W8xnkL= ݎaԾ"BG|~@ +},PaibNա-@3Wɘ\p՛Z,PNv)f$,\|PP[ove,!>UqžfZf;0:)КՇI0R,es%vM5:CoaX4!dB*2խ6qDw^.`!8H~QMTP{PӤӁ%!QӘnEw05mO+Dc%*X[oc-P&^!H|Lbd\GF2&JC28?UqRK;/^ ~5FPjV)ID6xXK(c@2>DyxSTm-#X cN ϓH$-m '4%m6q1XH@M%XTNJCEF^܁i3m[3aU1zQ6,61٤Ebp һ+uڞ +<Gt?kҬkN˜{sn:dc깤f.:ȉenBCy ,ȿ +Pl;t%go^eM粪K3]# +ק6cwr=K1h5ꊰ_37An!m+BL'4:t)DAb0iᘒV/_ۓJ$η^ĀM6ѹPZÈrk eڠ_W׳b_)5sBF;h$ז }jOIy I <.UvVPKÏNFh>Lc_ +X~ s&Q +:Wjyt6a9G +vF.΍ mk=.< xSL1X91%]ᘒ+̈́~ +5ghc'*N~6 Ge{嚆 Q&H%6"sS-{pF_8VsAaVs}/A܏ ++C\ױY: o+OviZ}>=~n!M! 6u,u}dTDQ@G1 kkoXrp. 󏻻c8\ +?с{[-RFJE!OYO@?gRHo)%.~P%Gsu^FPҫVj40~^+^:$XvOi=b@ +0k=ll!M5fr\pWˍkN[:Spra9Qy鿨EVLM0?))BK %'k$D C\-&A'ms kE@ Њk6\15_gE 1wA8gΨ'pAdqkleQ%|PRzt+rcpf (uH#2H+i0HՒ'Zȗ&ȥmApՅfLLhektWχ|"ހ\Jx}K8ٕDؠ+&-X2btTn2CUΤBg8# +_!%|܇.=) Uu@2s鼲^q5 vs7)DۭptU CKh)/q3aVCmqB69RGARe83PRyWu~@?0ՃЗH_PvlŬ!6|f"C9Itztå$:A"h +Gdw_DFC3xH{zhy"O`]H ¬|އ{`;"?4!LCѬ!e9}C$D(Ć5Dh>CdUUu4xAsExg,х8/Q r=" $!10 + M=G%ZBHBhFY%BE#J]=W!!Uթ#qv<3JT#|C/o0mhDq@A H8 :/n(ڂ/ 'QЂ&(DO%zDW쮣DcWҁgsDd :~[ &:&%pChvBəh&RABwM8 +m+8Z*'t`m'-<?\ʴoQ +EW痄B m(B9G$ +eN"K6[Ǐ.RT|41L>)6bW!5C(P5~([QQIT?~6(N!gdNG*z̯IEVT>Ҡ]Zj W=tuÆp{ +O{~Q#mIzԹzBգʢegN΢Rz$%ZEtEiQ&!"<ڪEZ]\-(~ψigd9j0G'W4SФ!&9EVN +jx9,ROQj881QF0Pok8,Fq lT8بp~̐ 7F jp poXݍ%?Hx + 4jnGAAıqQB۰9h%r؄m\2S)F) +P?PV97CJ`56젍o>ޒtFz$vT#ka./XzW6$yZmm'!H+U:c;l\iE:5$?ېq>t8GXDt%<:[C.Dլ1k<LWCС s|%cTQXT'ӤW\Q#GrP9i s*56QgL@Ԣ4k*/Ǯ5GiX:ؾ䨤Θ(9/g it%klud#il̸пA算C^Ɗ +\,cZP$|926U2.s2DW&#$)+ +uQEƦ͐>Alrt\[Mm p5!3rcxb&c1fT OQCZ܈Qy!Ƈq;Q1a4t% 3ba$~4=a43 !`0#'t 9(&@qXB` BrAƵ9L/+r|3_xF/ +D}FWba+~H6r'#59Hk}E9d's8`T +}9,pρvC@st@^ * 8ŽБ:h##u.(~.{ 3nꂺtt1ƹ¨PJ.qH?qbt͐pq_\[`jg.P!ˎ2ֵXѠZ<;YZdL@)) +dHWUE@_B Hˡ,L@A8CxP* 2Vt EE)QQ䂸2 S4 f=E @%Bu +qIH?NkSmBx)$d +5)LRp+ 9OJ)M +VdIQ)B`Hye-dpA!^</ +>.!& +TU :]ݐCQ bp_Pp;P0ZK%mHLW} 7u7d}w '@Jw'IJª*t"Ɯ9qp MC|kC-a8kB  +ލGbwìBJVAOU321!#&%GLxo ! {Ĵv ruHnC$Z"e8KXC]&=䔕TJxê*qE%(!퇔eJ )$*DD'D$fI4ψD]$sDDM($4FZjH XX ]8U I#Z +2`kx1Ҭ#`jQ7$h$hFb"e#g)L9c#<$B#,aw)"~-3?R`[@؊$T IqHJ=D8"4VaH Ha%6-ia8!*6­NCx$8 6! 8B%/*Bܳ2'@-&A%$kIK8=]Bx%Č be_BD!Z~ +vw틉(p]PWAf"x?90؝%8UoXvrM}Lڇ:nb\}h&NCą-N LJ@|Y9PT5=jGD':i^=NE" Л|<`PPnP\~#BA 5~5ϭeC11 +"F =EEQO&m؇Ah_%QAHT@DG悆m~JIeDٛ(^ACE-MOWγ.~PU5G ..CoGРbkРwX(D=9oʟDQg Eb+V)/ 2-b[ex(U8(cQLYQ($FHc᪑BacZRZVj1(7T +9203ja=Lq0e4 5 )^MN#0=_p)E R/x=TZ :xJTUTp*3&;ں@h!r6U7$VUjTɅW1qiY&jy_ʼn[pVق`Ȋ`̊$.E+Bj%RP;[,tVڙBp" kfJƂz6-2,7Y!LN~~EVȋbD] ? +X5h$KÃ/eA*ٲtJϩ\T@YLE xb g `ME?8ͦRL@K-%iWKQHa 'liQڲD/롰nK YX0?= +s#@8+`̉>Fa_ cE o0!n"dXG|"B_q1F;!dA1B[L6& +!ĸG%o3Ax[1Rc "d4t^@!i xGO~%#gJqLF*}@EH:AyMP@HSyܠ4 ).i@h$4#F 30jHԨIVP2pjh58V)lxA`A5 , +d08i | k@z6sVwKj-vZ0,kjф*]C+|rtV*l'67Vc9$#Qdj; 'Su1Dqmzm,}pB/vnr13(n%'JS+ nsjF[rNUH;$XyG#tgit^H'I6KRΘ}dlϏ3xiA@_DM_|`>G |Z)0:M4:d7bLj8ˊʰٍl+{`kX|Ve?ڑbM-'v P09a`^XB`XޖTZwiK*O! BpDB0PuF +B`]B kIV=?w%A-6AgRF!1ǜ8*R.LF2FPֺ[U\L6P Yzx7 tU14\T* m+'Ă@6MTi^"@p!4jk nmk Š (G:0KۦfGo`$ ˿V 9! JqCJZK`x\2??ۨ$a- &TV5).j.>N*hrm ld&*Y(C%|U( +`SV8|Ggz:7f` %(l: k$ +س| +lJ4){];u&I@]\&pI O5J&erfX̫\HWG^k$siHHd`w+X6A)l`@݄E `#.0A |6O/N9GRL"hV!O@P- 㿳ncYd+PbܠψGdp MRC +):)\>PV ۛpv 1Dfs6/L2`  +>iP0;rAТMXf`ԡc"O}?._V{ZtS><\b!ǸlȔFoӠͭ!wP?S' Z1Cg_a'$AߏR#>TF~*Z +l:0Ma +{~fx$WSh^:7d +ϓ"|0Wl'ٷ@?bd{4A5$re IXw%h +9rbE4!kKwC`'Zx`1WgA#z_Ʒ:r?xYϲ,H'xi]YHWB.9^ 0tG-Gq(O { /=(`x+:DͿ}'Xgvk8KlV=)mKSvq|\fɣ-T'_~Fy"W_u dp==&:Xz?BoFP^=m:[Qo= 2qJE#Tj VS n X:5Ċ/~@*ж'~l0nȣ߾QTJ֟-]j<}L1~~;M{ҮDI UdmLPĈ;C/l`˓`yn0;U 1-|3P?_/#29U_4l>UՇĮM} E(s xN` [KOC)vm{^-Cs{5 R'~g~~o_!BjO-q]̋L/E_ ]߽BKKuY;dpy=A}Ja v@@k7j!J{xƣ^/B>!|KƯ봽q"!})|5)E9+\b嗘9(?Hh_(X*Wk"Q 0>Xd) +)M%_V`Bޝ4jM|BGhA{@MD ` ‚y{N +kGד2eY}k*^/:<}y C`$4 J8'`DTEh]}r Ȍn`V;Gݩ~.$*exG^FUbUk-FGËQzo7:%C0zz^&O8y? EK5o{/٪d+YCqI?备zjҋaQ7ө˦ܱGYSF̜&JQ(zG{ E5&4 b)Rx zK+~(wU~\<!Z&2Qz>"cx~ 9RsޖT6H*I1  +v{0rS%m˿ovڼWs5?4̰EE/^f^>B7B̻gPeaoW'5i sPzmt_gdy-=V^W$au+nbMʴ9y_"FF#&U!sysDƠhːPyI=5tBac2Q~-pacB@[yjr5+*oe}o+ٚޕEc[}Wz +FAOEieYꎩ{=DLtlZОMŔ|K~~/c!̒`2K 7xB$m>'՘Тܛvq *,]}+t&Ȏ;:epuԻݻoo\?lL~{'2i*x_\/:P$laP +u@]/b|V{shʽ&p+ʺC0Ϟ +u`BO|Gw- +lc;Uހ鱹ba@Za=\-]B"&Ftă{qgі[ w'NH;uw{݄}oߟLK~\Ta[s>퉇O,o11WCk{FvVbَ+ * 9영uDc,]XNq:/Nd[FP_vi;"9 +yXS=4^0Vgs?܇ 6{ti&7#"̘ޕ+-8eGbA,u (y=5H)\\Jou{pcwabg$.v2 +2Į՞4Xyv/i*k:1Pu-!{GYПPQ|]°S ":#[%Wz kXuStOuлsq= Õu%}bNl}rZ.>C.Y!&pjsWPa]pAW/<垫ElIh'E+ W;}t1/8,N B&ۦ)`\cD+ -emfK_[ݵtlEڔN +̉J;JOdpJWդ7ʵqꈋC(x. y J0 BjN!{vj%%-m,@diMu־']q0\Ֆ -e߻ +lyЁL*jq/!. ,FB$A.yS~_\ *s^jcЙqCnkeASjrFAz@f^U'PcށCCC8^Ǜo֨Zua7w i <lj`cӕ)KQp?ZcjӬj# n](sу?Pj oP$ sE`0,<9n +# G/|aMhÆ._fLt+@#[NiV=~)n?o#3,Y~ly>a,xtԄbon!dT-VyV7K^V9fIi,E!,˔@8cRn+"߇ "NL'O,<$I'WDd 6LF5Ǝ/o]K;iR(֒?&m4nij5s`թ.~<!U4FM%aFi;DUㄆDv$X󙯀⍮˴e /8 P[ +ó#A d?CE N҃%`=Ww<S [xuc9i=F=p)(ҕ~v9Idž⣼- `?ˈ)MMVbZ]- t4NpWT)agyvXj8!rAZx<Rpr H<WȈޟW=AF'bݎh55y|Bzuµ]9 48upG.ngds~)b~1|k@82pXLtcK4 |8BUjQƼ (WX|7&;%¸G8ap5&<+'‰Q5\vj7ut*p9%:Tl@"1M~cipQk])k|| +k UѠhf:Q ^jpzT%Y|o58Ti@tp-,8 o'ү3P@>@wMZ[5{o N͔n\I5B45\ICPhbXv58=Xy٬XKC +K5 ?E;8W{ԫB\Yp"3?T-Oz~ s -Zh5"ͣة 8$0p~88?^?ǖH*Q<* QX(~g3>iH$W6oi$o[@_/&M5,+߷"ς1-} n7E?Gߍ(Ov + /t +(,oAxN + pzىL330* WLbkos1}~]g.BӀ:Apdһ}hR-匚}Qչy-i딉]5gJ^oEMxs=69;Veݴ2 ]vPjSe)rsUΧ,AT$duX";KC[Jnœ1 ˙(csHymJcTHsG{yTuŘ;sAmY17jx2憬9~1͊y6׬(k6w U endstream endobj 19 0 obj <>stream +9gsH_?D[s#ihzE+=;B.ʘ-ypy4s<* p77Vs屿 snH$GIk>;:wabP^tn^fu֥C3fMƿ$GE:U;WshѹMhq*$ϽX0As-jMrWzsk\K{6Zڝ.g]8 0o:7͍9QXX;E6r7 9ι=55Ha:l0j\?4!.plصw$i97OӦ xlÆ\m0!+ʼsQsOk8AeZ]<1ItnM!CvQ,s`1;.fJ[6wiQk|M lFҷPT\Kf +h [8=gԲ-)eSKP @[G/~0$zy9`΀_6 VF}?hEP G\ "f@ $Y;U,gbE*.%ۍv"@B!VivF6Qxpۋ@l/v1nS-$1U e?yۖiGڔ.~|Xk۲R%?`!^Y\yE^C$^SVa͆Gl?SLY*Ķv 6#p6o^\kGʫl)Ƕ%kt l^ #GGԆ[ϧ=$X>v0?FT+]PGᇥ$(Ɩ +^~TKCd{YJ7@AwnVp.Lܳ?Kl=$̈t(JGH7(lI6r*֥MMևfVI˞( كO v]@097 :ӷHUW"7Rֶ]Pc)8yE!h`nre!S`lamЍtO*fdtkfcZXJ$|Eu+RY_ی%y?@'$(l@=O^Z@뢓(dϠS vVJQ ]jԨ6"Q6f58E&6KFxj]nf +e-J}chu qڡ_X9"aӴknԞ:/kZi`\nht_ݖU;9}υ~ӟ(E:鹚\&tSF- Ihf4Ěd2OBPbHKҿ/ڥ]G8TcۖV~4۴VV]Riem''O$‰JGAcpH@~ʷύ9==9|J6\ȠS$FG˱I^Gˆ<{٣bC}y}mGcctji4ϏCʚǘMF@?hxq +/Y%he؀!oEY#6.:hBꢩնu`q J2һVEtren Gzn_gjC]8oo|l*d<)C'^@ >@A +BK#ܞ;0aC{4z3qT-=s$jı;,px'πv*-fnV=tu{P t.wV$b˸ywL ̏2T_g3O̶:{@kSvb:i0ӌ4F .s> 4YY^jBEI/qU*~ gBfX<&'bT4كkps9 [-@Eъs0VI;M66K+kf236ٽf` \O5Dv}SwKF6v:k4-:8Ꜫ|kxf-%IUγɝ+9q)Mhx`1=Q7UE\r#Y QI=QuzGC6#=c".sϴ۫A; 7w=!H߳M|ƑO%wAhkVgSk9 /1iP@s +I*wy3CL ;rJxm;#Ե-1:me8y)Σ57Cs~]h1#uZUW퀐p*d˂[v .=qblF7[w$,:O[ 1F%t\Cxu+Ri,HoLRp,z\Kʑb'SsbRJUP)Y)6G*ԅJ!]K¥Tw&1S,fN;f +Ā04=SQM oSxSϯ*4gJlm_e6O9?d +lTy zrޛ/ *4"F u} +~ly kcTtTjPƠ %FRG=!Ej\[)PF©@qCk%W +jX"֖)L0Uث$jE< sJa*ۑޔ[rٴ4ѕ, X&Ǵ)^e3R9WN9zB꫗60X3Kk0&koiԖBnqї՜ZJ7AkBȒVb $wSV^p[gwn-p5pUe"2Nr\"2R˲\K#wz12a8O{ :=g/v +uШ{_yc_ g&!6 +آ~ }eEgr }KŪJzۃmF];M Yw%7|+0T}(kkFCXWDȲpqӤrz'UK.fT !Yh vl`{"kהv3l|f"i gqZڥyis~+l:`QDT.*L%k7)lzx]]_{AayͤL2[ h bқkY"@mkh'80n?'l!L3׭PLԚ%|do-ǩj8_W .$iHNttz*NvQݥ@[M.}lRJF́\!$hxf;=*}k]Rp*jf<>u +yu׉?"2[r[(1bI.;s]=NZ0tGB2ЈۂP*o5'[;'tL,q2. {K&;vB _;]-K}o__fYfGN'op)C.4Ynr&jfqwqT V/$KJpx;_;'a g wW +$Yc^*E?pΌp_FxX)Wґs`/< ~Oso(;+܌Toa98B}=H@z + +S?J[<9mV}/!{9v>}#ֵZ$JPG:oPF} !rSɾ.v3$*Kj#?e_S;.CLB)u'h_"ײF^#l U2U/4 OO<ho6vuҾF}`u֒1YB\/Vʅ;%`~o3^ +z5a:]`ߠE]D369v^(ڄQkf`k%z- pk%5ey3~olEi;ƶ3cdP7էq%mǺaIN؇P"-7+8<=r+_iΪ ('@s\;LP5ivn3|p-0x/\ivPLs`vWY6aUO ޙU"`e`3!78 ,r~3):3J K +ǧ^4`/ْah+i.9>;lwzx0"b!EL38 \,V] QvA(.zƶFQecus\QOߣR>;+oZWYKGK=J(;ق_*ԓ9xic(^unykIdDŽg_Q[4Ms:rS_ɿ3֪\]8XhGkh-k=m'?nhk>:yt~ wр2wkK [KϢ\o vH w& Y) +o]YHuPjʷ~+IIL%k]7 l2ZgYަmT\KvN-*-Wv /K]|P\K)ÖsiBh)6D;ܿµJ[wҌEu$6oؔZk^bMkTEQw6o\fD|Ac#=E`sD ZFFywC{UV?ܚeH=fT + ΂D3hˮ$>6[jx!v#} mLدc0#v+MV+];C9*lR_"YNS9FaHvҡ|]]7@mmS} FM8f_-q;"ܖ>>wl<4ϨynDάجiPe eȡuS=uz -}t{k3YOI%{`F~54?35n!'pdKĖw1 r.B8N@^—v?xɁp. p~ZyfLbz-<ݚ,;1E +y~3arYۺoԧzO%?\īNj2,}_\/twy䷿a/K#@͡:>D{W{5ﴽ<^e6\z{ͤàӤ{(bS:uojO% 8~='=z=S$z۠}'S@8lq?7WnKqES܁ʷIPOH~̷oKyv~P[BY17wku7ŇX q'x>_(m [ᇢ &c +f]*aKZ$ݲE Ms&;Or[X،%cBsʅdtI*kw.1.k4-\. 2{G$4Y};:]8`sM_'ߺ cK{ g,D'ሾJO_J +do:w|;yi#8:U9d0 .">q;<_ު:lB) @(y{4E>{ҏ[3 _j|TĿcfiKYM4 1H3gX70ZHl1g3c:{'lwE9}͐*vݻ.o!Hϓxݾmðs `6$s>a3|juGh{9 +q9^;'SEC^"6,Ao|0&PDc ФTxG&s>fX@I]}ºoij$|lɿ$ڽk4MO\#JB{m[m#8Wt>NMaZ йGή |2xj3NwnV4ؕ<({]ƾ(>{Hsu>{}RD +T8'=qt'qAE:`:ۆ07t0sȰn]#AzSPi,ǕxK$طvn58) RHļW EAӽ?|" g0U4k;8`fsn28a:&kn,nnbuŎbG0#xeCz-dKV IFin=S,"} |xZ<4k+&?%mwvwS!7JGgg z!к͠eU`$T( +H|i=Eg ǽ?V8rS**껐|~&-ÇVB~~% :]O4)x,}D3bbX8l)1Ъ)'q~5㕳< m¹?ӷ6U)t'ki0$t&X'yG?%Ю`LW54-(k9`G?;UG$M@DrL9^"|x$E=#::=F=1϶!0x ce=6YOm#~Q#i.UTkKz(VEw 5K6B=i?A~vߛq;wvvҦ6p-C{|{7N7 uOA*J`J1lK#Yױ|Pv +JBi"RIydmuÒf~P>>zI"~Q$ZP+}Pd}8mM`qA}7!) n+H1thV;Qg> Gfu~0ò^߀T߄굛@x<iG#@Xs~Y;6ge4|m]mv2PЭC{z6i/pX71Yk蜷|t V|U + +U}2&l Dch:40۫ [JdMR_pSLP98`~Ihk]Dү/[E2k SF,^CmoioZ5ZHvLEc_.^9쫃n>׃&k]љJŤ7PF,~*@j}naԳH)G@BG*9~AEA{7Q䠭4yo?{o7t'weP !!I߻R9>yZŞ*Fd4l3b1:3 mA6KVOy)TslCY6vOyH@[jWP.9.ü;l+csMY6oVkanm䝿1c=mx:Ỻ[?_)yL`s&6sLG]5q.ݿ!5}p]N3gr| F2wj5߆}eȬbD+MaMp?Y'(h'y~5c@"-(\5ha_2/(k +MHg tS|e"~kؤpDfM |qr=1H{}GU%u{a +F:!@i[?< CDF6%]T xEE)k3JݯR! k 8sA wx?K#:;&ܹu.Z>ܤ»iЎbEH7fG0ufZD IR9۱m?˹Z1;6'\WY+![LФux?*~,e_5\K߻G=[GSǝijZO-&lsW&2xj3ܗauطN&w&6]:G\W1xxv[2;[!tih?8zH3z$ϮQú-W[;vױl}Eع[+Ç[[+gźu-Ahoik'}v(#R&ٽ0Qe9wpyYOMV8& L~;Y Vh;\RB^ҲZK02*I9^; LB I@i(N;Iy~͟c5o g y?cW;϶dX5Z aXٌ̞kmt-}-VgsvMa̫mpu̝6ƎM;cd.\HyzP_?$ XDv(M\-F,}#N X7[ۗژ2Zf/M3_dm;Ӹy:׎yB>'\W3k a6oF0ſ/D zηߕ9ȳsm꙾FL9ģ0 }g$y=5:FlgvJ J?6pcsw5s$[$T94N( :_;K|&>|~'{ `\m#o͟5~p?zr$8_ΗΦs6q2N, ]-LF|^' I^4~pL_hm^RY;xP ¶W'~,";2wl򌟛lC(GU@@IbDc7G.؁c/v&o5V;t5a9:C(e<g#Rd 6F?1uI>{Gluu IEg|l/1;w>h}y@?D)2g3~o'Wp?"~E?OA̳N5ާc0nf^ +OܣMAgQ9?{7w <.NbT*T+"~D0q~˹Yȹ~僲OPY+p؋<b +XzfP mh;Osy:P;v~,S)$:xC" \H'To?&z] +S,"kVįڵJ{Uw~Pò+0pNv?|~HSA$cxst_Nj}l G[H9>{r#yP  mC 8s^N{᝿!9:xy =g/PvU [*iheB 9Kw H H_9.lKOG?zvs{>`}S86 QF?$9o <ӗև B~_38g8B~6ԕ6[ cgoqp//h[4L-7ݸ_CqqFƾu2,Zp[6pֵ/k쥍&{Qbq8G4ju vkl?˹Kڬ[mj:lKk0>`|6F+q8[e\C ˂1H]Fș9ݹ9>ޓיB7tG8ÿ4>zЭƺu-?{#XKp[92D}~NX ?;цBUK^{ l(ºpeT( 7%gq-sVе=zGQ<7PAK)k }Bc,$MB.ѤSWt Kaf7,c&1ui81CwPߍw q*˚<\&O0 T=@zx;ry>nGgںM\7,`P~vfm0g=6uC;FǾIn"HvФqض{C=,,A9ݹ g1v7Wžyfkg3ya0z\2'\#y Y6\]Od^\fa[~&&¶&M9|ko v{[2{i[ź1{&a wkcc`/gٱ621nbض{ ǰf}+stv3{juO^}E>}avmMB?sv, Cwx ʿD}"=ȳ;~ۇq,9_m|e 2{i˛MVW3z mA3|j ׾mXζ2yk-6=yGȝɼYW[4{o~殭Si~LZM w޾5z=zq1Ļwtܙ<6_f IF_d9h3~oShm ᜷ 7s~|g7~o("mɮ]o11whsX.:w'{Ih/u~oMgtl<+ij(ۗ<\"@#g旷NfL޽lmg5p*xq&*#3҄׶1ziZ,hbxYLpMn{adֲX{w|LN֕_q07%n]CAB.兽dԲ3lB-M!<]Ӿm}l ܃qxaFr&al=m^Դ88tu ehjjrnrdմUtk9jrM`7 t6MUM tβ쪨xhҪʺzťUťdťw6֤F#xzE Ճ,UUTYXYX\9ڢzp K֕,*XYq  +cEAW*0?L`~wxqcF!:N5utf&ḏɘ3ӱNGZ X9Vx%sx 0 Pt?~(@L @ 7{d geAp-_Ș1m*Lic&G NDE C(v"nn,R5e0dѩ٨2S|cbJ׮WJ cPŲp YS|•ޕe/peq +ׅ-nk'`2عm$ 0.O[;mȢXd)d?*)d ~=8LR5ITi/@?5[NG%bʿ$="^ {2 X$H&ؕG̮D1[`I졥,EMSL/#0O(s,YR/BN2s5\%oiRQ'|+m긒f9d +lK.<\hu,zi+OI\D,EܥM; *$-_~oX<| 6M#&U +d?U1p R`5 llC,%TLۺ8!K!t%MH# +|+Yj@`7e`Q˜Ĕ6PV'uxik(!`Ĭɔ{ Sƕ4XN6Ie.,2)ɹXH$-f dme |%3+mLJ+D;ȈK"5(cC2IXO8I\Q ;G A@,5dҕ +n'b<>xp?OP]$B)*PStG'HNQM /*tGNNJ#N!!@5`BRb1Fp_'LR2Ů '4?dtzR%aFFXMD +$a71rkPE-uՉ^QI$@l?#TQND[؍:dN +:ซ̉N;AߘȰԸȸϸPm B`-mr.\$QqbfeB$^#L,7(R2crE ] +ld42X2ahhK.eH|HW*ʘvMr]b `fvH ~\n/`u{q ]Aao0u9S ڿ[%e_"OR|*,>Z˕S~c/h7p9͠Qc 1Zr  +'iW(# `WITa-ԉ^$0"G0PN!OPa\\VA dG&*j4}X@݃B⣞؎9*?G#ULWT 7eLV|&vR),) mɞh(s6)Ya;~,M3jYj*|57mPQ۔AeQG3LQ&yH1s뢣2tg/.t)3QT2 h Kz2MV(qM4=y.4ubJ"J&,j<}8wPt3-6?$YC˚*n!̅wbWH?HFUXY.- ӤoP`tJhuLr|'5z"CN(.3#'Ҍ19qYB7FX5!$sX5J o +:6J#܌LAo ٲ lf ڳ +NB?6b/:XFp'X".۴1^ 0BҙRᨅ`쉋~WD}$=G VFFköK%q9s1+4,΃9ڑ?5bVbst nW3&4n \L n׺l]nvV}TЎRn'XlL W:ULi|be$Aō3Q'DMS'uP$]b-\jE-LV!t!`{B)C ϭRꠝ5{i13\97dIÞz2)̰0 Zc3*CAPmVmX4r@-% 2p!YkHyx7d}SY1 DvHeV(~JEW:2g-P& ])Ȩ;b^rC6F;F2b[RZ=YR栽~ŅbYf6umEms1Q)RM!RYRQkAsИg+.dD` #(2Y6ń@%Mf]?1aFW ʪ@EUZw&)+ ۖҢTR=ʊTyBQCVz}6zPHDʽ嶳">;@-SƼF6{P\ ;m400~ +m-$*(hI椅]QNtGaof + -3.&VF^*Wb*s +vWTi $'2;#i1QbRX}*TJ*mAJDvpcdT`Tr*KHB 6+am;I]kMK9ɸ Q,\WQQQPBm%.,%gB÷)ѤɊޣd[FbRDx[;8i,u K.A33$ 8_C?+I5#sj["Z3197QIG Ygx%9(|Y$ܴ#(-a[dП - _m:7ǗӢa!BVr&NѢȖMvȷ5mdl)ƪx`q(3J_HJXc;\1cJL\A8)+ +ǟգ+5ob%u&4U_jwcTzȉ2")*8(hŻ'ujMD xC(Yo +Cf"H?ʄbeu*-#qMJ#,8$2^]Nفʹ*\WQ͚25&zgPH͉ {ʎ7/"ְ BS<E홨Y'$/,))y ' ,H ,6up\gVF*bOJJB(D]th8}/eBѻy:6@y.$WX+ƾIhGP ZDbj*Ma/ 1P*@Vm-&?~>~P= +N40$IB5@G8V8*vY{Z]Q)P~ +H$>'!OG =8t0ra])GP!VNƶeo ,x#֛e޴4nshamLڼhב@~5*6kMz3(AK+eҁyE7*5N>U UBJIP;BϚ +G$mźĚXECSƪ9'8`R!N m"JB h\4ԈtufL@j"S`TBQY;K ɚeGb'qz"A( Q"m"m(DoB)O0])۹I e;`ay%#ʑh0=Tw#yi +V +N4$Ÿ4zs4P`4l N&D,>M [-Ӈ6|`}jR >| 1Eġ9B] +}<Ne9fԝ5n=',m^~9ے@E`MN?&3i2i H/dI8eJڀqB8 NP- c}QBO%sI$@aWQ١Vm$PBF g[|YduKhzXaM`J*M +[B*[尤VS:CѯH]hʸ +PF5c*1^H~*~iSl+ +HeNB8rMxnnj{ -s)T|eCsd9KpB2A#?zXY9.{&ב+)& d}Ulmzk:~}SOPԀ;ri*?*z mѤ_S)kd9x(Hha (7'}|/XhIh"KB;U?1BA{ߦy==`hT aAC];~,Ŝ/]O G3zo5O؎CHgԝ/h?ዋ}}9~ OJ-R2AŞ :ἚH!I3IwaU3⮳lFBgH2B$]$u&TAIohR- T+" ?S̞ZlvoW=6e%ToDZ`x XXQ&kP]^HJ{WѾCrAPmo"?{P$rhY.4T(VFVm)G HEWPcGHJLZJ$ !+J<3akpRڱ|p \XC>lwMwu#MAτ +Y=0 &QHCzgR~ngǸ ` T08W\5#3$! ""`zp0dV`B6 yz~p~/#wU(}=&P}I(FD( Vn1%Yļ%c 0@}}^j4!'PDw2>;Zs@2fh H G@ >OFm4i"]+gR @}:_;"J;*Dc]5N‘H:jEb@Ga E'~'paIK02*W82J[BJcp%z =@i(D;[ghS8D}%31> ( o*6T݃g'y|Q+i0<綥)tuwi5[h*o:AY_VP=$ՌDbݤ*xpj\ @4A1̣p_JXk GL,=QzWp;&>ks"vs;BE:k꙽ahlVW +Löd#V[¸qe<7'#1ue3kl}루g[T$&mO»Fkol;5E2NgbPe O02*7vM/ TX9, vN1@}J5Ѷji;۳B~]й‘Q)U'Ёoż6`,Xԛ*h99)c z],ܟa=6?Vʖ:$!'g=K`S)mv6ѪGc׸ڗVrFgM=LxXWրDQhwBѸ9WrT},VH6 pbk_yѡ rѥ>X[`ª"v{Fdj&h-Fe-gxJ=a- !)01 2D7paIGp 6m7V6{Ȳ`c_3؃.f>5l7זsv]omM|ֆC &Bّ̟iwJB}ډU LmD"m-Z +jN =zZ"Bm@dӤܠϢ^n"}~@8dXNnJn:"47^H Vdt;傒 qs7 7޹ynyģNu)rWmVMYjc4ZX׀旵ZdʹC'l'c|;g01fY-0P(@Ǯð)PI>`@ T1,g32+v\ +VDG?}uN& <)@`]F^EJa^" D; +G.~i12fejL]Ҧ k!zKPlWZC VԀkUq ew&?Ώ1@BRJt+VdvHhբdYJW>@Rg#J28sX7$*{*a!AOV[ M"O02*uzUzIDHa!'VGk'7}9kiϺ]Mkn'wx\W4?[1>Łi/ɾ [q![";5r+t+EDRb:MQQA_pBJG"*/X HiP&g/#Ɠmr25VK)M#^Z K_2od/oMmԛN I&cݺ6`f1V5,CpBנϨؠ[X[()h2($j,M U2IwUYrȂZGP"*GP*SŨ2 kF#Bog adab_, 6% @@=9<8inA +}jFJ8j)$,(ȼp+,ȶ1Ъ}XMJtrWխ䄍ů)c1'3Ppe5[SRݨOLiDXOCxwx7A>ȭ;\WV \ _i{1*tPe4/jc| B'kq;!Q)dqLH0[镃( g' WNT멼 f, +xô~C?DU}2##kj?ؙH GT\mv&H D jMւḱ #zءXZbV0#/Ueu7s.x\ Bn3ewvXT H +Q0.~`E3k,#LlLc JmQ-шe$4wFBhwPztK B +j1("E\d7Cư%DGTQ6,>i$.d ؑ>T KVZۄ> +̰J0z ƾ{Glg]o`\Y^eV4p 98J'ټ̥W(P,TxЃ>2q0 a\h *ՓBa(+%cN0nHl@Ģt +@,'j='8n H:)r=a?8&L$n*3fAиtn4nJE'<#$! +m9u5ceh#Pg$RCqԛcв0u6 |sxLľΤoSFc6CؘX aø{ 94\CxB\abLØXS7*XDD=pAG`Rmd"fDZxnDjP@Ėb‹Q!A(+&n)Xan@B6[›#"=;4OO &6]-~k`8R2YvwgSoAQ40fohbz@u t/07@p\m#:&.f?^.1Z$RmF9 +y>bR@ PRŠ% /P1x*`o#i_NN?5H˵SN |KFƵ8p/3;jW{U!kai^>ڜC::LD*ĨmeȂJ$T* dC8l9?jj-HGFd^cb[ 99Va ȠGH†\S`O .V7 1n[0m\?A+thPW˲^c +픍J9Teϊ1X)hTz/*^ꄢKm +iX_sq#l ̢en¥х)bJZ؆"_7sfr0Al+¸%3DQ+Ѐ)pXCGi+$oUU RȕYʢn"bYg"Ϫ9 r>X|2$(,2rUNm@댕4۴q, <čdhXk R*tB<6sxFX W!s٦} $c&`k/0@ +@Q"%`EˮzRxx= 0~`ZX1E.I!Hiܒ1 v*k-#ixO8{0Ĩ% LZ?7jD6OQ[dڢޱ-S ~uxd`s9j1<c67&@߫G@@8ueР`KPpbYmdxN5Apj2Ef$d+I#a1*EHKQ_MYGt$Mz&۳1,="66vp5r'`@`DRغ $ | +06L/y(gqŀk^ j@80)!if xi[ q~h[ +J#5.rk\KjLҸUH-q8oeQw1)"4--"!e& 8+mX1Wwe :1Z73̃&"K#}~.Z?~Q` o|Y8Ic]>C'Լ2F% `Kj,8!-bf=}p0'#>ěd~8'I1B\I&;UX LOJ@"Z&)L shN&s*m*$P 7"6va<C񖮣*V02ܮx'M!è8^W8!p[GIAS?HGO`2 xӠUA IH_G8330\ƀPE A4̍B1s  VM8`FҐq"Jbc+:.T'U5B ,p-{I7+F $q DJ`ijxV*2\ECSd-<MNk'A?X y8Va#@ P!":QB%YD T-4d&8 ί9>-{0.6a3ƴDD*[!0Ă9g!9LDĻSkKZaOGɈcP"jCė`@Q2 ]dzw,8)Q@cJe\cE gJ$n&D(K"~8 t8Wcġp?\`9 =p,ց{\LqSn$It +"IH g77: m54|c 2@!K%h6aPt~w 2cHWN02Rd6޸ |[^FjXϐTĭr g@ĥb8G{8:(Ƈ7p.m`xC.tzqBE[d *XT,`RPs89 4p +MFF K4a{SZլx3īnNq:Agk - #!?)P5D>\Gp݁)W) |мtwIdĥ߫L:2gl8 w{L&X M/_66 y5 +CWPCH2K-<-Gvj \yew L0B)a`9$",AÒHLCa:28X;C<{t^'v]1 \ 3)B\`g>/4\gs9p9s_x 6.i86ZMd:t:\TVZ\Y9z`KMk +kk*TWۃ*46645-4-5))4lkjhYUYOzFg%]U%`%_`#9v>{bdU.([LvV_TY6U.}bɳ0UeK4 V4z$gv~ \Ц$H~І:e,X/. 9Ki5qfO:༠G/57mpݏyC%Y01;6ɓ^@шs|R3zǾW%o=;;? +3@ ?柍7p+eBU6s6ra±)g!FLMiBHKZ܄*3~n \!Ly Nh] lC2&C_DY0Ф`tjh3cca_xV\Ka'WMYn')-NQD&RF>.uկ5jw5)NH~'_9Й«茅ò/0!ٗ< + ) Ghw*=32vPRh=e3PY׋6 `R +O> }߿)ij!˿EjER#֌ˏ1 }#"? I?iuy9;|}ch\_Ke/,jep]#@|L=D)L^ZY;Cw L0z$Y䳑26TՍʚ^2&/06֥`f-a(漅TD eNP /j3vgyz~|R# kO+"?)QJD ڙ5Z) ^dX5:VŦ}I#FH`#XNS)cxMȬ m R#{Pd VE>Hh`.ZlIKJaQDU}6PA _ e蘻[)|2/HY7pej=6&p1uyucќ`<P=N ɗ[Lk&E7? ͹Y-~VoqrQqM`qșX8vCxQ$MWL,$?U +ɮZh+yvkh}a)MFDƣD lp=Zi& ȕt֪YOX$l#NTz 5lଧJLvS$*Lw73_=d.~;Э!sU J ~@ qYzi{o +FڊFem@uv%QRMB$fPx.B~flo[2Ȱk!!eƾ/I̳ww6Ѥ=wc3?ˊu thz +><=8_S/DɆ FDz96ڈjh;Nb@c_":Op%.W.ʸ qlſeccVm&RDW16iV: FchߺYcR!Xg?&P~ f;vrRi`=M=Bҭ([ [Ҟs*&7u`\y<ӨV\ߜ}"g`HLLN;+=قO5F ZTS%&}Qe yJ2tdz+0ý?sV9:?Dw7ZMR"d hzQ`Tt H6A@ţXix+yvo Ǿ_ xKPz|Z<6it} hM@7B ]|cd[{AӎݲI4iЭ.^`BYQhRSw-Kk `A4U+bj`\TXS8B*KJ PL*@)o +srAzv&mDQ[:8i IDk=icc蚽SX` U4"xUU̺I{4f"}U_ b7yE6$؞"iC*z|5o_[m'DoIKLQAX=,'=i{#ǍKʰd`0$ +FBR9JejMY6:g +>Bi/"^jB2PI3l;1zxe +={c时9r$W1(?͞O+@IzEp @/UB3H/j gF=$Nt$ET-%'0@3~kfΖn VFD!>@zx;}R +R[gqnLg! uO9*j*:=QIv-h#U]x$BWkiR]?~= ۣF$$? M)m tQ+fCornj^BH} T]Q{NgGb0O^ +O~B/SVةy~3+1䳁n?68W1ti4e8 neEz*WŨ T3e_BnУ1{eሩ`dT|g꘺4gqV2>Y;wv6NWξ})UHhA6uO$_:-D9ŚL +H8_ }D鵕4ܟ=Vnkmľ.4~n]&/yov5kM`Dzyg87L,I;k9xy[01E>/AHV1,R)ᗧG0fѠH7N ${ k+a~F>(x)h0\O{5/rk׊nAޥ4}m58sJ)a,ql9FcIOFCxy1]9HOxLTx# AkcqZ#2ݓXW4gMB~X446&P&z' =WϖK6oH Tl<ձ;vflുyua[=>e$yh%Qp-U'y~{h`@$M5rED57cF+T9ZDc͠F$Q9O:C8HW?2g MN FHk#F+&OXϖ;o^3,Zy*6X2 HBT?#6?z~E%=BU/g3Zy,MB^G@%SCb.v($R?Ҧ#@~܇3is(t#T|2vjixoCwyR(Òz`^IUV#(? IdCTo 0ա , ul|^Y+M> ήm,Ӏa}qv=[ø*nB.fMTw/E:wg4l(XMI {,J AMt8/&hRоQYs0BjW@ZuFףRVH`c픊H7&v)] '5_AZ[G{Sr|`FPȩ+0EgȬhJ_ډ5ܷpW4.;SiA0dJzoB-<TD"vP:i[cФ7LQ/<@(3l2v]u-J/< au>`|Ӎokz +ʲZGH"]* "@?FmGo:g\:p-8EBO>2LTxBD9xXTv FH{$]Jc/&Cg6&&c&@"JBc1WG?X*fų8Vǀw2}j[D9yM}2/sGz,k!3ܑ>ީUqr"Ѕր$t cGBS9 2 +2Zo#U3*~*dYk82ǐ,.ҰD[I(<ͳG8?7Od_muz;"^eaO ?jh/vm ʾI<:ю1a̲;ytxN~Ac1thŻhYu>YfOm8GY:x4\ CǕu<)ok%%>ğF~9s11!J#ݣgt0Hţf &0nJ@ VD]CS?uB;XqYSvgNB>ɈGR$;tP䟗CmVF/mz> @}6 w~9ێ}2%c0[ݭS.A<)8,{E1fpnƲ,6F^?4Iwj;0^dJ pfd/n؜vlo6,4; [Fgn5vm[D@Nbo8d7mo~a!A?qIu;Ót+BI䳋, m]HH"5PQ@~1uJ +ع|\I>(3~k4[1Am[ D Oc_1q f.'rOu0/k6%_@%-e^J'i i@Agy|f/vQV + 4|ps_d +DH2,ѩ$zgjFbưu CbͲ | C!\A XLZ@@U<0T20Y=<GJ\F>e;&5nJQDWSnBw`r׀8ӛт-G49{*2 Lo2j-Ԃ`|?9=dA_iVτELHu+xĈ/U)1sJ{A79$T5tYWФ!Jhb}S`Edd lD$V2(?C*C 9 U +0ti٘2m 62C5i'yzSӨ׹5X\#UqP + `H:-S@0 Xԭ<afc B@ӢCsUg'w8 rTgGGUtg=!= D3 'c^0a? HYTv+T}zW y`7g]>Mg~O> +XV+fMWv.vpネLta[wƗd-to۽QOy1#Guxe> ĵX ]nk;Na"mP7Ii-_n_{^B4fKq֓: |F~<j6p'ʮ^Y?kͭ;uI^qFX')0%Dι,_lH$z ϚUVx^K y3[=yn~_b0g=MpmgquL-\oYN|'6tS2>?P!q=ʯ=cw 9Z.xDE`H7;Y}b%v?Mu+2`=cU}V}iqgGbVX[1xOR:4AT=/.jAqȫ7&M'vړxq8z|=;YMB-Һ,h?4 2+1O~ g[90"A:VAk6zw*^['+!1?԰-|Ÿ[@t~Itù{mК%{,2/aEԥDC 5NK'z'ح>ȧvbZH*=z1y/cD/t;@·D3;ӝ&~>4h +T 3Rkh͏shp vDT>g ρ޵iv_ISq`MMuī=g/ ? +ku]ʞ[\{Ń +񧊫w̽-;,mfg;q(h?7G (7 +!WSO^fMxVa5`]؆.LUlݑJ%+س/8ӄ<^ ^%+T(Zz&{::gw5! 9 il$^r\(Ui5]T{읳ռߞ;B2A xؔ"H=n8ˍ'8L@c| gxIܵQ0!Ng(<\+ 9Z: 5v)۳r%sG7Sī*pcoQ9} +5׌8j]%1C!+kAu1E@7)q&[l~6i4 7 Lμp%/ls߶rPMqGH?\~[y84S#ܔՖNpgvQ(Iۮp?Z?܁ԍ@`PzV@FjI|2gH0V W3@hxF͇^Y_;$Ƶ)6 (xkJhAk6_-ENzɪS Ϧ} |nXz8az:]-JnjAk {LP3&(i6 +vi72 ewItvyj`Riq䀛6s_ ώ ? <]]%^MQfTL#2AQkO}f#:OḬZ晣! l!Z X{k{ܸ`,g$ZjNh/cFUgԂRAɹvA¹=ϡ-m||T!rڄT,P㐄,^]gt ˯%Py2;ٞG"k #Q}"V}Ճ;1J|{XһqF,a1cU߆EM%K1V.@ Q),.m 'q4b枺 *(c'_ W][F8.ʡB+P҃&JygZiX5R@9Y:* @e̵n.di.3< +-襶_&RE ;5 oCq.Ijl@:#1'ƥBi15@Wu +CS +U@E5_rtqa^gh"kIyUqj-D͊S@# 2:!ƾW,H\ٴMT]6#bK 5Do+> +oT_AT {\)M;#1: 5=[Mse%V+ @q@ +P;CR̹AJR3EV,V hL~ ̭=Hw(Y}H>wW{\qA?%ω/:JJuJKUh'5}L +;!XI_La4?CP;[s>ɽl*/=20obIa4$˽s.Z-F4ݙP;ܡY;͢1:J&m>#:*fւ.3;XjZsw֜1P~J/ +1P`hR{1VBU:+casKR" hrBWPh})t`1_! l11Ĵ*4E_7DS +Ѣo+I(zOS q q81y$iyj髝58=3`hZ.r%رe?92oԱd"y0 y#OO?ala9#dmul!DŨ*Kш#@@L Es=[%>@A0@ a0HA@ R$(5v!&1zٌi4RAL bjd52 Qhq#%Mb4pxN] |J <`8x+>EA+27zd(|u(/\Kb%]2YGkJ@Rfa&Vd^ʖM_%R@)FS{/=AA6TaS p0D*J{E^mnTC[ZlV _;!$@*`xMQ"DY:^}TLC4 2?vE+dvXۜQX55)R[x,vC<}IB0Y[8lC1`cDcԂktH?N(7L] GM:n)ޞ":u o@G; +#K&xIO'm9TkpQԓޭD0YOk'`?F~7CVә1c_X!e +gr@݌% ^\p7f}KC_r[GW`RHO!VbsJk]Dbo?"[V""AgF&sUgU=Z& N@_-:y{2fc`Yx:jNRF^UVئ\uu!fr"s2z+&+)NO{I&dmGM\aبEkQ+uG`!rƺ0ƪc=҇N@.-?+# D(+f'n=Pyniao#yPbli'w_0Ɛ%yա'=8;wQO# ,I0>@0 &ʊ[ze%JLM|Cr6h23=Ni?%͛deѐ͋yxw`#k ~&tIZ੖顉91,uxQn3^[(? NXj +_)nQtv'jWDȾ@ ,\'beC2sxIQ[mS) ^"324Y&ؖ{o69xƫZDb%ްu^&<J\0M8D Qc|_ kd&h,vyp|L}3r}EPƔ] 9itzj + i)be";҅ҐC5 y{ DHȉv@&iC#c2EzfxK{({k >s~f'_Ļ+Pco¥Aב7БKuTp;slţI4 tiXPtOh szI,mIXWk5_[K]zψz6Q9'yܴΠ2ݢ8u?L?5ӣ20rّUЩ|wv=kC~".t +_ӝ&OKNfFlQ" +bb("Gt1p-\A>.FHIb9pMm*t*'}#A/uVѦ!'>sUGD] 2-c&rHo-1%-c,g.Y5^PY@'o_d22F.pQ-ڵNvɣb-Nќm!Z5/ѕ5e oF8ۍ:JQWC$41I>bF^hU>X0#n)p5e')"YkN(^g¿J`ND0@;FʛpsD.y%\6G`GUiS)>ꙶG+ |\摝 Zh}hFjn{w*^6BϘFns#UgP +MDXlߗ k҅g/Ae[*4'r$[+:4Դ*\Ìsx@(e*mXM0g  0:(;;[8x#D *>@dy^)hOh$ $57DB\ +ILj@H4(Lw+T~6f-ޗKZ$ģ=N/*)4 lqMtO=Sn:Y(lsur.]S%*FeMx(YeM3wԉ:nB!r8NKe;?2',# n4-8aa唛ѡ[0z&9Șʅzn:k4w6=5݃d[;d)>wt`ZmD-XL/ +϶Q e3 %D cGS?QP(Kd[7w]M$S. hyJ\RJK3NnȒF' %g C !B!(kNo9B‰Ho EҶ9-Lܷ(- u@*։G0CGUzsED~q,)-lic Q@F:RJ;EC!x5x?:n^wﲮx-EZwYE NH^*ؖO^UxoN$NG@v+W{mV+STbo՟^k֟)Qҧ+^O,/NnE( 2ʿ/q}:jI 0nIN{GLa{2zGZk3J R[N`mO8?ZF;׊H!bkg};Z-e'?) ı~S+UZaN[~=<'YkFQߏ%+أ-hKk|p}XqRNk9=^ݭlX%Jlҡu@': +l-蠪a.k[-.]21n}R-V;|i|[}BqmS :u݈%J RV?V꒱ވ)uan?ZmGiYbkƆUQ^ϫWƶ5b8ߎ2k.Q8sH)OOJ*{%hᬕ8NïïrO >UoAKԻZݺR}*߲v[ "lǟbd!m bAw ^L]CJ]/ +ݡ;GLcۏ=-JzUݧSv*mً*DSni[SJk)=S%Wtt,w68PʋjY~ˢ.?oQ%|r0oY%0җrq )] T.O婨T +</OUT:EXI}'d9`B(t@Ji, iQI l +sQ +sa. +Zk#^0R~lvwww0tVzc;Z㽗(LS)0V,#(L߿2Ea2_uH- +Stbgu=bkzEalT'yZYǿl n_rR]/NN6~ 7nD{* O(O ,TSy*O<՜\<2vmOU6SQ:Q><5I1P i?xKiqVl/pNkc g|;kߖNcz+{R mkw_'7^-?#`:c;TK}ǴeuԶlVw.J}eWkym?z8^+QuEU. +]27QB:F+@VޏAߵ֢@/uVgn[TKdn]pu*(xSXzU[it +$лGk>ĵvTbut +1A [vwޖuĕIqg]N;)Xq_OؾRK{kq~cSW:oWbK]}vv?ڮ}VZeΗkVy{mMqWz-YZ;oRV[mR.-eM|;k}^IrS6t/OS6})Kt\N@)?祲6/Tʋq Im׽~^9{}U];[J^c۳/O-~^ܘrMK~Wb'NU~|9N[?'IٓF\ v+g_-m\R bw+j7rzuӦZgŔJ ^))KrVkպ;Z{}Jٲkc|kl*- }w~^+>tJWm߯_\S)b)㋿>}g*)s֦T;-:e-޷V[)ۊ=}ko:η.im|'O(S6S<6;]J~S1\`NoCY]76\>ql]g:қ6lO_uj\}ZT~c;]J'lNP+sֿVw~˖Ni߯;rzy´ƆӛRbJWvR;G[9N8ePCQ||8qB[_X*lABudB4O(F94q] 40w(_`hLTa&\NNMts;Au wr +pmpԡ m1}[qmQi%DчqFƁ^:1K?cC+Sҿ'}jKJ!'(sDll&D.aYRc߱9$ mB[6Lyd-d2GBa`(tyqqOЧѩ1:lNC :|9.ONeD( e 0eXtqe PIhMZpNL6дL3 mlyBEhZSh0_q&"?PD '&݆u_y؅¾ɼce&`h;Mv`!:(p[ƄpK(+#lP#p2ipb~P4ڐv0 +ɮ}^]<(!  ZB)a{cR +B "|#lr(_pCS[.2>QyYqpeoL+3a#ЉЂ#i#!&@ -Np!6`C Ӏ#] e!'sGz+X//e/*y>}Pl/n1ߦv⍺y*OUHwE`A`QuPH5$߈B2ѐLzX"( C(g轸t,8؇e0&É@KС`VDٷŇlه}dqn4fLޝXrV:YJ\Ye. m&kYG8}Rit9öO4-oB2y|4y9^mHHL/d;PЖaC[F??0]x3 +Tº3}'vR޲.(Ƀ[%*`r9-L0~[.H\d80X[(˦,e5-ޖe me"9G̎r'uF):. :Q2u#qqDvd8̄aBBM~a 3Q\ +^e2 +ܨyItH[5Si'<ta-B?:)~pFGPp M`/xX,@}^g2eO8uL$Lؙ0Yzdbg2dL[HPp>EuQo LZP"LǐmYL&LQ H*L1ԁpEd:yD@LMaH*8S, x7:K fCC8LdYPz@3im=2 e߆:d(qO$B.0Meo;L&ę<; qƁg*NIk3m.u9B&-ReBQiH>Ƅˡc +,ȉ-8.O]L >iҰ"L&NcT@xXY`mxa/,tB}e8e#&l-@CjZTZ/{%klL|:a8,5SAS )dF^aZב7L||aerh@H;Sd-q'? +MuB>i߉k-OevBC ״'i(_~(_;4)W7煎BB<(_iec} 2Y`0wy.|;H+xBYq,0}&;N0gx޻i'ͅԱ:ׁſd/A6ˇ2tP9i+}?fSoLCl.':0{ 2-Ahh@@h|u!$vL]&LG_(S2g*U|]&NhVo +:B@e ԁ0qCSȆ];pe /,.o0MM/w t8N +N}_ ahtY;qL"}q8-s|[q,턉rƻrg1qFoqxPE;2}8m WV2-`_ݳ4ޟOt1%5`M b.B|a ߉#sR3U3Sz*yu: :)0Psa 5`BؗKD^Y)L{ 뾒!´.[\x![}D78m'(_%O +?Lr2Y30o} &>X_tٲЖ}$@T"u9D¡3R=Dgpu[[}Nz1/)ۭޞu1vZL/nZi):L +~>ΧpDr1o_ǷwJ)8gnyדּ:[+k|lukI}bi)=s~u[JKZ)_ϧ.-/cuNOZ^wJLs֋%֋)rR6z%.-ŘZkgVzߞM6vm;R:pD(X鬗zΧWZo:罗VoS.C8a~B8uր$hdAMa@UCPD F)z B!4i *P}©aX\׫4l$*Q::L#7d`L.'e:(ʃ|͂LEYB `,Hp)F ,$q1 @`]<S}0>.xM{qy Q;Ϧy<2ydE)HhʃHCKx@L$pX"I^&y'$LX"A-0::>:> "@@hLt>2*24(aH' (E)1JqpRQ!2 +N"CE"E/s]&B1=(χG,B(⫼EE| e<,Wx)%&P^(H0S Ӡ¥ICC^Ie xE"c8Mb@;i*zļL  <6n@p(_ *D$:S(cz ֆidUCEp$a*>UĈjlp&\ ,V:"dL. (^0&b͂ ` +NC82Cی6*]W}:0Qz9tZv&q050`tSnXp!Ȑ°Yp؈]H>ﳉ:>.?1"ȄID&DgS <"E$4P@9bpEaaL &ǂG +6T8Fƒ dcT$+JB$$OB8M%A,<#j !C1 P?Te0,gS|t>:Ec* +ƒ0x˝aΤU +`"`".& 'U0EQz x' h FS`ya+r9x*LB}.8>PTBEbaS\x%~<a1zU$eU(+)$e1@Dž[}\.^,SlMERTLڄ860(dqIEqPB~*ǀ%&$$r>clh!"B!0. $C0@jM*YM IEe>0v!AԜǦ"pܔ$aa$ +YQ *, +DBV< +LMNɊx9 QWT&%9.QQ q99xx ?<C&UU8 'EW,`+ "lT<\DDX  !QUT*BaHEu"DC61q5&H"AP@4DCbG u+B 08_ )a90P+ P`d#V4 m|c@E55bVaP bq%G5x tfp= fJP| CgEJ6i( dt`(uʤ10-7RYWC62_2*H,`82 RE#$:"$@0!U$ p?m_XȊ(4`y1FnH}RH@2( +m"10U48:sӛJCh X<#qXp& 6>'xQ8fyLT,<  3480!H  J(6 8C#ۨ*jqJT|܄a<|W 5<6?U P +\߸Gu &@**!!"L iy)`xŁ0অt`d}*HљtG*cШn:ZѠL `#PDxHȇN"4ֆ 6 bpQС:aԐA#525&ŀ-0A$3Qy x4x  +,|,!BtF݃͊ޛ t/ D`tApPG" ȏjs+6(f3'qG뼫2 6"y`@ad*ypagAa- +J 6&-d'TDBhf0x(ʤ VȬfP.zVD.cHRGq@A(AXHݎ˘S8"^ ?RX{`AV2F~ 2?eM~c +woe{/.n=p]hH^ɝpj4&mI\{w]sV1QwpZAoZJ )t(n#qO +-B9ž =E.ož)03N'4/( 608k@%=&03!69m}fЖe$C[Ik0 endstream endobj 20 0 obj <>stream +ZKOna9R:Uc>,,¡#ycGvT +Ǫ#2yaWMޥ6+P5mԘxͤO,8!Dt2hVVd4&ozբY*墻R8V#2:$eYt#P]7GG7Ϙ(#id$ +_"<~t *nUҵ؈_ FgbNX"7!HZFaǶ3,Y[ nIHb0) dƧ>W1H9zs:7ÌZry,^/b zZ#IULaf'&@=Єv skAĉ@UY4kV%a3j0#0[=J\=$,ejwа*)Irfm ^G MsoSw+ `A@alw4VG\ROe&%D9 +3-ֵ +Xl<`&0Sl%ߑ6&*wee!;xM.8 a"IXʹ8BshՅ! XS - x\iS3㖨#X|jI6Ϛ]0Ӆ̣tkm%1JWWx.f䛓y5HnVϣP[5H8PUػ+ӭ\D醉[Ontlϒpu˗*3]Sۼ $sǜ_޲ »7 Q +n~i2^ c6kBȒnxe8*g9W+$ +Y,6#Ab쾧8w?-d'0gq]R-U)axF.%.ON:s1sPOl`q]1_I ,8'[݃b\" T9~T`?TT""zVLkltJGdIڷ|m 0:kPrEa6& DtKfdf8Y.jD]dž,BgyE*S-( F0|rSs Se(u( yM<օMO/m/Cou\ez v@LVvf8Ŋ-jy|H ~SviD.1V?S%L+[(Pt]-g/GƎ9ZI?yЬ=VXlnK}IԼQ+Z܍P@I^&53֖ IQCjPmƯ nZjiݽvGbpZ.ݰ(M>,/_<iQFSkv  ^x63Y=Q|"_/|<ɇSieG$_m=8mudRG#f"䣖 gzQ QP, $DW{)dGA&AH4)pƩm TZ2#ȁȖNhWDT nDZ=lf"p>T7m< {9ɗb\5)5tTh%E5lSUC C&YR4mFUs{ݛg&D{W2Qa}/8-sbМ"ȩC!j;_E=}yz)-Ըgq}S[BԾkRPM1Hp.I8"LJ7ϔ`8[xәIUTj+ռ͔Qy +y)$nNxܝpvq=< v[ ޾TiC o$-$A/#WkVØumf"}bgF] +NkB D%,0+,ET^B:%Y-ڴn;aYR? ݳbp)e|"r5m\@6LڐeNO}!8S942]8bQa7q%'`o+$"91gfgdSӔ*6f~RET.9.oO!V  kK<Ȏт%fCe)qYDP6ݤBF,W D5ڿ\7dB󇡇',XM8we, XM+CpZK1qv1#+[Xt ͬ>]7m1NHBf{̐BL.@&[J.vCԄ@%+ڨ[Y'?z` oyՄQ: +/0lrB!&Bԡ@i'"e*Np?>Hq! 6wuq=:֤V|@*qM|8.(o-xwb5zEhcY8≌,@ʫq,&&Vb-A!>#@AMcpoږ#.ѻj}G]V)nUIkCwn*C,c=EIWsbjT88Q1Z`w{ob>A÷T4 @Ƅ)M +ڈ \h4An76.6L!=9r[W'PR*a 0AR`?~x01*TqؠDTj(@ ijT%zbi!Xď#2CH\3G(6 oYBd{n"8&iO'XR1!?Ee`hÕt?XZbAeTǞFkȷ/T:454A{ɏ+@2M; DMU3d "6iwyqҧE(E +NPV4Y;MaUEdFX~#}p̣:aoZ;aral+Q}+?{z +qaKL5<(9))<;tMLq0hPA=4 \N$uYy쐩?yLu(¥mӽhL7ƦP-uqQmu3:i Y'SQS.Z-W;K~۟×[OwD įq Aj:cb6ѵƵȡq QhՎLF@l @ݶW|@Z"+LlP%Kyjp5w 4 c +gxj\lQvHG8}rZk`lV8$ۿr1 #jK'<%.|xRj >i=kPT5P&Kuq+IZ{lЙr1#,h 1~A[Mi*CkQz8-K/fbagaEq!Ӣz-뀯aϡ qj>w~ӷ]~2w SY +h_Iz +v/S 'IB +@$ Dzqam r! 8Zh@Pdm(no~ 'TRM9 hp\/jU^( +1oTAu3 Kɛ\DYfN2تS՚jsyeE",3xϠ/a8'W:+D )oNiC;\ϻ+\2~Z~DHA*PWLNUi1V12r {2I!is*a.~2褪d:BX"HDqAߝę p~beKʥ=+w(z 0*5hF[8䒷XK-ʞLiWYlD$fj5ρD_s#{Ж23`D@>{P|ҡnQ';[SڵKh=pRB]YY%/~4xƒVbbh\# +SN׆6JPKy~ݙ#F{b:֤@"*bnG 9bQ6V wRܥV(ч!(Jut1C(15*7ntb㶺b9UOϊ4 <an[UVaa/]61"߶5 +c趆dM=ox?, ={{ *[ iU RQm2r.55GPy3' Pץ 7F9qYXp#7[Y%;!d5U/s!N'/]BPz孮XӇ.$Xv\\b|uvS,0GW}Ig DzGÍlX +3$@t6"ЦڬK9GxnCEl=iy<׍b)Ĕ }`ւIlYEHX2{Kw) NҦMFF# +݆+Ɯ}ZhvPECƸ%bw7/:Ck 4xQ@ҖZ +~>fZ7QK>a%z+ !n&d`iF,+5)ᕅXއQ$QU$ $8KH +0xrβ pl?;A8dlJrS8qpֽ]ޑ WdPy@}#eWU1-wcK .Í=Ԯ +Z~'M;E9o(?+S_2.%:[lY+cKboEv3Brt֢^°3(!\(z˕9@1{Pi{J7 CIB6kn~N<[Hw@xy`)/ϏXFIA!2p9iJ VTy0U~e2+F +,>ob4` ۏ2GCF"&m`; F0qAJš E5{ gyr +rz j9;Υ k@s +(4lN^'4Y>)I-z +RAed% 1/ `õ5Rs.8g`n<9%3\&fIAE|>2|l=1NUg^~wE3K UA&gv  -?"~>vbkVJ +ZʄU'&SܸN2G"[i<[ +A0QDGyo>e%ďT9>'&hGK`8=LXDOFy8Et8 mӣ?L!џ*3̥.O"4+E9@1-s xtXe@M}v6Y6CˋZBP+1S@.X :F@Ém98us=b;3ێ+q 1,q,Y@`VjZ sao +gcc}v(baC.>0@p0d:IQmU'A,rvʌPtX}bL̲<-a)/DOHffOzjk +X }V0 4VW;3"=:O*cyw`cʄJ"L hrJ"0'@AsHt18 +m((JssV )|O ЫzuEsBjLDQHL\R&ALFA~rI.=NDY= k@ "GDl` vocv%_+nEL(A)C5UG$̈́0 +<794ZJC~[͆\k=kaN缢c16=# G,r(U[P_9k2р}L7Kz~ +? 4o DŃ9'n:.,Loاe>tu$b=2"H?#Ѧʚ߻nUCO?q@q|p-oEMֈK{ rx/BQx` nr6= 9 *fq0+Ŧ|F<|ow鉹壱cZB(Ȇ7WYs$퇪>`]~>yqk^)ub1iKEuChPFBm'FxB'Q?#9,%IW1x?id+hha7yy j |=Aw03e|c/YJF#8.CM G7$ +Q4r30)K"j 09OHB{KR\/k=Y9 Nw{ܑZ.cD<ѥ!!/Wdf{'J$x채HPpdxsPpd'|hn#Zmlxެm#UGnM6D6 v"g{_Dmq%G0bcz;B^_-ہHn2 +TJXB/ה!&AJ[O JBF;CLęwLWm7>T7B7->3R20Xv9lፏ U +<Ո>mxO WF֕yxL.\)>-:%nl60~l%4[ +?`AЏrՇa1Sk'EY%*teTl^C?'́ي5 !.GqqVY]r~eٯ8}0e+Ղe'>zlI׊|3Ӹ"ev6X_0 >FB/ K.%&5Ө-Ș]tJɠp+ i=z1~t'Bá +vtISkJa,\넧m9.y"m)Fi5Pǀjcsyc̨!r j&yeŰeKҏbj$@H|8/>7/͚TO2}3hb_CBv]㺰YF:xQfPnfj/)EY8kc>H)`1n_vGa71X,nQrJ +!DJ(]Az[┲M3%:9wCb:?BSSuNNoSND\1&_fKkZ4 +Rp,|^[c\$C(ߍ "+բwjn(\[G9}N='^./ ⶣu~E bO*Ahtiz)ԟI}si)^2dٞ{E8PL/h?s>{2-bA&U [jy|^,I"WF2!uTOcU NHe MBFeߨr#tnc?N!>}6%#Y'JcWY=:V 5X*o/Wr؟چbkI ̔wP C Ċ٢J%\d9#"ȅC˲7pF `TXS=`dIHo +4E-)ipL[&pgo8(NuCM n<ϻ.3p\B0,@tEg5Z'X#-5Q1WJsrо*~m pc8KiswSkxI3t2d@K,s>叀0 +%| ŃpIHdL̮7D`h Z!k`mD$udriNoWf0U Ff* b_9xvgIGX@ڛ()\abUj Ic}:X5}>+ ví]uہk wcᎤ^B&H2;ylr3"YC5xOȞe A,3V7f/ 4"PQg={rt#5P'U9+&~[iM6b?ă=Dyw$秖+ $2k?2:Whn[Ⱥ֝|jx+شB؅/# W +νoS1f6J&+mې Yد" 0  oJ@t ,[O GDߧ=wC|I7$%|ܵtH + ՛KLWEV7ڰ;KE>w=C*YI|h,PKD޳&BcHc$ N~(WA(6FaHp$>[C2CI`^ u#ЕO=: (UV }9/R2a.ل'~dBe{D74 ?xc_ lBHp NZ]C`K"YX^Er^XБ\I L% + +ayHzTtϐ émŨ`!'Z%d["VOw] DZT1p85htsL˝ʡjVȘY{m5 q'CseQ0]?Eͩ;b[OԄMnRSt뒼;+۵ֲWUVU,aR-7k +YQk#C) +iX~ԫ\M+t*,{红 d#߃.]>w``执NR]Qh3#䴫Z.bkrD)`m'`%C@ErTz<&=9.1Ə;гݙH,D=YK_ +:~͈̂^2U 8怌q-ԓKG{?Z4&V`]KVT?:?݉'Ӥv>RY!\~"lX@Vӗ:Uj{Y1Ͻ`AC fť&g}Z0`h +==t? &068e~}PԹqZRݾNJu32ᖱ$ǯ5&vD[hƘrky1'ͻn;8nX:(Hc}zq>1 R8o^[H(_aIBC[myhߔ˷"Ӻ6U_^8nUvgN#~⏎r%?o'6;O|ßlfCZC`9yH@z;=j872>pܬSJ;zeSh"r4e 5Fmx(z ?~םc!GXn: ;6+D?GKu!(6 *yi#aϮySF_oCQ m]ARo̵ե/sz5fNE9잘mTe. de-`"HJ9C}| 9\=XWhqh_Y2 :-,{~fIAA ePF 2$|p0%:|n+,~B8.N֧^0AO;LxQv$V*JQge~ ݌b"0&*lwTM5󄩡Z阯-I1Jl)+譄/F +" kBճo3єR<h\A>k%UIsMک24ފoEB|bw@uy,_Ov7^\.OsI~"ц9EKtޯ`l @uzE 3l_< +t/%e5vQSmW $`V8̆MT/T+G=Fs\^nRT(6* n[S*F5 j|G8&h89N*|~xѠ%2/`"= R.>m5٣1gR-(5ʿz\OF0m-fF5K-\58Yl{dR( }5.tȳ +' ͞Dh ̻wcأޭ=^?q)-kϗ'lidS mi롾^8G3jśţ,,# -#}xF,FJ#`_1urk(ʹc:\aȦ[)5u +nT[ʂԂ׵R^ +- 5h%cbwUϦƛqfnpwiSfxŞU 4K ]">Nh4#(?L6yttA`]2-VXfN^WTj3,㇙YB d3Awƙ" 0Y؍|VW3H-aꜶiޏLFYy6Vn {ȘNYgCA-GŽ #%4A,.+wԮM6HC#`ަoP0&3rM7@%Kl+U-CL(epԤўE7+]+vFsS_C|mvoJZXdx,߄$`*ϗ4.N]5PJUjSTO='hAE+tRHst 6MN?:qlM: ZR1$og0r5J +e7>cJG<ُ<ҢH}Ugq0XÉI[RSa|y_/jC8DjtT +ߦ_CϿ/8x*T?EhS[B됟(H%?>J膻w6*ϕYu"`•"b[v Hԍ7~ٟ_sDm5y CIhIBxpd͒%Xly_#UL &vQP,,@ +JiiS&咡W^ DK"6R%$"?U(L";=WveFZǥ n+j=>ܤ/J8Mdێq7 wgdz-y7s"lz6 ^ӼsI f ̪Bo S{7]7Awbq~,};9-p +Qqm6bgz0Gr R6P|  wvrxmJV\60 Ȑsh2 "ұxl]heOQ|ZR + +twhc- QR|dJFiگ;+uww*::54kUFГGޚ@a  kcW$U*I*Q?o7Āg")Nc~&/<\VRF0>@8T뽽MI"0m}QǏ|uVIgY[bm&:#f,gq5GUZ6OO@ R{T>G]{АmcMR@$"Ke%Vg$YM@B5몢H'`ms1ZS矮-:T@Y`%h!6X0\oʋ~qyvt_ȃq+_K$fjΔ}/ŴĨެ{r-v?یa=)oJAjS.RO_JhƩ廉'Q㯇D:\цPqQX<@2FKFuI +F81VJu*M* Yg=+o0MI"ex^#/*`PH3@8YP$ +`SMTDaN)A/@QؓHˈi$a ^?;GǥCMB/i^$4qME+AǜXDȂ"G@Ze2}.SiA>/~5h"aqZ:Tæ%H@(n>f(4Fgv.&=X6۹p>0۹h$RaL:-([ +ŵz OpFš +; " +HkCuKj`qnoC>FQIB8EjvK}`VZMRWhlP5)`Mc(_Ea@(ĄaTzjYNb-x*!L8sd)[- t#{܂] LVV `8`8myPDMeIFIa$Rb)q10hxG%dbx\VXjhJD0A$,4hjbr< %ڇb &{P!I$`Zu=&?9*6$h@C8 , ZupsA KHi P iU"Qu P@H[D!H΀MX"E0BDdTbv|z(.ZNj㘆ӘŶx\J5$ + (x̫q5H*xdr.46 0= U2Ԣ$@KQ*bb kH,@)D#B4 +[AFiu:S[ΰ\V8AG0F +7CB)cD(Ϣ3TBo"J<) +P4(74i w hl*iuL ӑ:?"B?*7q46?QTLP$T=a"C$ 4P(<(@h.Bt5VGӘ0H6 l5JW):*NӰPu&1Y$"M | V1:jq +ipi"U"@C0$, ++q +9& {Y0Xª4a4|$QZE,O51,Y>!15'([‡zPVfZf<4N'RW嬆3 h)֤6&hR<z3 U=ZB{< 倄Y,J#q +" +z +>B) +}(L #Uo TZv\xC|# )+ꡐQ [gzr[/tdYG6rMPfѤ6NgԦf>#/ 4ֹ^$6b]C4LU24DXWK'?MUjqF^ V5BDJՋRXV[D>*LB)efI$l1ȣ:mKNX*x(b- ;„ёb= 6X'CqPɁ0S"H8MNyc%-;ץԤ'pt\~6 iQ#" ?Sh!CMS88 mv@9P2i|0,1Jˌ~fU- +f +\^F{Ջ_@>zXlރfC`%ݤ'qyH~5;.{_nxoJ33RrK1zmL;[VRM{ik~Z']dy,<2ݼvǵg˚sww}ŝ-Ι9{rbշNL˾rγc931SZϜg~g[̴rrBN +>p"a5効6H\C8xY;BXZHj!p(QK#@h%Mb2 SU8 &)J`MkJ?ia`MӋf\\5\h$Mh`i?YLEdP Ȅ8%TI >[@.prb#sŏJ(`X9ы7Vz ^2=$Wc=0pP H'G ͉U4Wa }!,XŠ 3"p]d/xv<k3 q MsѡFl?it =,M&a|HEC mY= 3:$qؙ"p]>/3q-O<&ǁ3,ԙGffVe Q'""JdP.> C(8&)ĄqQEHZK-0aT~En(DXxMFq+DZ #3vᄙI|9"aydY, l]fqj.&sb7W&jWmA3Ͼ8M1*tJeww߿ލeeS +vVZ>wb[c<]өxN9lZmO+3`7曭9PgsƖ^jA<ϊ7[ϊk6_Zejm,k_Wkks}V5YNZZ\ݶڝmZ.\w/@)g\% +vL:roeթXU^}ͧ)u=}e{yeuҊov)f,qO7Ϯ_SL17;ڛ vzuWs}ve}/`gcٺXcy,}Ϛsbϖv[R{ձ:i-igԽO8rSlcy1ޏ+R vߞ鼷w=/~Z˹8;͝ZVقLwsw7]nhozs:s;{/e[ vӊUz}?qLq]O^/ӥ(Řc0((IDg|nD.8D>2.8aQB SLIV* O!S9zjڠ/1}qȭ_xW63PlwEu/D8`L:P"UT{=/_^fd,ldQbS@9 +e@okPp( u:3 +\fTf|5 %k S`.UϽk*?Q*?g$:UF+1{xQH\H)B~ۅHv=PAefe (8 + CLh2K-j\ص7M8J0gj/:Cq`ǯFbׁ Y`/e44ETK\DB/)EWv Tz4=FZҳC)z@JO0j HJ"CIχP T ?'+z>@x:٩ ~=#8DG4 W<'6Q7 .I&i=fēL+z!ѮltU߹yRkolXIej0So9<4+<$mŸSSR7U C#'ڛ#D4cX3b%4nnȱ)51F $6M2ؓ/XrGIS/] 멱|УZЧ0 +P9d?>yp)˜&(־΢!rJ/ukOR+0}Tcl< Gr.}sꖕK4l H.+ڍ؂"s{Raal/EQ^ N6 G͸de+d_|݄ *?LyJV̽ɠE!A?%3^Q|A9Js75H{YdD)ipEƛiEykqDIqݛ4j/M^QL@B_h2܌ it@d3QiR7cL15!VT=xY&:r*iBhgE0 )V)xu` bm,qxDTMh(fi)TQڑ8[ԳaR4CL輍czc{#1F a;?ƩܟX7]/ѩ,/g +*b tu01N%"S` 'Qd ;U,J7/[g05BO(Q͊/T2CM\ј#J;Fm˼\Z A*G}E׵VdgY9HZQS,9#t=&Et@,4@|8'j6>{4swP!`*B +ӎ`Th'q́S۔y^ 3ztBk^ X25UR1 Y>eǶ"[!1CM[oSj&.2pFV`>a/5zj5Qv=&$zb1P8B1GD{ezϸ/lYH&ܧVo"BM zʛBDo̽W3Lp4[GT?)65ri`mG' 2O v7WIK X"UOad3 wϊ2wC|m@ٟՓXu$I^QeƍЅDlHpLy$3.jQBR9;XK/PqpUN໦OH*aVsp]d+3"_N"'/թ~W$r )ĞDD(Sbwg+)K(ңJPX`3̅ci;@}@0LAVt(9 =@a)% G'bZ#ك鞛Ql(黦zfQ ~M8P+GC`nNH2F"ؼl%0uZֿgYШ@q˹j`Ls&f#&I!,Dy8/RM}-D2\ǹXXFދPWp+F0\@UԬD_Η`7r;d<͵50r@lGGmِuk +DxK(.EFQW23D?:\+LT4IYs [ftɃq8 |K y8sŻC7瞃㶲(WB3F"8Hq^BA cdKXG@1UdDH4ƒUoIX{S=p5:fCN2!R^vv7UC=5yHs6v.^{QV?aJ*C`u(РٹEDyP|^qtݒk7.#܀'JnBA4|I] +idO )Tu, ƹ2sO +g +P##ZDxb!#DH?dV8:=7KpD]8|ԇ^9!kNZć1zLs._I5OSί)G!9_Dٌ,[7L^3 +5q׳5)_hcCB]ع ѱ&r1qĵE*K=SiRC7 -t r0,3u|m-kMOp qjWi <>"tF[UƆV01P”.n "v`C{Ld`LP˂s@fF7ZqQb#2F&jBR](+/VOv=N~\H\fAn~\TbA*ԌBh0R-R5xt} #6VZ\lI`ǯzXlH2V&$|B`TRG`LAk/auyuԃ_巼A(rٵA]ȱ0UA-(jXFa( ͣC>Gb՗|*I~2 GA|?2j.11Dac[UOW>-‡DHţFuQW/sڏt5^@#[F4c9!;n)%=Y"GOp%e9O%g"hwrF&N:R޺2Ӊ +ـqFMMB[+ +*E'e.aJey7 +&K4_%%%Σ34YeigԿqlgmwߢ "x*-xDxy.` 'IX633-F#anj؅+: I4&-Dl9 *=3K^~o 5P%\+ WMQ~&@Э'Q[^s3.OXZEIF2o~<,vU(!PVuZkrk Gh׳ț]b\rQ#竆Aq#0> z!\3mɖ,oI)Q(J_H$%cG Qfof{m!|P>n*wMѻ&GgBhq \^HZ{FDgI#5ǰu䬥R'qC)?wFA TN)ԩm2:kʒ};ek8 dxhX5ޭC1FXںFaJ`7ӂ-N_`|'ȈK3$}OH֌%3VMts]ݫ> +6"ZUPO 6Q|t;&}t/dc^gDG$gלϮ:ZX&z !҇Z/M'0Ѣyj3Pgi>ɼB'C"9{&S~+ 'R)2Ul +{:bF8QJ=Z )jB*4MK@P, lQHOC)IW@zs-">6lƷo`)GGB'b_1CW׈xGdQ".m%Kj+^ aQͩvu>B&xĩk<=wV4gqV%17\  +Y)xv.>gOSsx +Ĺa5vSx khi<D,h o<~X̴NQl31 rSuh Ⱦ]YZ[t{Y7)2혔|è#5X0&?IqSioxcᆔAar3_J~(GcG!RY^1kfecf}%u"Q c}說]pJ\Q2sR 8Bݽz A2eGs 3nҕTU^k7Ha?rP ܉ (/9a-Q/>K!3WP0u1*ANRp L4LUuN$XoJ1S5/ƃ$Ŭr~oh/czw+s/ֵTns2-?)= !Dx SFM`c)CrX~i Oz518s=`{:g ԣ Y( WS|8J{={xD1 v.]V[%pJ_ #ĹYFۛz 4^=[yYKſE(ϯLY_¿d^SFLUh{C!"o-#ylenw-22($bzOՅj~_n1k4y3$e,*uvh\U:%?`jF+eG!@tХv;ں_vZ$=56@#P 0;T{LeM`⣼R1F_U&n+4vՔT %Јv*58 W[xHefZz"[Bd 9`[0v +@T">c|>)$ + +[-M7}GzMe'͗D|Rxz5 飅@a7H5zҩ~rLÕz5ɆO5h[ +PDRoIՑ͛(]Ⱦz|hT_k0% N>cݝܹiAe8zx4t]/͑m}ObU2>lUR40JO6.Ic~?O/%KP$v̬P*aR,c)i5pwf<1A]wPQ +>Uq >qY"L3.r-ЇWْZ 0 .T!|e9A}oZ7sɾPsA@ l;ib -av(=}z躯0wɬ9U/B/iodHzۑz|!nW߈?)zo UoR}9Z& eU8rB#,`@I(z%[گ^w:37ڨW"AJ Hv^>WYQ㑛O[YY + [̘ ̊ *B4e䚰v2Cz}I(atfBbvA'΢M%hUz5@jլ\*7# Z'ISeHךQ'zkKg2m=|7災GwI)o%A?b`H´2 d ү7&QEI,$r&EJ +hK>ķPO +cY2()~LfPPy~5S ٧sll/|AKgrýHNJZms)2 !BbE[ xU]#~,]$6jEZ~ͼ}>3 g}w7$e8/9_mqau(᫓-{t #lsųƻw Ж!W9>\ݵ* L&3xMe5U\jf>v^ |LT}Pg,lgJ~8[=pkSʓ'Pi}m.x+7kZtejny,բE9Փ< +%D gr$eMfbZ,>p<շ'g͘喹uFh"," }:j5b(Y/зݸ;COs>o4[h JkGNj>m\cńn<Aָ`[ 2 endstream endobj 6 0 obj [5 0 R] endobj 21 0 obj <> endobj xref +0 22 +0000000000 65535 f +0000000016 00000 n +0000000144 00000 n +0000050674 00000 n +0000000000 00000 f +0000054021 00000 n +0000216933 00000 n +0000050725 00000 n +0000051086 00000 n +0000054320 00000 n +0000054207 00000 n +0000052310 00000 n +0000053460 00000 n +0000053508 00000 n +0000054091 00000 n +0000054122 00000 n +0000054393 00000 n +0000054589 00000 n +0000055803 00000 n +0000121391 00000 n +0000186979 00000 n +0000216956 00000 n +trailer <<90A246E9F2D74D3C99497A7810200A9C>]>> startxref 217163 %%EOF \ No newline at end of file diff --git a/src/res/jamulus-icon-2020.svg b/src/res/jamulus-icon-2020.svg new file mode 100644 index 0000000000..5e08a45cc2 --- /dev/null +++ b/src/res/jamulus-icon-2020.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + diff --git a/src/res/jamulus-icon-uninstall-2020.psd b/src/res/jamulus-icon-uninstall-2020.psd new file mode 100644 index 0000000000..428a67dac9 Binary files /dev/null and b/src/res/jamulus-icon-uninstall-2020.psd differ diff --git a/src/res/jamulus-server-icon-2020.ai b/src/res/jamulus-server-icon-2020.ai new file mode 100644 index 0000000000..f22cef9500 --- /dev/null +++ b/src/res/jamulus-server-icon-2020.ai @@ -0,0 +1,1360 @@ +%PDF-1.6 % +1 0 obj <>/OCGs[22 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream + + + + + application/pdf + + + jamulus-server-logo-2020 + + + Adobe Illustrator 25.0 (Macintosh) + 2020-11-07T20:57:34+01:00 + 2020-11-07T20:57:34+01:00 + 2020-11-07T20:57:34+01:00 + + + + 256 + 256 + JPEG + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYqk nm3zr5V8o6YdT8x6lDptpWiNKSXkb+WONQzyN7KpxV45qX/OaH5YW9wYrPT9VvkBoZ1ihjQjxUST B/vUYqy3yR/zkp+VPm67jsbbUZNM1CYhYbTU0FuzseirIrSQk9qc6ntir1HFXYq7FXYq7FUNqOp6 dplnLfaldQ2VlCOU1zcOsUaDxZ3IUYq8X85/85efljobSW+jC48x3iVA+qj0bbkD0M8oBPzRGGKv FfM3/OY/5oakzpo0FloUB/u2ji+szgf5Tz8oz/yKGKvN9Y/OL81NYYm/81am6tWsUVzJBGa/8Vwm NPwxVi13qF/ePzu7mW5eteUrs5qfdicVQ+Ko2x1rWdPINhf3FoV+yYJXjpvXbgR33xVl2i/np+b2 jFTZ+a9QdVpxS6l+uIANgAtyJRT2xV6d5Y/5zT882RSPzDpNlrEI+1LDys5z7kj1YvujGKvb/JH/ ADlH+VHmdkt575tBv3IUW+phYkJ/yZ1LQ0/1mU+2KvWo5I5Y1kjYPG4DI6kFWUioII6g4quxV2Kv mv8AOf8A5y0j0PUJ/L/kSOC+vbcmO71qb95bo42KW6KQJCp/bJ417N1xV8/335/fnJe3P1ibzZfI /IsFgZYEqf8AiuFUSntTFWZ+Q/8AnLn8yNDuoo/MTp5j0qoEqTIkV0qdzHNGq8j/AMZFaviMVfYf kjzv5d86+XbfXtBuBPZzijoaCSGQAcopVqeLrXcfSKgg4qn2KvD/AM4f+cpPLfkm9m0PRLddc8wQ 1S5Afja20g/Yldal3HdF6dCwO2KvAr3/AJy4/Oe4uGlhvrOzQ9IILSIoPkZvVf72xVmPkX/nNLXo LuK386aXDeWLEB77T1MNwg7sYmZo5PkCmKvqvy75j0TzHo1trWiXaX2mXi84LiOtCOhBBoysp2ZW FQdjiqY4qknnXzbpnlHyrqXmPUyfqmmwmVkH2pHJCxxr/lSOyqPnir86vP8A5/8AMfnrzHPruuzm WeU8YIFqIoIgfhiiX9lV+8nc1JxVjeKuxV9e/wDOJ352ajrRbyH5iuDcXlrCZdEvJCTJJDF9u3dj uxjX4kJ/ZqOwxV9L4q7FWndI0aSRgiICzuxoABuSScVfPf5rf85deXNAebS/JccevaqlVfUGJ+oR N/klSGnP+qQv+UemKvlPzn+YnnPznem78yarPfkMWigZuMERO37qFaRpt4D54qxzFXYqyXQ/y1/M LXlV9H8uajewt0njtpfR3/4tKhPxxVmVl/zi3+d90oc+Xxboehnu7RT1p9kSsw+kYqmZ/wCcQ/zl AJFrYkjsLtKn7xiqU6h/zjB+d9khc+XTcIBUm3urWQ99uAl5np2XFWE6/wCQvO3l7kdc0G/06Ndj NcW8qRH5SFeB+g4qkOKuxVnP5e/nR+YXkOZRoepM1gDWTSrqs1o3j+7J+An+aMqffFX11+U3/OTP krzy0Om35GheYnoq2Nw4MM7Hb/R5iFBJP7DUbw5dcVRP/OTnnu78o/lXePYSmHUtYlTTLWZTRoxM rPM47j9zGyhh0JBxV8B4q7FXYq9z/wCcRvPl1of5kJ5dklP6K8xo8LxE/At1EjSQyAeLcWj9+Q8M VfTX/OQX5g3Pkb8sr/VLF/T1W8dNP02T+SacMS4/ykiR3X3AxV+eju8jtJIxd3JZ3Y1JJ3JJOKrc Vdir6C/5w+/MS70jzs/k64lZtK19Xe3jJ+GK9hjLhxXp6kSFG8Tx8MVfaWKvAf8AnNHULi3/ACw0 +0iqI73VoVnYdCkcE0gU/Nwp+jFXxTirsVdirNvyT1G40/8ANzyhPbkiR9VtbdiDQ8LmQQSf8JIa 4q/R7FUp80+atA8q6Jca3r14llp1sKySv1JP2URR8Tu3ZV3OKviL86f+cjfM35gTS6Zpxk0nyoDR bFGpLcAdGunXr4+mPhHfkRXFXj2Kpx5X8o+ZvNWprpnl7TZ9SvW3MUC1CrWnKRzRI1r+05AxV9Ie Qf8AnCx2WK7886sUJAY6XptCR3pJcuCPmET5Nir33yn+UX5beU0T9B+X7SCdOl5Inr3Nf+M83OT6 A1MVZfirsVdirsVcQCCCKg7EHpTFWA+cPyI/KrzYHfU9At4bt/8Aj+sh9Vnr/MWh4hz/AK4bFXz1 +YX/ADhn5h05Zb3yVfjWLZfiGm3fGG7A8FkHGGQ/Ph8sVfPWraRquj6hNp2q2k1jf254zWtwjRyK eu6sAd8VQgJBqNiMVZb5i/NPzj5k8pad5Y1y8N/aaVOZ7K5mJa4AKFPTeQn41UfZLfEOlaUAVYji rsVdir0D8gbG5vfzk8pw2/Lml8s7FRU8IFaaT6OCGuKvoj/nNuG6byNoEy/7ypqZWX/Xe3kMfbwV sVfG+KuxV2Ks9/IeC5n/ADi8pJbV9QahE7Ur/dx1eTp/kK2Kv0XxV5f/AM5I+Rrvzh+VOo2tjGZt R0101OzhUVZ2twwkVQOrGGR+I7nbFX594q7FXYq9j/5xX8j3fmP81LHUvTJ0zy9/p93N+yJACtul f5mk+KngpxV9s+bvNuheUvL93r+uXAt9Ps15O3V2Y7LHGv7TudlGKvgL84fzh8wfmV5gN5eE2uj2 pZdK0pWqkKH9pv5pW/ab6BtirAMVe6fkn/zjDrnnRYNc8yGTSPLD0khAAF1dodwYgwPCMj9thv8A sg9cVfY/lTyd5Z8p6SmleXdOh06yTcpEPidunORzV5G/ymJOKpzirsVdirsVdirsVdirsVdirE/z D/K3yZ5/0w2XmGxWWVFItdQiol1AT3iloTSu/Fqqe4xV8TfnF+Qfmv8ALi5Ny4OpeW5H42+rxKQF LH4UuE39N/Dfiex7Yq8wxV2KuxV2KvsD/nEv8mNQ0OOTz35ggNve30Ho6LaSCjpbyUZ7hwd1MgAC d+Nf5hir2T82vIEHn3yFqflxmWK5nUS2E7dI7mI84if8kkcW/wAknFX50azo2qaLqt1pOq2z2mo2 UhiubeQUZXX+HcEbEbjFUFirsVfQX/OGOk6BdfmFqF/ezgaxp1kW0m0YfaEx9OeZW8Y0IWng58MV faWKpX5q8x6f5a8ualr+oNSz0y3kuZRWhbgtQi/5Tmir7nFX5l6vqUuqate6nLHHFLfTy3MkUKhI 1aZy5VFGyqC2wxVvR9E1nWr9NP0exuNRv5ATHa2kTzSkKKsQiBmoBucVereSP+cVvzU8xXcZ1Ky/ w9ptR6t3fEerx78LdT6hb/W4j3xV9i+QvIPlP8t/Kn6M0sLBaQK1xqGoTlRJK6rV5p32GwHyUYq+ L/8AnIL86Lr8xfMpt7F2j8raY7JpkG49Zh8LXUi/zP8AsA/ZX3LYq8nxV9R/844/841rdJbecvPF pW2YCXSNEmX+8B3W4uUP7PdIz16nbYqvrEAAUGwGKuxV5r+Z35uw+WpW0nSUS51niDLI+8VuG6cg DVnpvx6DqfDMTUang2HN6jsP2eOpHiZPTi+2X7PN4hqnnnzhqkrSXusXUnLcxrK0cY3rtHHxQfQM 10s0zzL3eDsvTYhUMcflZ+Z3VtG/MPzno8yyWerXBRaVgmczREeHCTkPu3wwzzjyLDU9j6XMKlCP vAo/MPefy1/NGy83QtaXKLaa1CvKSBSeEijYvFXf5qdx75ssGoE9jzfPu2uwpaM8UfViPXqPI/rZ 1mS6B2KuxV2KuxV2KqF/p9jqNlPY39vHdWdyhjuLeZQ8bowoVZWqCDir4l/5yH/5x4n8izv5i8uo 8/lGdwJIyS8ljI5oEcndomOyOf8AVbehZV4ZirLvys8u+UvMnnGz0PzPqM+lWl+RDbXsAQgXDECN JOYoqv8AZDdmpXapCr7P8i/84yflX5Ru4r9LOXWNShIaK61N1mCMP2khVY4ag7qShI7HFXq+KuxV gX5m/kl5D/MSJX1u1aDU4l4w6taFY7lVHRGYqyyL7Opp2pirw68/5wcuPrDfUvNyfVzugmsjzA8C Vmofnt8sVfNvmby5qvlrzBf6Dq0Xo6hp0zQTp2qvRlPdXWjKe4NcVVfKHmrVvKnmXT/MOlSenfad MsqD9l16PG9P2ZEJVvY4q/R/yT5v0nzh5W0/zHpT8rTUIhIEJBaNxtJE9P2o3BU4q+f/APnND8wf q2l6b5Gs5P3t8Rf6qFPSCNiII2/15AX/ANgMVfI+Kvrj/nC/8vvq2l6l55vI/wB7fE2GlFh0gjYG eRf9eQBP9gcVfTmKvmj/AJy//Np9O0+P8v8ASJ+N3qCCbXJEO6WxP7u3qOhlI5MP5aDo2KvkLFX0 B/zi5+RyebNTHm7zBbh/Lmmy0s7aQVW8uk33B+1FF+12Zvh3owxV9qAACg2AxV2Kpf5h1VdI0LUN UYchZW8kwX+ZkUlV+k7ZGcuGJLk6PB42aOP+dIB8f3d1cXd1NdXLmW4ndpJpG6s7mrE/MnNETZt9 mx44wiIxFAbBRwM3YqmGgazdaJrVnqtqaTWkqyAdOQH2kPsy1U5KEjEghxtXpo58UscuUh+Pk+w7 eeOeCOeM1jlVXQ/5LCozeg2+MTiYkg8wvwsXYq7FXYq7FXYqoahYWWo2NxYX0CXNndRtDcW8gDI8 bjiysD1BBxV8A/n5+Ttz+XHmspbB5PLepFpdIuGqxUA/Hbu388dRv3Wh8cVeYgkGo2IxV95f84zf mw3nnyULDUpvU8x6EEt71nPxzwkEQXHuSF4v/lCv7QxV7DirsVdirsVfNv8Azl9+U36U0hPP2lQ1 v9LQQ6zGg3ktK/BNt+1Cxo3+QfBcVfHuKve/+cV/zltvKGtXHlrX7sW/lzVOU0NxKaR212i/aPgs yLxP+UF98VeV/mX51u/OvnjVvMlxyC3sx+qxMa+nbp8EMf8AsY1FfepxVJdF0q41fV7PS7Yqs97M kCPIQqKXYDm7HZVXqxPQYq+4l/PL8jvy48u6f5btNbXUV0q3S3jh0xDdF/TFGYyp+45O1WNZOpxV gut/85waUjMuh+Vp7hf2Zr25SAj5xxJP/wATxV8v+avMmpeZvMeo6/qb877Up3nmpWi8j8KLX9lF oqjwGKph+XPkfUvPHnLTfLdhVWvJP9InpUQ26fFNKf8AVQGg7mg74q/R/wAvaBpfl7Q7HRNKhEGn 6fCsFvGP5UFKse7Md2Pc74qmGKuxVj/5gWU175J1q2hBaVrSVkUbklF58R8+NMqzC4F2PZGUQ1WO R5cQ/U+Sc0j7C7FXYquRHd1RAWdiAqjcknYAYoJoWX2VpNq9ppVlaPu9vBFE29d0QKd+/TN9EUAH xTUZBPJKQ6yJ+1FZJpdirsVdirsVdirsVYn+aX5eaZ5/8mX3l69CpLKvq6fdEVMF0gPpSjvSp4tT qpIxV+cer6TqGj6rd6VqMJt7+xme3uoW6rJGxVht13GKst/Jf8w5vIX5g6brfJhp7N9W1WMft2kx Ak27lNpF91GKv0ZjkjljWSNg8bgMjqQVZSKggjqDiq7FXYq7FVO5tre6t5ba5jWa3nRo5onAZXRx xZWB6gg0OKvzx/PL8r7j8vPPVzpaKzaPd1utGnO/K3dj+7Y/zxH4G+hu+KvPcVdirsVdirsVdir7 K/5w6/Lb9EeWLnzpfRcb/XKw6fyHxJZRNuw8PWlWvyVT3xV9FYq7FXYq7FXyt+aPlix8ueb7mxsZ Ve1kAuI4RWsIlJPpN/q9v8mmabUYxCdB9a7C109TphOY9Q2vvrr+OtsSyh3DsVeqfk1+W13qWp2/ mLU4THpVowltEkFDcSrujAH9hDvXudvHMzS4CTxHk8l7SdtRxYzgxm8ktj/RH6z+Oj6CzaPnTsVd irsVdirsVdirsVdir48/5zM/L1dO8w2HnWyi422sD6pqRXoLuFaxsfeSFaf7A4q+bsVffH/OLnnd vM/5UWMFw5e/0FjplwWO5SJQ0DfL0WVfmpxV65irsVdirsVfN/8AzmH5n/LubyzF5dvJvrHnC3lS 402O3AdrYNQSfWGqOCSx/s9SeLUoK4q+O8VdirIfJ/5fec/ON39V8t6Rcai4YLJLGtIYyf8Afkzc Y0/2TYq978of84T6xOiT+bddisgaFrLT09eSh7NNJwRWHsjD3xV65oP/ADit+TGkqPV0mXVZlpSa /uJXO3ikRiiP/AYqznTPy3/L3S6HTvLOl2rj/dkVnAr/AEuE5H78VZEiJGgRFCIooqqKADwAGKt4 q7FXYqo3t5b2VnPeXLcLe2jaWZz2RAWY/cMBNC2eLGZyEY85Gg+QPMWtXGua5fatcbSXkrSca14q dkQeyqAozRznxSJfZtHpo4MUcceURX6z8W/LeiXGua7Y6TBtJeSrHy/lXq7/AOxUE4whxSARrdSM GGWQ8oj+z7X0ho35P+QdLmWdNP8ArUyUKvdu0oBH+QaR/wDC5tY6WA6Pmep9otZlFGfCP6O328/t ZmqqqhVACgUAGwAGZDoybbxV2KuxV2KuxV2KuxV2KuxVgP57+Tx5s/KrX9MROd3Dbm9sfH17X98o X3cKU/2WKvzpxV9E/wDOFnmc2XnnVvL8j0h1eyE0SnvPZvUAD/jFLIfoxV9l4q7FXYq+ff8AnIb/ AJySi8pfWPKvlORZ/MxXhe34o0djX9kAgh5qdjsnep2xV8Z3d3dXl1Ld3cz3F1O5knnlYu7uxqzM zVJJPc4qr6Po2q61qdvpek2kt7qF03C3tYVLu7ewHgNyew3OKvqz8qP+cPdPtEh1X8wpBeXezrod u5ECHrSeZaGQ+KoQvuwxV9I6ZpemaVYxWGmWkNjYwDjDa26LFEg8FRAAMVRWKuxV2KuxV2KuxV2K vMvz58y/o7yvHpMLUudWfi9DuIIiGf8A4JuK/KuYesyVGu96n2U0Xiag5D9OP7zy/S+ds1b6S9l/ 5x68s+pdX3mOdPggH1SzJ/nYBpWH+qvEf7I5n6LHuZPE+1+tqMcA6+o+7p+PJ7jmxeDdiqE1LVtL 0u3NzqN3FZwD/dkzqgJ8BU7n2GRlIDm3YNPkyy4YRMj5POPMX5/+WrLlFo1vJqkw2EprDBX5sOZ/ 4H6cxZ62I5bvTaP2Szz3ykYx8z+r7Xl/mP8AN3zvrfKNr36jat/x72VYhT3epkPv8VMw56mcutPV aP2e0mDfh45d8t/s5fYg/Kn5h+ZfL+qRXUd7NcWvIfWrOWRnjkSvxCjEgNTow6ZHHnlE3bfr+x8G oxmJiBLoQNwfx0fVkciyRrIu6uAyn2Irm6fIyKNLsUOxV2KuxVxAIIIqDsQelMVfmT590EeX/O2v aIq8Y9Ov7m2iHjHHKwjP0pQ4qnv5Fa2dG/N/ypehuIfUI7R2qAAl5W2YknsBNvir9GcVdirxP/nJ L8818h6MNC0OYHzZqcZKOKH6nbtUeuwP7bbiMfNj0oVXw3PPNcTSTzyNLPKxeWVyWdnY1ZmY7kk7 knFU/wDIPkHzH568xwaDoMHq3EvxTztURQRAgNLKwB4qtfmTsKk4q+8vyl/Jjyr+W+kiHT4xdaxO gGoaxKoE0p2JVOvpxVGyA/Mk74qz/FXYq7FXYq7FXYq7FXYq7FXy1+a/mb9P+dLyWN+VnZn6naU6 FIieTD/WcsflTNNqcnFMvrHYGi/L6WIP1S9R+P7KYhHHJLIscal5HIVFG5JJoAModySALL648m6B D5a8q2OmEqrW0XK6kqKGVvjlavhyJp7ZvMUOCID472lqzqdRLJ/OO3u5BJfMf5w+SNF5xi7/AEjd Lt6FkBJv7yVEY36/FX2yueqhHzc7R+zurz78PBHvlt9nP7Hl3mP8/PNF/wA4tIhj0qA7CQUmnp/r OOA+hajxzDnrJHls9Xo/ZPT498hOQ/Ifr+15zqGp6jqNwbnULqW7uG6yzO0jfKrE5iSkTzelw4IY 48MAIjy2Q2BtdirI/wAv/KkvmjzPa6aARag+teyD9mBPtb9uWyj3OW4cfHKnWdr68aXBLJ/Fyj7/ AMbvrJVVVCqAFAoANgAM3b5ATbeKuxV2KuxV2Kvz/wD+cn9PSy/O/wAxBAAlwbW4AHjJaxF67Dq/ I4q840W+On6zYX4PE2lxFOG329Nw9dt+3bFX6j4qxj8yfPuleRPJ1/5j1H4ltl4WttWjT3D7RRL/ AKzdT2Wp7Yq/ObzN5k1fzLr19rusTm41HUJTNPIelT0VR+yqrRVXsBTFV/lPyrrXmvzDZaBosH1j Ub6T04k6KoAqzud+KIoLMfDFX6E/lR+Vmgflz5Zj0nTVWW9lCvqmpFeMlzMB9o7nii1oiVoB7kkq s0xV2KuxV2KuxV2KuxV2KuxVi/5leZv8O+T76+R+F3Kv1ayPQ+tKCAR7oKv9GU58nDAl2vYui/M6 mMD9I3l7h+vl8XyjmlfXUbo2qS6VqltqUMUcs1o4liSYFk5r9liAVrxb4h75KMuE20anAMuMwJIE ttkd5g86+aPMDk6tqMs8ZNRbg8IRTpSJOKfTSuSnllLmXH0nZmn04/dwAPf1+Z3STK3Pdiqf+XvI nmzzAVOl6dLLCf8Aj5cenCPH94/FTTwG+WwwylyDr9Z2rp9P/eTAPdzPyCW6zpjaXqlxpzzRzy2r mKWSEkx812cKSFJ4natMhKNGnJ02fxcYmAQJb786QWRb30n+Svk79BeWRqFzHx1HVgsz16pBSsSe 2x5H579M22kxcMb6l8x9pe0fHz8ET6Me3x6n9D0PMp5t2KuxV2KuxV2KvhX/AJy8AH5y3RAoTY2h PueBGKvFcVfqpir4f/5yx/M5vM3ng+WrGUnRvLbNA4B+GS+6Tv8A88/7oV6EN44q8LxV9yf84ufk /H5P8qJ5j1SADzJrsSyfGPitrNqNHDv0Z9nf6Afs4q9wxV2KuxV2KuxV2KuxV2KuxV2KvAv+chNf muNdstEWot7KL13/AMqWb/mlFFPmc1mtncgO59C9kNII4ZZesjXwH7Xk2YT2DsVRemaRqmqXAttN tJryc/7rhRnI9zxBoPc5KMSeTTn1GPFHinIRHmaekeXfyA8y3vGXWbiPS4TuYlpPP9ykIP8Agvoz KhopHns8zrPa3BDbEDkPyH6/seoeXfyj8kaHxkSy+vXS7/WL2kpr4hKCMfQtczIaaEelvKaz2h1e fYy4Y90dvt5/aqfmf5vXyv5UmmgYJqF0Pq2nqNiHYbuB/wAVrv8AOg74dRl4I+bHsPs781qAD9Ed 5fq+P63yySSSSak7knNM+sMw/KzyefM3mqGGZOWnWdLm+JHwlFPwxn/jI23yrl+nxccvJ03bvaP5 XTkj65bR/X8Pvp9SgACg2A6DNy+TuxV2KuxV2KuxV2KvgT/nKe+F1+d+vKpqtslpADUncWkTNQHp RmIxV5dp9o15f21oteVxKkQp1q7BdvvxV+jv5t+dk8k/l5rXmEEC6toDHYqf2rqYiOHbuA7BiPAH FX5uzTSzzPNM7STSsXkkYkszMaliT1JOKvUf+cb/AMtl88fmNareRCXRNHAv9TVhVHCN+5hNdj6k lKjuobFX6AYq7FXYq7FXYq7FXYq7FXYq7FXYq8n/ADh/K7VfMF5FreiKJrxYxDdWbMELqhPF0LUW oBoQT8swtVpzI2Hr/Z3t3Hp4nFl2jdg/oLz3RvyU8+ajNxmtF06EGjTXTqPuRObn7qe+YsdJM9Ke j1PtNpMY2lxnuiP10Hpfl38g/K1hxl1aaTVZxuUP7mCv+op5n6X+jMuGjiOe7y+s9rNRk2xgYx8z +r7Ho1hpunadbi2sLWK0t16RQosa/coGZcYgcnmsuaeSXFMmR8zaJwtTsVfL35s+cf8AEvmmX6vJ z0zT629lTo1D+8kH+uw29gM0+py8cvIPq3s/2d+W04seue8v0D4ffbCsx3ePqP8AKjyd/hnytEk6 cdSvqXF9XqpI+CP/AGC/jXNxpsXBHzL5R2/2j+a1BI+iO0f0n4/dTM8yHSOxV2KuxV2KuxV2KvzU /NLXBrv5j+ZdWRuUV1qNy0DDvCshSL/kmoxVEfk7o7ax+anlWwA5K+p20sopWscEgmk/4SM4q99/ 5zb82ssXl7yjE1BIX1W8X2WsFv8ARvL+GKvlHFX3T/ziX5KTQPyti1aWPjf+Y5WvJWI+IQITHbr8 uIaQf6+Kva8VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirAfzl84/oDyu9pbPx1LVeUEFDRk ip+9k+gHiPc+2Y2qy8Ma6l6H2b7O/MajikPRj3Pv6D9PwfM+ah9Regfkx5OGv+Z1vLlOWm6VxnmB GzymvpJ945H2FO+ZWlxcUr6B532k7R/L6fhifXk2Hu6n9HxfS2bZ8vdirsVdirsVdirsVYl+bXm1 fKX5ca/r3LhPbWjpaH/l5m/cwf8AJR1r7Yq/NjFXvX/OG/ll9R/M641plrBoVlI6v4T3X7hB9MZl +7FWNf8AOTvmB9Z/ObXPi5Qab6WnwCtaCCMeoP8Akcz4q810rTbjU9Us9NthW5vp47aEeLzOEX8W xV+n2kaZa6VpVlpdovG1sIIra3XpSOFAiD7lxVF4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXkO kfnqlnrF3pPma3olvcSwrqNsCQAkhUepF17dV/4HMGOso1J7LUeyxnijkwHnEHhPu6H9fzepWGs6 TqFh+kLK7iuLLiWNxG4KAAVPI/s07g9MzIzBFh5TLpsmOfBOJEu58ufmL5tk80eaLnUFJ+pR/uLB DtSBCeJp4uSWPzzT58nHK31bsfs8aXTiH8R3l7/2cmNxRSSypFEpeWRgqIoqSzGgAHvlLs5SAFnk H1f+XnlOPyv5XtdOoPrbj175x3ncDkK+C0Cj2GbrBj4I0+R9sdoHVaiU/wCHlH3D9fNkmXOrdirs VdirsVdirsVfKn/Oafn9WOleRbOWpUjUtVCnoaFLeNqexdyP9U4q+VsVfcf/ADiJ5LOhflj+mLiP he+Y5zdVIowtoqxQA/P43Hs2Kvjfzxqh1bzpr+qE1N9qN3cb+Es7uOw8cVZR/wA496UuqfnP5Utm HIR3n1qhod7OJ7kHenQxYq/RHFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXx/5ujaPzXrUbfa S/uVb5iZhmiy/Ufe+zdnm9PjP9CP3BB2eqalZJPHZ3UtvHdRmG5SN2VZI2FCrgGjDfvgEiOTfkwQ mQZRBMTYvofJC5Fteo/kT5O/Smuvrt0nKy0oj0AejXRFVp/xjHxfPjmZo8Vy4jyDyntV2j4WHwYn 1ZOf9X9vL5voXNo+cOxV2KuxV2KuxV2KpL5z826T5R8sah5i1V+Flp8RkZagNI/RIkr+1I5Cr7nF X5u+bvNGp+avM2peYdTble6lO08oFeKg7JGtd+MaAIvsMVRv5c+Sr7zt500vy1aVU30wFxMBX0rd Pjml8PhjBIr1NB3xV+k+nWFnp2n22n2UQhs7OJLe2hX7KRRKERR7BQBir8tGZmYsxLMxqzHcknFW a/krrq6H+a/lbUXbhEuoRQzOeix3J+ryMfYLKTir9HsVdirsVdirsVdirsVdirsVdirsVdirsVdi rsVfI3ntGTztr4bYnUbpvoaZiPwOaPN9Z977H2Ub0uL/AIXH7gkWVue7FU98r+dvMnlmcy6TdtHG xrLbP8cL/wCsh2r7ih98sx5ZQ5Ov13ZmDVCskbPf1Hx/Ae3+Tvzx8u6xwtdYA0i/b4QztW2c+0hp w+T7e5zY4tXGWx2LwvaPsvmw3LF+8h/svl1+HyelAgio3B6HMt5d2KuxV2KuxVxIAqdgMVfDv/OT v51r5214eXdEn5+V9HlP71DVbu6FVaao2MabrH47t3FFXhuKvtH/AJxH/Kh/L3luTzlqsPDVtdjC 2COPiisKhlPsZ2Ab/VC++KvoPFX5WyRvHI0bji6EqynqCDQjFXI7o6ujFXUgqwNCCNwQRir9K/yx 83R+b/IOh+YlYNJfWqG6A6LcR/u51+iVGGKsnxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Kvkz8x kZPPeuhhQm8lb6Gao/A5pM/1n3vr/YxvSYv6gY5lTs3YqnPlryjr/mS8+q6TatMRT1Zj8MUYPd3O w+XU9ssx45TNBwtb2hh00eLJKvLqfcHvXkb8mdA8v+neajx1PVVowd1/cRN1/dxnqR/M30UzZYdL GO53L592p7SZtRcYejH9p95/QPteh5lPNuxV2KuxV2KvnT/nLX84L/y/pqeSNHEsF9rEHq6jf0ZA toxKGKJtqtIVIcjou3VtlXxtirPvyM8t+U/MX5l6Tpnmm8S10x35rDJULdTqR6VqW6L6p8ev2RuR ir9FURERURQqKAFUCgAGwAAxVvFX5lfmBpbaT568xaYV4/U9Su4FAFBxSd1Uj2IG2KpBir6f/wCc M/zJW3vb7yFfy0jvC19o5Y9JlWlxCP8AXRQ4H+S3jir61xV2KuxV2KuxV2KuxV2KuxV2KuxV2Kux V2KvlL80VZfzA1sMKH6wT9BUEfhml1H1l9c7CN6PH/VY1BBNPMkMEbSzSELHEgLMzHoFUbk5UBbt JSERZNAPXPI/5DXd16d95oY21uaMunRn96w/4tcbIPYb/wCrmbh0ZO8nju1PauMbhp/Uf5x5fDv+ 73vbNM0vTtLs47LTraO1tYhRIYlCr8z4k9ydzmwjEAUHhc+eeWRnMmUj1KKyTU7FXYq7FXYq7FWJ /mX+Wflv8wvLkmja1HRlq9jfIB61tNSgeMnsf2l6MPoIVfAv5mfln5k/L3zJJo2sx8kar2F+gPo3 MNaB0J6EftL1U/QSqxJWZWDKSrKaqw2IIxV9tf8AOMP55P5y0r/C3mCfn5m0yLlBcyH4ry2Wg5kn rLHsH7sPi/mxV7zir4I/5yo8vNo/5zatKF4watFBqEA8RJGIpD9M0L4q8ixVGaPq2oaPqtpqumzN b39jMlxazr1WSNgyn33HTFX6J/lF+Zul/mJ5OttbtSsV8gEOq2INWguVHxCnXg32kPce9RirNcVd irsVdirsVdirsVdirsVdirsVdirsVeD+Zvyv8y+afzI1iWGP6npnqx89QmB4U9FP7tdjIflt4kZr cmnlPIe59A0PbmDSaHGCeKdH0j3nn3fjZ6j5N/Lry35VhBsYfVviKS6hNRpm8Qp6Ivsv01zMxYIw 5c3lO0u2M+rPrNQ/mjl+34sny51TsVdirsVdirsVdirsVdirFvzI/Lry95/8sz6FrMQowL2d4BWW 2npRZYzt07itGGxxV+eHnXyfrPk7zPf+XdYjCXtjJwLLXhIh3SWMmlUdSGH474qh/K/mTVfLPmCw 17SZfS1DTplngbsSNirDurqSrDuDir9I/I3m/TfOHlPTPMmmn/RdRhEnpkgtHIDxlian7UcilT8s VeBf85seUHuNF0LzZBHU2Mr2F6w6+nOPUhJ9ldGHzbFXyLirsVZr+U35p65+XHmiPWNO/f2koEWp 6czFUuIa9K78XXqjdj4gkFV+gfkzzp5d85aBb65oN0t1ZTgcgCPUikoC0Uq/sOtdx/DFU8xV2Kux V2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Kvn3/nL/APLSPW/KEfnGyi/3 K6BRbwqPiksZG3r4+i7cx4KXxV8W4q+rf+cKfPDsmteSrmSojA1PTVJ6AkRXCCvuY2AH+UcVfQv5 ieT7Xzj5J1jy3cUA1G3ZIZGFRHOvxwyf7CVVbFX5q39jd6ffXNheRmG7tJXguIW+0kkbFHU+4YUx VD4q7FWaflf+a/mn8utcGo6NL6lrMVGoaZKT6Fyi12YD7LrU8XG49xUFV9zfld+cfk38xdNE2kXA h1ONQ17pE5AuIT0JA/3Ylejrt40O2Ks6xV2KuxV2KuxV2KuxV2KuxV2KuxVLtf8AMWheXtNl1PW7 +HTrCEVe4uHCLWleIruzHso3PbFVvlnzNonmbQ7XXNDulvNMvVLW86hlrxYqwKuFZSrAggjFUzxV 2KuxV2KuxV2KuxV2KuxV2KoXVdMs9V0u80y9T1LO+hktrmP+aOVSjj7jir8xNf0mXR9d1HSJXEku nXU1pI60Ks0EjRkgiooSuKs1/wCcffMTaB+cPlm6LUiuboWEw6Areg245eytIrfRir9EMVfG3/OY H5XtpHmSLzvp0JGm62wi1IKPhjvVXZtugmRa/wCsGPfFXzpirsVdiqK0vVNS0q/g1DTLqWyvrZg8 FzA5jkRh3VlIIxV9Kfln/wA5lX1qkOnefrM3sQov6as1VZgPGaD4Uf3ZOP8Aqk4q+mPKXn3yd5vs /rflvVrfUowKyJE9JY67fvIW4yR/7JRiqfYq7FXYq7FXYq7FXYqluv8AmXy/5esG1DXdRt9Ns16z XMixqSOy8iOTeAG+Kvnf8yf+cy9KtPVsPIdl+kJxVf0veq0duPeKD4ZH+b8fkcVfMPmzzt5q83ak dR8x6nPqNya8PVb4Iwd+MUYoka+ygYq9u/5xE/Nf9CeYX8j6pNTS9bk56YznaK+pTgCe06gL/rha dTir7KxV2KuxV2KuxV2KuxV2KuxV2KsT/NTz3a+RvImq+YpiDNbxFLGI/wC7LqX4IUp4czVv8kE4 q/NqaaWeaSaZzJLKxeR23LMxqST7nFURpV/Jp+qWeoR19SznjnSnXlE4cfqxV+pOKpP5w8qaR5s8 tah5e1eP1LDUIjHJT7SN9pJEr0aNwGX3GKvzl8/+Rta8j+ar3y5q6UuLVqxTgEJPC393NHX9lx9x qDuMVY7irsVdirsVV7G/vrC6ju7G4ltLuE8oriB2jkQ+KupDD6MVereVv+cp/wA4dBRIpdSj1q2T YRanEJmp7zIYpm/2TnFXqOif85wQFVTXfKzKwpznsbkMD40ilRaf8jMVZhY/85lflLcKPXg1Wzb9 oS28bAH5xTSYqmaf85afkq0XM6ldI2/7prOflt8lK7/PFUDd/wDOYv5QwJyiXU7o0rxhtUB+X72W MYqxPWv+c39FRWXRPK9zcMfsSXtxHAB7lIlnr8uQxV5n5n/5y5/NvWA0dhNa6FbtsBZQhpae8k5m NfdQuKvI9Z13W9bvWvtZv7jUbxtmuLqV5pKeHJyxp7YqgMVdiq+GaWGVJoXaOWNg8ciEqyspqGUj cEHFX33/AM4/fnPZ/mJ5YSC9lVPNWmIE1S22UyqKKt1GO6v+1T7LbdCtVXq2KuxV2KuxV2KuxV2K uxVbJJHFG0kjBI0BZ3YgKFAqSSegGKvhL/nJT86F8/8AmRNM0eUt5W0dmFo24FzOfhe4INPhp8Md e1T+1TFXjWKuxV+qmKuxV5h+fH5MWH5k+W6QBLfzLpys+k3jCgau5t5T/vuQjr+ydx3BVfA2saPq mjapc6VqttJZ6jZyGK5tpRR0cdj+sEbEbjFUHirsVdirsVdirsVdirsVdirsVdirsVdirsVdiqbe V/NOveVtcttb0K7ey1K0blFMm4IOzI6nZlYbMp2OKvt78mf+cjvK3n6CDTNRePSPNdArWEjUiuW/ mtXb7Vf99n4h/lAVxV7BirsVdirsVdirsVQOua7o2haZPqms3kVhp9uOU1zOwRF8BU9SewG57Yq+ NPz7/wCcmL3zms/lvyqZLHysSUublqpPfAfzDrHCf5OrftfyhV4JirsVT3yJpL6x520DSlFTfaja 25p2EkyqT26A1xV+m+KuxV2KvIvz1/5x/wBH/MWxfUtPEdh5ugQC3vmqI7hU6Q3HEHb+V6cl9xti r4a8w+Xdb8u6vcaPrdnJY6latxmt5RQjuCCNmUjcMNiOmKpbirsVdirsVdirsVdirsVdirsVdirs VdirsVdirYJUgg0I3BHUHFXsv5e/85U/mV5Ujis9QkXzFpUYCrBfM31hVHZLkVf/AIMPir3ryv8A 85h/lZqkaLrK3eg3J/vPWiNxCD/kyW4dyPcxrir0Cw/Or8o76P1IfN+kotK0nuordv8AgZjGcVTC f8zPy3twDP5r0eINspfULVQT7VkxVjOs/wDOR/5L6SjGXzNBdOPsx2SS3RY0rQNEjJ97DFXkvnP/ AJzYtFjkg8m6G8km4S/1QhFHaot4WYt7VkHyxV85+dvzF85+dr8XvmXVJb5kJMEBISCIHtFClET3 IFT3JxVjeKuxV2Kvav8AnEnyo+tfm1b6i8Za00G3lvZGI+H1XX0IVJ8eUnMf6uKvurFXYqgdc1zS NB0m51fWLuOy02zQyXFzKaKq9PmSTsANydhvir5X8+/85patJdSWvkjS4rezQlRqOoqZJZAP2khR lSP/AGRb5DFXinn382/OfnyO3XzNLbXclqf9HuUtYIZlU1qnqxor8DWvGtK4qwzFXYq7FXYq7FXY q7FXYq7FXYq7FXYquMcgRZCpEbEhXINCVpUA+1Riq3FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX3T /wA4ofl6/lf8uV1a8i9PVPMjLeSBhRltVBFqp+as0n+zxV7XirsVfHX/ADmR+Yt7e+aYPI1rKU03 SY47nUI1JHqXc680DjuI4WUr7scVfOGKuxV2Kvoj8tfyI8o/mv8AlnDquk3LaD5q0yRrDUCAZrS4 eJVaOV4iQ6M8TqCyNTkGPE4q8+87/wDOPv5qeUDJJfaNJfWEe/6R06t1Dx/mYKPUQf66Lirzkgg0 OxGKtYq7FXYq7FXYq7FWbeSvyY/Mrzk8Z0TQ52s5Kf7kbhfq9qB4iWTir08EqfbFX0Z+Xf8Azhp5 f05or3ztfnWLlfiOmWhaG0B8Hk+GWX6OH04qz/8AOb8lNG83flwNC0OygsNQ0YGfy/HCixRq4HxQ UHEBZlFD/lUY9MVfAd1a3NpdTWl1E0Fzbu0U8MgKukiEqysp3BUihGKqWKuxV2KuxV2KuxV2KuxV 2KuxV2KvU/8AnHz8o5/zC85x/W4m/wANaUyXGrzdFehrHbA/zSkb06LU9aYq/QBEREVEUKigBVAo ABsAAMVbxV2Kvz0/5yNguYfzr80rcbu1xG6n/it7eNo/+EIxV5tirsVdir67/wCcH4rkeXvNMzV+ rPd2yReHNInL0+hkxV9M4qxjzP8Alh+Xvmks+veX7K+nf7V00QS4P/PePhL/AMNirzLW/wDnDn8p r5mewk1HSWP2Y7e4WWMfMXCTOf8Ag8VYjef84N2DvWz84Swp4TWCymnzWeL9WKpb/wBCOar/ANTd B/0hP/1WxVH2X/ODdqrK195weRaDlHDYCMg9wHa4f/iOKss0f/nDT8rLMq9/danqb7ckkmjhjNPA Qxo4/wCDxV6P5b/Jz8rvLZR9H8tWMM0e6XEsf1idSPCacyyD/gsVZlirsVdir5w/5yd/5x+fX45v O/lS2L63EoOr6dEKm6jQU9aNR1mRRuo+2OnxD4lXx2QQSCKEbEHrXFWsVdirsVdirsVdirsVdirs VZT+XH5ceZPP/mSHRNEhJJIa8vGB9G2hr8UsrfqHVjsMVfoP+X3kHQfIvle18vaLHSCD4552p6k8 zAc5pCOrNT6BQDYYqyTFXYq7FXy5/wA5hflPe3Zg/MHSIWmFvCttrsSAlljQkxXNB2UHg/gOJ6VO Kvk7FXYqitM0zUNU1C307TreS7vruRYra2iUs7uxoFUDFX6I/kt+XS/l/wDl9YaC5V9QYtdapKm6 tdTU50PcIqrGD3C1xVnOKuxV2KuxV2KuxV2KuxV2KuxV2KuxV4L+eP8AzjBpXnGS48w+VjHpnmZ6 yXNu3w2t43UlqA+nK3842Y/aG/LFXx15k8r+YfLOrS6Tr9hNp2oQ/bgmWhI6BkYVV1PZlJB7HFUq xV2KuxV2KuxV2KuxV6n+Uf8Azj55z/MKeK79NtK8tcv32r3CEc1HUW0ZoZW7V+yO5rtir7d8g/l9 5X8i6DHovl619CAfFPO9GmnkpQyTPQcmP3DoABirJMVdirsVdirTokiNHIodHBV0YVBB2IIOKvB/ P3/OIHkLzDdy3+g3Uvlq7mJZ4IY1ns+RNSVgLRslfBXC+C4qwm1/5wcu/XX615vjEH7fpWJLkeA5 TgD54q9s/LH8ivIX5eD6xpNs93q7Lwk1e8KyT0I+JY6BUjU/5IqR1JxV6HirsVdirsVdirsVdirs VdirsVdirsVdirsVSTzZ5J8qebtOOneY9Mg1K134CVfjjJ/ajkWjxn3Vhir5y87/APOFAeSS58l6 2I0apTTdTBIHei3MQJp2AaP5tirxfzD/AM4+fnFoTN9a8s3V1EOk1gBeqR40tzI4/wBkoxVg+oaP q+nOU1CxuLNweJW4ieI132o4HgcVQeKp7pPkTztrDhdK0DUb4natvazSAfMqpA6d8Vem+VP+cSfz a1p431G3t9BtGILSXsqvLx7lYYPUavs/HFXv/wCXv/OKH5c+V3ivNWVvMmqR0YSXihbVWHdbUFlP /PRnxV7UiIiKiKFRQAqgUAA2AAGKt4q7FXYq/wD/2Q== + + + + proof:pdf + uuid:65E6390686CF11DBA6E2D887CEACB407 + xmp.did:951de943-bba8-43ef-aea5-cab77fa8aef5 + uuid:712bf3f5-e035-5944-933f-a93da12a05ae + + uuid:7db1d830-3358-1345-97c6-ce947c85e793 + xmp.did:18110077-86fd-480e-ad7a-4a63df187377 + uuid:65E6390686CF11DBA6E2D887CEACB407 + proof:pdf + + + + + saved + xmp.iid:951de943-bba8-43ef-aea5-cab77fa8aef5 + 2020-11-07T20:56:10+01:00 + Adobe Illustrator 25.0 (Macintosh) + / + + + + Web + Document + AIRobin + 1 + False + False + + 1024.000000 + 1024.000000 + Pixels + + + + Cyan + Magenta + Yellow + Black + + + + + + Standard-Farbfeldgruppe + 0 + + + + Weiß + RGB + PROCESS + 255 + 255 + 255 + + + Schwarz + RGB + PROCESS + 0 + 0 + 0 + + + RGB Rot + RGB + PROCESS + 255 + 0 + 0 + + + RGB Gelb + RGB + PROCESS + 255 + 255 + 0 + + + RGB Grün + RGB + PROCESS + 0 + 255 + 0 + + + RGB Cyan + RGB + PROCESS + 0 + 255 + 255 + + + RGB Blau + RGB + PROCESS + 0 + 0 + 255 + + + RGB Magenta + RGB + PROCESS + 255 + 0 + 255 + + + R=193 G=39 B=45 + RGB + PROCESS + 193 + 39 + 45 + + + R=237 G=28 B=36 + RGB + PROCESS + 237 + 28 + 36 + + + R=241 G=90 B=36 + RGB + PROCESS + 241 + 90 + 36 + + + R=247 G=147 B=30 + RGB + PROCESS + 247 + 147 + 30 + + + R=251 G=176 B=59 + RGB + PROCESS + 251 + 176 + 59 + + + R=252 G=238 B=33 + RGB + PROCESS + 252 + 238 + 33 + + + R=217 G=224 B=33 + RGB + PROCESS + 217 + 224 + 33 + + + R=140 G=198 B=63 + RGB + PROCESS + 140 + 198 + 63 + + + R=57 G=181 B=74 + RGB + PROCESS + 57 + 181 + 74 + + + R=0 G=146 B=69 + RGB + PROCESS + 0 + 146 + 69 + + + R=0 G=104 B=55 + RGB + PROCESS + 0 + 104 + 55 + + + R=34 G=181 B=115 + RGB + PROCESS + 34 + 181 + 115 + + + R=0 G=169 B=157 + RGB + PROCESS + 0 + 169 + 157 + + + R=41 G=171 B=226 + RGB + PROCESS + 41 + 171 + 226 + + + R=0 G=113 B=188 + RGB + PROCESS + 0 + 113 + 188 + + + R=46 G=49 B=146 + RGB + PROCESS + 46 + 49 + 146 + + + R=27 G=20 B=100 + RGB + PROCESS + 27 + 20 + 100 + + + R=102 G=45 B=145 + RGB + PROCESS + 102 + 45 + 145 + + + R=147 G=39 B=143 + RGB + PROCESS + 147 + 39 + 143 + + + R=158 G=0 B=93 + RGB + PROCESS + 158 + 0 + 93 + + + R=212 G=20 B=90 + RGB + PROCESS + 212 + 20 + 90 + + + R=237 G=30 B=121 + RGB + PROCESS + 237 + 30 + 121 + + + R=199 G=178 B=153 + RGB + PROCESS + 199 + 178 + 153 + + + R=153 G=134 B=117 + RGB + PROCESS + 153 + 134 + 117 + + + R=115 G=99 B=87 + RGB + PROCESS + 115 + 99 + 87 + + + R=83 G=71 B=65 + RGB + PROCESS + 83 + 71 + 65 + + + R=198 G=156 B=109 + RGB + PROCESS + 198 + 156 + 109 + + + R=166 G=124 B=82 + RGB + PROCESS + 166 + 124 + 82 + + + R=140 G=98 B=57 + RGB + PROCESS + 140 + 98 + 57 + + + R=117 G=76 B=36 + RGB + PROCESS + 117 + 76 + 36 + + + R=96 G=56 B=19 + RGB + PROCESS + 96 + 56 + 19 + + + R=66 G=33 B=11 + RGB + PROCESS + 66 + 33 + 11 + + + + + + Graustufen + 1 + + + + R=0 G=0 B=0 + RGB + PROCESS + 0 + 0 + 0 + + + R=26 G=26 B=26 + RGB + PROCESS + 26 + 26 + 26 + + + R=51 G=51 B=51 + RGB + PROCESS + 51 + 51 + 51 + + + R=77 G=77 B=77 + RGB + PROCESS + 77 + 77 + 77 + + + R=102 G=102 B=102 + RGB + PROCESS + 102 + 102 + 102 + + + R=128 G=128 B=128 + RGB + PROCESS + 128 + 128 + 128 + + + R=153 G=153 B=153 + RGB + PROCESS + 153 + 153 + 153 + + + R=179 G=179 B=179 + RGB + PROCESS + 179 + 179 + 179 + + + R=204 G=204 B=204 + RGB + PROCESS + 204 + 204 + 204 + + + R=230 G=230 B=230 + RGB + PROCESS + 230 + 230 + 230 + + + R=242 G=242 B=242 + RGB + PROCESS + 242 + 242 + 242 + + + + + + Webfarben + 1 + + + + R=63 G=169 B=245 + RGB + PROCESS + 63 + 169 + 245 + + + R=122 G=201 B=67 + RGB + PROCESS + 122 + 201 + 67 + + + R=255 G=147 B=30 + RGB + PROCESS + 255 + 147 + 30 + + + R=255 G=29 B=37 + RGB + PROCESS + 255 + 29 + 37 + + + R=255 G=123 B=172 + RGB + PROCESS + 255 + 123 + 172 + + + R=189 G=204 B=212 + RGB + PROCESS + 189 + 204 + 212 + + + + + + + Adobe PDF library 15.00 + 21.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + endstream endobj 3 0 obj <> endobj 5 0 obj <>/Resources<>/ExtGState<>/Properties<>>>/Thumb 27 0 R/TrimBox[0.0 0.0 1024.0 1024.0]/Type/Page>> endobj 24 0 obj <>stream +H\I q +] TIM[  a{j{TDe@f_/|m/Zfq=z|(-ʏDC不X)NmNзog{5pu],սxkV}` w5coUbQQ<9wy:,jܩcz Ӻz*dKpm^myQ͵y`rѢ̅wS3ϧ.;Lf Yxh껗v%i XxsӪK.4kz]ڹclS%/{w=#]T)tzݝn]\:a5p+ޱk+w]1;jU7ohܠWeʞvxёXktf0o2C^:s݃Q}@YgZsG:!jdL%1t%3H[usKkmw#ɉ[ stvt7|OD<nVh2 Lא @pU,hHyЗ{P.睑0NBsu+ز~?rrr;p\B͸[P[܃Kҕh;=t%"ANT:@`cvOqC` + la9T4OysO/PzfZYDNy҅lQ-5@u5""`C{Țr`HRE/CJQ= 9فy[v }UE[JfRsM ZKCc8-o"MgP_d&-W3a}A)1P~^Pڅ O5j$hO%ED΀VPM1X>Z>sQoa(BA硷l,USgI>'_!t&h͑&jDK=TQC=ξ)^1%!Af"ۭt&.{Wsr_6MxW2 ";TSy֚VSWܐ17 acڴ橤nJhQ+' &#Cel`CrdI}ĠpPvGPJy+({G\L2y +}l⤎ȶ$&DK%(ߋ7ǖYy;ɢ T="i9Y>9^J\AI}2B}]vO$+7xj7W5?\ *2q}غN~Fz H: 7 }NmmGdMj\JHcpua`"k_` I75WS@J ^E¨2r~Dlľy%IUR%hJ\Cv{FWEFhgؑSl8{'bH9:4RXWBcRB2™ΦfITv]I @s݊)s9fw~159HO<]0gO̲uJx.RM\+^+KU(eK +5Ol*L^ sD])ORU5OM(b\j&OfvUL=|J1;#nVWPb ;0TPld{s\auR<. [5<ŪZ9JyRd4Ű[41͇6o) bN9>%RK^%.5 ]d$K8u +SsNd.e@Woz;%I-'M + 'Y>Wk)2]RMžjD|6.5u^p Uܚ2!FRmɇ /ᆲTrO@K"<2vdf5KiY,BP;h-UGeӈD+2*g-CzA~εz; 3qaYٵQ]cx]y#EN'֩ +A&/%x]AϱC5(A#FXZ%`Z9PDM+q]0s + %K $3oh$A*{{-,k/A|[xZ,>JbւYt懗4f89ϣw{gzFk}VB:'NHҎ+`7=RMm*m?vUDМ|O"يP +KcЈ(̊$X9eW9%hR^*R62[rHO v)+Ck:ʁ٬dWɕT[ @&`^d )rwsFT|@# uC|6˔6nQEyC_bWKb8)I*f$zmY.8_iޗ9y#9o`%{sE-fEbq-i endstream endobj 27 0 obj <>stream +8;Y"299R18$j9Dp^RA6" +.)W,Lr,br6Ubd,oceQpTXRkZYF<)&*&OG8,\gciWKX6((HU)q@cU;\1Gb%qq*=nnk +ehO-HlM6B_$nHd*:!OXa651jpS`mc2Z#E<6T\nempcW9tNdlIj5`arJF-MLpd!nNg +4/TYIAR.-74Gi[dU*)POTD?-;7fn>8,`pGWV:V9$AG?.nWt\M?;^3e8M3`4JJuOdA +:T:;RQDTM/K(l/rfhPN2iTK8L'hNH6H&FUUXe"/[TmX6EY@Ru<@NS\b3GJ; +Llq&,CkWjcE^l44J!K^McuAE5Y;UST#.tE4uSE8@]da +.,2_*JiN#Bg%3Js<-T##$tn-O:O.@LG86e9QWPq)pJE[2uCj3QSQ) +%V`tj8s/\3DX]RbNr^No~> endstream endobj 28 0 obj [/Indexed/DeviceRGB 255 29 0 R] endobj 29 0 obj <>stream +8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 +b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` +E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn +6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( +l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 22 0 obj <> endobj 30 0 obj [/View/Design] endobj 31 0 obj <>>> endobj 26 0 obj <> endobj 25 0 obj [/ICCBased 32 0 R] endobj 32 0 obj <>stream +HyTSwoɞc [5laQIBHADED2mtFOE.c}08׎8GNg9w߽'0 ֠Jb  + 2y.-;!KZ ^i"L0- @8(r;q7Ly&Qq4j|9 +V)gB0iW8#8wթ8_٥ʨQQj@&A)/g>'Kt;\ ӥ$պFZUn(4T%)뫔0C&Zi8bxEB;Pӓ̹A om?W= +x-[0}y)7ta>jT7@tܛ`q2ʀ&6ZLĄ?_yxg)˔zçLU*uSkSeO4?׸c. R ߁-25 S>ӣVd`rn~Y&+`;A4 A9=-tl`;~p Gp| [`L`< "A YA+Cb(R,*T2B- +ꇆnQt}MA0alSx k&^>0|>_',G!"F$H:R!zFQd?r 9\A&G rQ hE]a4zBgE#H *B=0HIpp0MxJ$D1D, VĭKĻYdE"EI2EBGt4MzNr!YK ?%_&#(0J:EAiQ(()ӔWT6U@P+!~mD eԴ!hӦh/']B/ҏӿ?a0nhF!X8܌kc&5S6lIa2cKMA!E#ƒdV(kel }}Cq9 +N')].uJr + wG xR^[oƜchg`>b$*~ :Eb~,m,-ݖ,Y¬*6X[ݱF=3뭷Y~dó ti zf6~`{v.Ng#{}}jc1X6fm;'_9 r:8q:˜O:ϸ8uJqnv=MmR 4 +n3ܣkGݯz=[==<=GTB(/S,]6*-W:#7*e^YDY}UjAyT`#D="b{ų+ʯ:!kJ4Gmt}uC%K7YVfFY .=b?SƕƩȺy چ k5%4m7lqlioZlG+Zz͹mzy]?uuw|"űNwW&e֥ﺱ*|j5kyݭǯg^ykEklD_p߶7Dmo꿻1ml{Mś nLl<9O[$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! +zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)Km endstream endobj 7 0 obj <> endobj 16 0 obj <> endobj 17 0 obj <>stream +%!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 24.0 %%AI8_CreatorVersion: 25.0.0 %%For: (Martin Schilde) () %%Title: (jamulus-server-logo-2020.ai) %%CreationDate: 07.11.20 20:57 %%Canvassize: 16383 %%BoundingBox: 0 -1024 1024 0 %%HiResBoundingBox: 0 -1024 1024 0 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 14.0 %AI12_BuildNumber: 60 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%RGBProcessColor: 0 0 0 ([Passermarken]) %AI3_Cropmarks: 0 -1024 1024 0 %AI3_TemplateBox: 50.5 -50.5 50.5 -50.5 %AI3_TileBox: 232.5 -892 791.5 -109 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 6 %AI24_LargeCanvasScale: 1 %AI9_ColorModel: 1 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: -924.521149296938 1848.14924411266 0.197049008168501 938 689 18 0 0 46 133 0 0 0 1 1 0 1 1 0 0 %AI5_OpenViewLayers: 7 %%PageOrigin:-350 -350 %AI7_GridSettings: 72 8 72 8 1 0 0.800000011920929 0.800000011920929 0.800000011920929 0.899999976158142 0.899999976158142 0.899999976158142 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 18 0 obj <>stream +%AI24_ZStandard_Data(/X\nư 5 oB IHSm'@=?xRJ)q&c:N %i Nt<ԔcӛjttJ#ttAx|F>(oyIy+GpYGʥDyH3>K(}|vڣo!<0$ItYE$>wA;NzםʧC_\h8 }S*-+F "A +@ !B (8(XQ \P„ &,2\@ c# dh1=Dp1H`c <0:x@WVtH+I<3* ( (8.@c64XQ nq5 ,84ѐ0a,g H3h,ʨ"08`Lx@yNMw&(ؕ9h~ÏﮋDR5fbp5~r?ƺsрpP"iG$ +`( pҨPL W`sԌD"`4"anP$h>G9. YPUz1ºZU%++2;ٲgG)_LmHT2խY֘̑sMϕ$N9ҍA:k=3滄E* ¸i"oF7h$4D̅2i$r81XH ao8-2q,aL:jp0J6a]E& xqАq ++D#(|YaZ޳geev8dCk,qjV9gvt@ECBbhjp44,fq\CIQWbX( ipC78HEGJN㎹>leՈxəՒfg#a,P, }ᢢqf* D@z"P^ajlxCaPr0tCaaF"a\A +2 !4/Do9Š\ái0ҸV84, E&mECAH7( 5F`4(35<q"qi$ +B؍c);`fDgѳ\݅9~sZlp@5DeQqFI0WcQC¨GDPaeXNZQ3v̩AeH(505X{6Xb 2 Aeء4E K\X4@Swh ca$O4@HeѨᐋ\QFXgDFFkFӡX(eFEóZ +K@d2m`$ɡF6F$WcqdbQc_ٜhd Ļè! psܘ'eħind26Ykޑ9N/+vYsY62̫]^`֋ze餒߆n[kmf*taiu# CXL *<"a,tE^0H   c`>aLMe7 ŢADbPBH, qӡ)` << +/(2AхX,""cƘ1H]\ao@K2FޝqDD%WfT%bn` K6@ F1 `k-itՆLâ!FèL@@ C—-8Q +3f*3R.sqUJ2fcc}ΑF2agExa(e%ҝF ^C)+Y6X(sa[3$yd3c&2G QSIY#oi\Wv<Ǝ;L0$ <00H@xpBx`q>:FC5(tH4$ E"@s87ؘ ՘l#8a|yFۙ +rݱCІ:aa(("ѨUJnI |u12`d8aTA 2`d0!P!q\- +  .Xp82  *ʺҡhyYML`}u#ZV?rݨeuUhlwZyOD*g,emIͺ!Qzgnݚĺ Nk,m?jS3#`Y<"["a ϔ{`Uۻv"+Z^tWvDsU׹YV_JNN_D\!>GFVϺj)]̒Fr2)pXudVn6({j脗x{,m&+ ov)3+oi|zS(EdfJybӲ2ד#r5g;ݱrUz;9ڪR;k#ezrIƻٓ.$,j OndxX96꒬V%APJڨUQ=+{c=%1cq\`ov&rzv|8v/IBrt4e9yI.6+U;=汔zJlWz]|\Fvˆ|9,Ng{ ai vhgd3V^=WV.j3焬9g+4WA96dϥԒ}:'^teTMo5?'u(v}۲Wٜ_ٞcvRGhZ{LI5%ɗX~$MA w9kD:ӭߴK;-ubVYW'3dey9ϣ6w#Sß^RBEyGG>JU#~I($R3ZD3eƕ:VOY5»Rs0]Iqy*1NB˙9; ־ ަ%~,َk{ǖblSKV5 #sL(X'seˑf%}e!^;晠":bs֖-H+6f{ +_G$PY$KWOzLmI)O68M¦IwX%Z:h?y{cu&!kK+^Vʗ}NHTu+Hu*޹3mJx&KFwY7$bU4J΄$a^i*egƳCRax'YG#IlO+e[YZpVx&+$oL$KGnz$y4sl -n"|m_x!X:}i/ ]z9x=LlX()$P2ITO~0Q=R?_dQW$˫g""XHgБuSf|~m>0[˫"y0Y=^^e"IgVei{mtW{)5j$z˜ 1Ddiv*C)lmxyD.t=IGh{ݝ!مrmwVv&eϥSqǢ1%]䝍7_z2[:>l{_\h2vXQ +a/vSȇCo$ѤtǧεxU$ϰl4+1ѫ^4 tÚfCCVc}д쭋)Ǥy8UAyRΎgwu4lug^>j3>&ih(&yܒN5=dr?rz!,Ú{Kk|uc]\wG֫nH󠪙baU_H'VHQ}%dSؠ]\VYeAYJ4}nuZSB+qH%]0BDbLsxUrz+on,t ݛk]I:xK|zãOe9WHwX=<;\{7% YKK-Ɏ󛚏'[4C)^Q9mjէbQ;` 8PaB@ c%S>N_ԑPh;$tI^nh#I>.kZPqBҤG֩^z9b=3=%w>Mc,C2reݔu,Ur6z7:oKY3ϒIJiN5 K3c'i/ݩe“ԓ\(隩 kx~ʆ +=SҧdV}j&&V5Cۦ~N$M¢,"$'IuSJw&i |P$ !T7Y9_2GU* OA=DrjR׳+9(ՠFRj:=JZأmյJ;>LAeˈP鬄lu;OԇK)Cw/;/!`eK2g7Wna>n4h9 +B+1tԧ(N2з)ܟ4=?FF,"+푡# ǔaIb{U?$g%9egʒ`U9*Τ{quXa IȦEr/3dȎ]^ +ՎZӖTtҌ[4YI[N3y(w(WG )nD ّWIyStfAvr+6 [ytId[kiD:Z莦/n<%t"^"-㮣. 6{<8Yzi׺Q6Yj%f_>ʧ^&YY`bV9A) +eHzؑS/ $K2 aU#P~)faM|=#JIK<D+Vy|fHE$F# wIbD)T%xi !Xwq2A852/;ƒS£ WLDxrJ̴Ux;H.]StڛҪ|Iwi>1׹͌>ϝ;2lFY|n 3 JIi&,'!~j#^++ɎWEt}kULϲ%Uf̫SW4l^bͫ;'ޗ]=M"I/浗ٯ&[{<~хNGXU8.l5:"}sdY卌r0sv4K#mLvD_*{Cgyos|yXT{mm!ل]ݕ>Evծ|ZwX mΦsdՐ;999992c·fLcsq(%KZ}5i1m6%njSidttt~lFX+ݍt}f&kÏ/GǏA"hcIcBEĻtНotXHg;Q?]ٝZs1[I +?Fڟ=eMDGgYG ݹH2LzӞY^1G5TU=2*A+՜ts> Ye١ɹXK;E=\DO!ΌgƲCfgE]kgFVySڞ)"J*KIdwɤ2/eDVy)#V|vVdY{b*|7f}bPuo+}2xetL>rnuG 9kJ؎9zdhR%|FtQrbk%tRQXGz~aeTGdSegdq ɤȶ̻֖U':QƈlXf.c>Ww-R-QQ?U43dSX~MFnkZuе-vVt TJeڛPZ+JggcƣT&U>j\dt ՊH5z"It2-쎞En|ak7;^ Q}zXQL!RL<+gZVǵ8h7p;K vKK<̎9&u)պɑ󹭹z$UAW^n9Ϩi1XG>70d$y٫8#E۱mGQjW%G:>DʲɚWgUdKK-uX-YE6Dˋ|:syR.SK=:jĺlZ9'u)8v4/غhkK㫳_qP>?czaYVC[򎣔6kr: +dvJ8ūnw>o4;Z8sV:YliCDUt]|PhD)+-фxO2ʼ݊N{FR\j8XGWšO8t+#T*}#Ω(-<|ChXZY6gf<挌DH|.Z˗KfNF7« iFO1zq.f>RLF"eS+W.K+-eYɮGf3lҴz2jwE7IEL?^Gv`x`O+yTGzݤG0Ų^5_=)9߂&S4}JPS]竘OyuU/Mx 힘:n񝼦HiJUC<bs0P›$za`{wf!m6t⡛{J(;}`՜Ўsh.1]]rWsJҺ:ɧThl;LKKXSU[3.R-8h=|](fdqKf 7;$ˢ੯wZc1:&DLPc +!H0@0 Q\~B^2GL1!`MU_ +xyA!/"{d n)lꩴZJTFNFϋP/:aK bpGeZa + 5^^48:ź\'}HvJBFfpJ4!™Bfqq{]FVZ43tN_{!-Hz}at;,R7člJTT eO$ۂ_QY +Tl_T%.Ga0B1 ff;Mys\1KάJ l\jvȃa 7Sh*Qo)wFH:I+9c +nlM S(4/jX+mLuR'䃵&Z<^f, `ޥ<&%ɋIJtighÉcTTq䗻L> sO⍸[VyHK$]L 8c%cPl]?O+Fe"MtK*o=× "6¥/VtwiEP(4R>zw/-ܪg]6jօ Pٺu*"y<]ͺ!}T׀nʭm~:K$xgEFL'9u#uiRޜ,)mlv%>@"X+^;;KQ ݔZ-M"K1 +I$!]vL kߪYWdbgL`Lܙfm*Ӆ3'H +t&a6݋PnAB=G%J@E_.Ad#8z8ӥM,21xrq%6zHE_z,>bR/˿=t_vއbu,y*0n~>*BE5 : ytwe=":zR0BihC6{qAN8#ZLo7*7`5U4u^̩h1}dhJM-AIMd ٗB Ll=/'!PL +7S>, :y=$W!d #B;"dv[)|{U5R:x]OVpB5ĸ1yD# Aaf\ 4A`ksq?\l-cD>K!hAApT +5/?PSټs.I$h$ %}ɂFk2 )B@O͔ rF^N5yVT#wւ[/\)|Щ7QYC*0&}R.Iǟ AV'*J=TVfYo\|+pΌiAkuP@ >/dlw]~:=u`1ODH*׮Mol# !5G$kܩ;%. p 7Es?E6" ziXoܶꯇ6N~G$7|pQ!q;+GJY*oY_ښ +sqCڛ)q4 4@F^E_!p`=k[4U=Ma ^n8ضZ7FXIA l-Ne eB*d; +=O}:-o.<+|WLn_n. *ĺ/0U!qYa i_\P!V: YBY/'C"qj;z@A:WVͯX!6FSny'mॄ.q^G@4 ݿ\ݧ6>~Gao͹H3&H)AH+= *ITW<8 4mGxj]w-= KCꫲԁ }eZdǦV YC/8ǭGis8W޴HH+\)0O\W"Jl!RVq+20`칥֪Mfh-ē)agvO eCm aq1c9h Ҏ(ޟ"lYY> +RfpiCsM/-cnac!m?}moX,M%i"P+`E&Z8&LijyԒxǷg)Ow<4ՠ[ ĕ_qNJ`6ϮƮdr'̌ZJ9plz,R-Sⷑ (!a(Q]9};?#><0񄣦&ُڄ+(#j”< kD9sF\XiK֮ě0d1 Ki DB8 T5IS9,a\$e.8C z@JAɌvW6W RSN6!ts8hhڈ`)*-]:☩-Њj6 8rNbʍhyUapLm9=-#zJ2CqɇbyQm5ü(B[,M]rZc{IEe*aoƅCՍgK+"@[H+b*IyQCo :p'6Y!'o.{}ss^:g5jnѨlN $9:xH1~M'涰eA.Ao~ȶR^Uǁֱ*/+yC׊";Z`M*d IQw-P;ѮegVm$^_,(M6r "JA:5-E/P.+4JɺƝ|EiOK +Hf}~ IbΪ,,v epPsDdTKq=B{~qC% +2\ZxӃjUYo0XI8ps{ݶG]NP]H`!78m/t/6E1 [`.g bx,`U*[\ 'fP-W-)_?cxu5c3K4'i:aPV 5{Q.Q~eHL~dDx}PwFXd҃@Ut#I[a"D ~r)alJH!:,w|ԣL?{qCd\lEmvRԑ/6*{ѵl:p*:b^,'0XjzӾ@"(̅gj{A8Y.eXl~8CN%Lƹ˷5_I``%1NߏN1ޯMonv|Y^3vbt^zŽ b+41bxBK =-jrb0PHbȒ*$JPQ*j^3$DFTAd69Mܠd}!LLHF[ZEx?fU @j +a2SKc& $ +;l1eVg( cYa?awy/@r#z4X"4i&2yj{=nI(FER\A}tX,ed5x9c$gJ Ґb8+ y;89()g<@J-va-Fxi7o`cðL'L1O0N,$֜ %y!<. #Drz/ axXp#"B熫_){]/" Ϳ^Bj*=R-RUl5L[b ^4"ŕ]ߠf11GuK54cmjN)U`~u|x#Cv:t LStZ2[~b5l *CR舩j`_ E=X|E UY2s [a,7ԣؕgMS6@ȉ/ 8}DPܬh^"F];+ډ0O"į=_0a^t0 +? * fP."~P` 9> }}Hq7"X&6j2Ms,hP Ŋ滛RJb-yF%^?đ΁Nf¼[]3蛕I!ظPxP-‘CW/P&0҈3?q]ڰRr;z7gI~.y^UZOKGf"HKO?Arܒ޷i.̕Ê9Gߖ*6LF31yH:=0ߖRK5{;08ńa~/fVl8#u;ʥwN~ ND* h MY &+\ܷ݆Ym4}Ƌ&ϗh_Fֆ]Of􀇀/3\G^dL*#ȕ:`GM:m;,nLHc5Ķm#ܓ w4EbKII!hhۍ+1mV( AHP,^, jR(fk<UK/X +6 V^D{ȗe.[@A'-/ ^p'؀@8=ysעuK18|#7JoW*> 2^Ul5F8ٕPanwF[T>WSY\ЕAqb1@ZYĖ@s7mӕHH!ErJJ_|1njVNyAg"7;{NE:Da]ƣM5c(Wsk'9)CEi2/{?5#5Rfq18y';=0ֆ)5hcz_Te"yСʤ닡Ӥ'&,-}d->^Myݨ|U$3"cŵ`R j&kk]M7c3@v!4D#j]%kda肉lD]\h +p,8EVnbC=}rE1J2t*R9 G0D'sa1{ChQ{A% -tFꥶYG.YL,)ш7&ٌNt^ZjQ>O!$kZI`D96O5>`LppL³1U_šy\ɟ[NZB]LK_P)f ^ ?,o|1Ty;oLӸxRV73DOl &=Qˍͻ Fc#*͠JXCS C"~Q}N^Dtn0ւU2F!.iVkf΄5 i̎dHԄagXA@bPEXW^OWOi&eցR7?8cgV+' wN5A>G9}"$BJt6w)cNB1P ݢ/`IY_`l$"몄%)\v%$kC 14^}yW 鞱ocƨA5?bQ" 5@ + ?^Lp%R# osCJM7|Ƿi()'*CkZ1 (aV߹QikoBAЯ\l_Upv ONT &qĭʳdrE%0/S&'-_yN_m(y͠ +NS5!3+Q 4O%=sCĺA'y0n tDL-> +N4"Wgr#L +ٴX51wUrFtY'@' C C#s!3ҹQNjx]Nv5~JӣUH<'aotUϓIM1ܻ~cXn3?$VN( @RC6\;A.R8SX. Fi2 0>Ƽ/_<ԺNH1B- ^yu!uqyHWLcZ5PSb\KC2=l0QGh3dٴ҄Cg^vӦqTc#4HV%:C]OO3tW&I-@`_ %v0#,Bzp,j[Ê2bQ \2`1e~j45 `]THnUz:~{,چ "@E 2ץo~!] ^^FuFߔ{=pW&^KFH1tI:R [8 Z"J2>/;}_j+NW"y!ufV75(jY>"$6ݽ.TwNaȉJVh$_BxC4& +oo&Z;0E`[i<97 гI4(PBm1Rt;k +ㆀF +G TiL )PJ%7crMrihUI#]rsIiۀӦD>oD>*muiJ5_{/N:0_rj,^Ӛ^6qp] A4_2Y?iU]@^<] +!u+|dEf/(VӌPL=&wwygA*B +IB}b4Z,x>$w[ĮvHe!ᳶ7 7K杋0\@60ӋK~u6hSH)YT|t4̐2h\ V/קԫ(pĭaF@ yO%{+DHJ $Fp4 1#kjL/dz1g0u{+I5CjA͊i[P ݄2Hz&+ O)&cp\?9a72] LD9_0yHͼK05xS}Rgqg%[Vb,UfN1—t3x>8 O+G[i(Y@ufAݱDN3G#[^G|bbf6L,tK{^K`Ʃ~̌)reA*lLH ~)G*0XbMSXAL7"IN饯%q[AbP#9zGla$(I>feELWL'b 5{HO '@UQaƅVcn/>hXh8l+9run#o0 +T.&c1wڏ H0 >VFvV:vц^2" l( ˗;f̤g|f 8 c3>Ù?#Uuf)g`<j^3-pAC45hUJӖlM)ƓӐjz55pL_8 'Sր4h-2v9 Ҥ+#SxNM*z4 zO>KOiPsSئi4 Ct[xuUo!CeJؙtatFճqZ=h m$WU ~R4>4-κ뎃y8Q˼P ~;۷yϥ=#m//ᮬW&boR,:6/K Vd A >2V+)>w)g]ၝV4 EԹ +,{5 ഻^w1D1cw*Ё#7NwXY\gMWEWyB\jim#'?u.w}!nH^ 0'NRc^HZ B&c;C1zg>Uچ|1vOw<ʘD_Z}cEsZ`d7X/j9/)}XM6bcs9JRG'zKBWjgT[1XDOx?*B;R_Thב^='e6-z7lǢQ J_xTBGqMR QCifn»'IgyEk:8ٴ,.5 "kLP@/%'UID WD| m6vE4I5flkf-(VءJߨ/*AȰK1l‡eيNS֠Prt/,6ʠ6ju<U7DpGNA,W4UL, +t1S6/Oa~M,(} % "Aژtt+%SZ^a0x0Jol}|hit Q?0AYiNŽgGHNDj ;Qté,׃v O3M"~=47Bz% R0G"0.\A4`wO_)ty+=̛yO]Sphdf(t8|hŇf͟-(:Oi eQ!d@ 9B]!B;''P9CJ$řޠvv3Dp삾_8} z +o%$I@Rc uKpA&x4o'X\(*x,珼K9RPmNN0=>-dY?}޶|$@OS;ODR1y + A)[>D\S\'rCϔ;c& <(Eh@(UQ*#Cnqx^ T SY(L H #IƻqTe[`[a^v0‡۲I˅6(jCXs5 +<̲Օ/K HK,m<ک1v`SF, Kc׋ӡT;鼊F$'ftQt:ݯ<%XoMPCz6HQ +*M'AF(u5u}z_ OFH:JO(wEJș}XDD"^s}.&r2x5v(G95R; | 5&\$ȁw MQx(}\HQt4铖^NDl(lb p=L_]41&ϵbE7I1z_§$N,\-2OtF KϮ~A ?0kM!pklx skɵ/!k:A t$!gv9y⹷VzJ(Rkvu9`PO^7I>- M=.,.mo)-54C۾v-[ܖ]gb`oVВq-*b +jbppf,ң$%Ō?SDS}f4vt|D-R,[(\◞ HwDH7lǖɲa>qVw㺌#QG ++!ZL'+%v%Q?he\8AW#}*_c6vSa0ʕ\+Z.M׳YZu(F zdJ8)]V-4ˤ Nv,gE[_曣i5pGdXCՑAgwU[;!+|[! V+:cf(;C]a"3Ud3C+6Z{NZ_iU/I_gL"WT0t(;K8B4I[1B5Rn׃VXQ^xk0t# +ɑ }Ą'%}Ը؀k 1qJ> ^5E_DA:%fO^kֆEn L~ rUJԻq<݀Iqži4f6}XA;ju27 +sy9 IBMuOCF]6z@Zwcb|@53W"v**WlOKo+Uf}+t,\[gh&['VD.i%'yfrB*^4:9ArMe|8H@K-ߠdg4#VۜD`#EdymM$`@Hʁ_S$v%LYWKg?% Eoӧ#6\SEUQ?2k揹il9(CRl<0!|%x)~$bjӳ"dT[LX0$u߳c%H0(M}h+hQ)ng"4quɍK4nY"rtR"$_#BJ*ұ|M%xW*cK]R~16YƔɽyQjnt6d LۢvрS*5 + ~ҋ_>jfRʴ+&/#cr2c1N_wwSUۏrI17]y>|ǭL}zxq+, + \Xb*| o.ͭ֘ۈhGD@V\_;zTlAUg>m`c$2>q(8b{D#Y_By(U@8Mev';dEpqψ(jD-jJ쿽D$=1#ƢM`DݺMSByZ@cfww}rpjD@ْlx]y,jol³ZƏ͈;|zZvxX}{n崔<{,$M3Yʲ: _D(nAxy +&N_ '^x2%t&U4a!- yа$ٶlhh,BBk~NF"P(,v*6M]:>wuǖ\XދAӅȋC V:]ov3@חi_=ڑ2ua<ڂp^]HQ + R GչĂ^f`ynAk ,}6 lAUV/3[Q+_ƶ9m{6umo :cX%z.E.>h|.啴y膰fVyO-&W.]SӴk"2vٻҽӻ}9?f tgڥ:<tu\!} T[z]@w{ޡ>`yU\ er!u(4|]d&X5Tr]嚓Gȹ$UK7 &VdGI+&y~rbT=s-sB~JvU1{iy%u =t}ld~({<)f"b9S8Gn eNҍ)_2|Te pd^In +]xC\}Jo LmW{8 U~oo3:)VrC/ѣ_o3S>‘R.JO<0rvOr ;?rR' uٜKkn +kt\'A$Y(,}Hk=#PMN [0 +yKke*8,oz c,m:TX *;N4o1a}^lr{7kv39re g6 0ěIk<ɿ79bGmrS_ӍzTyT{ـ{t)ZVPVz{%֣nrzq"t";Wl,CW T,]w8J*XBJnu+y;hS!wa6:a}ܒu/*84]痱 &IquX,OrhёU_H1ᰞp70XeUevF +"P}H*ܫ |uLdu d\p0fCZgX|\\x&6'_b@ŊAl|غ' #˼@1VhjdTN 86#k_4-ßp˳L:3TB}ࠥ[!MK:`?_/U| Yi!د[NP+PqB EQg+`Wp0Ryt+IGŦU ke; +w+Ytr;)ʾ9ߥ N;(ȽH1 P|QARi<;ki$ g2Ux{Lb*K/GwFW P9]K+t 'GlFѬ ޙ;dc*qs\Y0 \BG`ݭת!m+=Ua횱$X%)W9ro βu `1uLr#˖F +{Hۚ|bv5+7Lpb9[>u 5Fdl۠,?r7 ĩFg&rOO׽{\mJEYG,TtWͱ ZtEEнښmM(WfW&n +K7"j ~m_ nvd<ݘ3A㔶d,B=2OKtHEN-:FTZYuV1u~ZHDv(*5&㒍*#i[|iּ,3l7o˛KqS$!-dᔬGXw|E=qvqYM1K:R՚v" bZwmD:㒰[@ix33뾟P^PY YJ{#En\˩^C,[hSarb;Z;Z:K`36pj }J4&r2&Z_!\j}4>fK +!d=7T+WfBiӛFFCЎ0\8y \vρ3,Qr8u7f  wq(>QX%ik :H]upq %N͜r+y8N D'a1TWvݤ^%C5bcsje{I< v 9_Yq:UM4NO)+nqo&B}KjGQB$^I3g# 0Q? ąp|kq@+}+[ Y`U 1x3ZU5[험d7U%zfMKL_m&;My-F2c4F#c}9#)qˏ=qAs5#%A5-Zlubv_X^4!'rU<BdN1 ?-/OM3 tUZyz>| 3r@w+ +P )wSFY8ʔN?V~ /e(K%6Bf RUY DMf ЩeAŃt*q:%EbǨ.}v/Y͕*.e : +:_GLr>pHY*JIIa=Y:4?,NG3hЮC4rkye=y-IHxŻtI'd7Wk,[-縒}:@ CVC[u]5B@OwC UKb{Zzr4mw {Uo~%Օc碋z"Ji==Z:TEQ^G^5U h׊q6/K/]p=;kN{ָ< e +Bw.[;E 8ܬ3;7] %2!?oV(梶- QQK|l: v)Ian9&(}J" ?*EgA!;TΫ?Gv`\?iR l8 i$h (/iz e1}( KuR%޴4\$390ޓHvb@OkAl?=z9P3_F6= p}.&8牢qO2B0ؽFIpξF|V~9&<3%P!Ã#5BJtDtsbD7i罉QUG CF9C}ɶۦ!a%XV0uy& q8f7+As#'2$-{O O7].fy_^@Eۃ M,kL\."֨- 1-X`1p#0;|+&l7c lbNF1XSH]ͬ(-vՑuuBF28Q ecaJE7†_uCR'/n\HO1$y֋4J8em:x3x&p|)jJ64rZGC@jY@25}WOn(#*bVF"0_[ZуjE{]# ~h%sVz@_tPd02naDO) H^-L_0fkA^D{j֎ZOmՌb;G* ^@v~ +(nŚיBDj0\ _^$!䵊AU RUyFUM\,U1o5ӂKUfxR_Z`I5돩ʁ*4,U#jHSrY̛sUUL;7\jK@@Y(|]5 w]aE;S:5@p64*W/Hf!q(ֈTX߳ؔJxz5@~K3cE!=IX)'6ZVqgyLlbJ\wbg"FD +!TtvLb2XK,CHFO- H5o9I ڝB.${z`!`Hh|;3(/PEs +Ue=?hl-=4G80j}KU"J|-Gc\\yD؉m;iҕ=us+懺* VX9@[>vㄈÑf/㫜t ov#ފJԃ_uZA0`sfxv[ʊ|T. j}xqHgHۃZBOf6iBHB +T<ѪY~#mZ_:iC{PZj1z Sʡ;׶ΉkQR:i qȣ';)<'KiXΠ'R.<{C֖?5̈s%A&BVCt]aD2tu);\) +6T~"'Az0_IYbm1 zO<{giWo˾s}Ƈ Wշ DN/b&jooV/ eqᓈ)uLe?@ɃJQ#?{x=;`. +Kx(YQi1#x`кYe sb.5~0-B K'bARM]^vM5] 7Sm }[JwcSi9S}%˸wiJ8SZ j2R5m0 QR7LtDt:6jcD{g t2Q^9A64ģn͙P܉},.p Ҧ4r_Ȅ~68W`ת́7ô%u s]F5lewQ$K +ؾ'/I.m[byd -liqeR-GD^%B'͵ĵjRr˚|QϤ*nu_WLg #8AQUgcʬe0 +~M>SZ ,n4X>S.dP`(3-Hl (2`+A4EX:C, ES#zR$EW8d"[Owc2S =5uŀVR"Ig ŏ$v:(TiEKR^eR V^KwǷ,#~Q0QgϐObBQ˙I*/H],lŊ^"!? ] 9p@C`?L !!>OM!uc(Wɍ<6"ѾwTJ~0ZRI3ȯqӼa6o_?yЙU{Dbtx0/:q99;0e:{tɎ+]c553aVod׶?0B(E ӮH +;zkgͮe]V)%jCvMV2]XiXvTѶr[PnM2ApFͿT8+. XzO|z]By5Za*-Ȉ^xͭ* $]GAz]،|5m rURmqwDnxGV0D%Lve6tǑt cn(iw&m$igi r eHEc=@E! ?r񻍫";tbk+mfdʓ,Pd3jh7s=#G[*Ŵϭ.H7K ''','3Ob;]ġvc蔰#SZ3iݿ6t6hg251,9#MDMDLGV 9q "|xbnTtkhOHk# f_7a>*xɁklL]Ô rTW <-ThA("əɘz23O3\QSɰva&ݽ?V_`8sk#Lu$U%xxC9?h",~6<:6Q` X>À#FRʔLRKG}^bVZb*f11fBǨAteZu;9Oc>aA*<0x*§ϧLRmJPkEӆW<\;US^U$#16;so"oj5,N D4>h(ife.]WꐟM+b$,!'#$Vl3!S#OYƑ *μ0P"BDWH ]VŇdFLYy^WΘͫ"l¦k/3AD +%IPl:ACCa">M_-}/!W(А1BK&K:kk䫠6S%sg*Nx ScLN +WUՙ҂)ш+?֢ +<6%{ YW9+jY8ZXvJ m2HD&Cތ!AؤoV!FΗ8/&,P\'*O ܼZ؞o30q3 e%d*&}aS+d1b}]=/!;L7)#'W")AA 'mw#AUL1֩Er +.WUSffo:XEMgQmխ)&ySz4BmT鼈Aͅ,C$2Dqe2ce#M y}b$WF4A\0]`Z#/BP$D+/PL"/7T8QAD(0$! cL )XR+B(P( + DFDɱB[!ꕆ!31`G=N .). +EV"tv d6\UpU3Q;Ԝ=u<}v lY)&Mmڤ]v@{'س]OZ)pHa!zh~ +ѵR> +j%md2rj=),g*^5冓$`#;1eńJF\BE UkcI=SF0PM +'w.P 5fy}wcn;2E#r3=b#$0ZT 4^%zZ>q{ e(=)qΛu^(Oh6m IǻJR&ALNBJmd+1 HVNQ%JV9J%E+ҽ(OusZ m8$Q JjJjĢiG%.KR!5J`FG I G-=99,)t-jY.3SUS3Yh>, O:WiFx>"tb˄"ww,Hz&d!UwaPѾ$.[ye< yuo,4BʫlQq1&W~skAkki hb}^Kf2NQ>"+f!qtPNbɥ+;7FhqzY%$95_ڷ0ٌ) +v]TG#D~MF#VԂtѸYj5exTi4hdaE=ZPt&Me(6Bru\DjITxۙ#\t'"KxalfqNӯJ6 +wňxaIgZA4ATpjr:IA^NlQ.Tg܎گu !7k" r4"mӶF6Nm}9_L2r8%:Z5XY#e.9.E*M4ѩ(Hwu`YMS*z 1!bdGJd2*PUR1q+Gŏ?:5tVV``pb#(9 LbuD#"D"b$y + :  ( T 00AD@`#Є۟TF%h}bhj}S*4*As9S!QǮ"BbPYdS1ʠ=2aB)*)'HQŒ(Ǵ F+h +.,XDI_T5bZ 8*sSHTAtŪxkfM.Ɨjǚu2~3W:?ZԄ5mNelb5!FF>k3M+p=ԙ)N/:ATQ^֕\4cZ?Q~D?ΰ3K3gq7OgZ>WeU'z$4VFah-?9+U-gJ^я%ZG{9vo`\Oq<5cI2gB'+dF7+V b61΅i?C[Ju;1ED^{7D}%&$IKt +.|""Rn$$R!;y5)3ܺjСƶ3\kjQ^؎t]#J4ߌ(.L%g]hH.*KCӒİ)jZkdBYjoԣQYfqɤ1}B۸ +4F\b1Q#Y Eka­BL#V)%_{ƬA9_%rCngqcD!t*#ub't}lre !?&8MӐ(TZT=aZ bQ= %#,ˈ qNċ\Ň)9t}EH?O|fcB(/I6r'Wp%'fX6KMMcBzɎj۷(EDV EkLJYM$V)5d6/ƛ\3gGMIhTSxyT4'B]t6{n/׆vTۺZjgڟI\.*!rHBq9zQ#t<ڑ%-D܅s (ƯP>FBLf߰B-W^$(AAEqrDe#XvObF30\9UK#q6"QiAjf2⡂))$ ` +M}ǵg<?0ؾ3U4zryk)!?ˏgyBE-wO&qkȨUiCQ A!&NwD':QN" + + Th ԀÉF3a9N +m/ 3 t8&0dÃ6Ipjиu L 4cqjF"T D4D@S@y()9WI=B!h&|uP'BAj:oEGRcTxTx)4lsHQB44کNSX뻐1u)P4>fvWNrZEa,Y&kMk\>E3c| ̴VTHr +]Ejcar(L5$*<$94ǸwÏO*&*bbNC_aS@*YD :0%4 +^ԁ3j@!I +sj{: iitBCd̊0a4$PA"SB4*t@RP.! )IwE^#Qf̽@$^ԆԬ杬DKDDDcټ(Y2m:%0 ),gE[M¬i/BOPbg +f"A-OB󡮔C-PD"t(x<ǩ\W'p.Øat] 0_`(oA%e Y[Nb P@0{eVY.ZC_?4N~,5LȜ&LHbR*p8 *GSt͞FD#И31R6bFP9j0!e +:SX(:Ta>7EfK[FTjRh .|-=BiD-C`ZS-pZx(Bu&B'0 ^VRp2 SV3 +M,2i^{MGVL7T}Tn`@| yE|s9|!"m] DRE?ka9nf!)7￘)9J^U#!W.7AZUI0T#b=Æ%0r&D"iy\δ5 ,BG164 >zDZ wh5@Qa뺸,2.'-6HAe>^* ZăV/7s ;Y(I2`uҜ*έcvJq +H~gb +֧OQNtZC3C* U&vW?M` 4V?S`4:PF9YHTD$I0wOŃ_n(b/e#+Vf( X,zBM(f(@>m $URSi),YA LK94Ga,DnڅcY $B,G%pڙl\@IJkvGK.2#Pu."`;I -_d7y 5{ˑ3ۙ\#EP +nFђZ7#.F&arL@?JY5% ٌԯX7^_aLlPT32Q,S{oDUBp*phĿ v̵v Oʔ [:Qvl)0>p62)S$>!Xݘр| =X W7.Yf+ 'ˋ_N +*9@7U,/J.建1- \/}m$oW*J&#DB#tHzL@*Q$f͆UZw9!zQ2!|Ž]J1Zj F&\岘/V'k8~IU_E3%XL=7ϋy$8ic kBIڒs|b]6t`y 3ѹه ?TpڱJhdMJLx1 j*T!!+^OX'x( vf&A<NxAS +$eyIJeF,:؜Hyd;UkQ'I_]henpH)s_7;,-vd~Wй)@\)ucMȄ40qEEwأU^G!!da]y21PU;8 65t$-K%ش5GLֽ!0҄ K\`Mȟr{C5f(|3XǞO鰢#I '?pÐ&M4*ׄ\8mx.\<,QkЅP̍8(B EɷxŻ$/ K$&@O#؀-ݱ.S/MúUf0 +!YP`>0+.Ws6Ēy (m}My,yX:#x⟌bM"4ς~l+ooyFՂZ4@`$SVz :8)!Tk~h80.ÂVHaE0@SSܹK&|ّsHLdZ fFg0;]/ïM#JюX2`,pċ%2|k0{b5!-!1ڴhCs8c  +T6tDJ[7t %t 4ş8ji!n3p<ER@kۀ}*iFjI(JO*p6z1w8`P{{P J]*kVIW+2gC`BOgșr=JYX1wnKg~#lkҎPi PAcK;+l1Fr٭p PY-EBD̉ÆkdA4צRvPJ?ցA U< +zv>rwGz*b̵<J*$Se텕.*w)Wb[O:%@ND/҅eܘ!%Kz`s]K?+%J@^+3nL>7+g2ue -ڡlxæ3΂g?NW({1߳fV\#nE4μC3}iV{F0+".9.h6ʦ2ؔU D9)N۽UTvsf#g ]VHhyl%* 5*we,1c|V~7BnCmƓ(aI; F!6Dqʇe$̇IϘ=c4F"vXL"c֞^kf b ;MГ~DL=s-⮌ ` cHײ8is!5Z[+omK­ u.LHVp)W_1WרXd$/spWIr"wx#(uybWϒ^ttB +H!7u]!հcs)OF;2?;>H7}]+)"rL\ pVtI@z P#EH2!2:t.({P󏱲~rRBԜjbNvQ OQU!wS`KjܚI(ZI塀vDK'_,(=kAM^ DIkԇ%Z2aJLKR-}U WwB0F?jFKiuL={;\BwWq=I,ԗgiS ¾M6mBקVqVTz+C'amo9\5!&IQ)C{ +ġ7cB3EkgBOM4HO Rزjr{{BE}!enmKԁkx#iDw~PڋS7;^RO)#龜DW+e%~Ƅ^;B n67X9ǯ +#ba+4ܴ0D:%r=rQh:]z݁2lH)a5F.BaAk@CNU'*B;19.aSP3Y7^ uZj CO:(/Ǘh)k\&P"  "P(kq.CQ["!Kho,1Ms !kFWOwlkCY+&(A/%dž<%pL.]Zl4˷ r1:q)zaf~@[Pw'g+gS惄 A(Z "1LC@Z+[ԬWМA(v '4qbVuf1ړ#-7\9z3K +9a^0\W ޗvguXZ6Ѵܳݐi0  uc$w-"V!ILy$D;TI_.B|矌\q +n\ь_q[",)S2,#iP6"P.g9DGoSV`Y98srN7;F;lCP3ArכH|zOj:"(*&/]A/G19,WZ ZTnk*Pa1&0 +#U +)I{]*iF;q|hhۜHysHr`2Y=\B#>eZ J>#>CBN7YK8_[^}cŒcʠw-t)8o6AI$|^0J'^ޱ{@kRPgv4*g6F6M'T\Ĥ7hm!fZeCA^$835 >e[?D0P=5H=ELI=.QP-sRԌqR:Wt9Q;IEП DMc:E^_3@'o,EA0T +<\텀:]qտXx+ccJ u".deir+<#7*kʘ@rw $N6I: klp)Qu|@Vxjo^gt3z+U{t\7CT TW[ZY8Frvu[ +" =o8Pyio` B2N +kfm-X[^]-W6X*V tUpNI_I,j˔(/Oe"$6mQp't-@}*~{N !Zj21_#ZJe[/Ƙ)QB4MXCòI0}83DnrՅ0Hddψ-%.Z}UeJz_捇GZ}MT> %5x]ιY"lQ%7rYyS8~tܩn|}=gϣ6X11|^=醍Y"&RXd"n8P)@=DZ2/{YCS.ڼAQ(yIl6?TX +3S3pQlnoeʷA8!)607x>,D<conT-vhvZfKsBgX_0G_K,spU,<_Y|θk +(=HouڐՂҷ8AW7X\_:@|*TMLt#99d,GA@رS4BDqC*~<dӇ6p՛7C^"`NVE.lFḯ,؞Ɖ&7ZϓFKcC +eLu1t('7\l1Rt>}erO |]F^ +Xܯ"0<['A2CT=O1"l9aodɔd+ PkqӚ?kLSq1kE]L` atbiJ: ?blpK޽~Z8] LRmFXfbt=BϯP +#)HK'&rM  lʒFa{9b $qczýI +BW`ቓe&jQSB41Pe (74vW5ҫ pjcIX #8C.Ӡ[9ttAWȢQ ]lPɺ>l-xFɢk_ݣ#sorMz4gy(?Z0pkbj ,rGY͹0nׅY^zp8+BJ/HH;"|A }`؉ i@ J1 2H109MvX{*8Sq2bOB OW6f)2 Og2?4!  cCv  +A !*e=XCydSVE.e dЅaZB +rV: v8DҞCD-! IH'b<!cgRga|HĂ!tNj.7E6,(T:BQ$.2RGb rMR[%18IQCPb HS "JduV@5Z8N:0 n D/` R I \'@ R&8݀p&([@@ xe8@DM N@bi6psi `{GWyX0VB{ݏ6^A*B2yߢ0M(  +? +K}+SPlan +ҧMPD} TRQT,&GS1Z>x3ݪl|̶ +Y>9hf}v4X{An +ٯ+{ 5؃a1\\5d!zTYhb0,FJ[hу&z0-&DQ<ŤlAQyiyĿ!01 -HN<\0$kA.8 ж b6zw`T|4 ;;c_, +_,AA"uDŝ x apB; +@a@a܇a:1,Xua1X8Buiѣ9ƀctBƧtL #Keft]31 Ё ,pO 7.Osj!S 4V9bh,1h,a9xN98&&.l.r9YAPc8JTcbo5G Yh5T?9bcևcRW":kAG2mzm|)8H 1p7HZ~n ޙPHyo,~cy:1I1 ٸpv7?) S6O:z +L' ,/zbMP8ow)]8w6^$@ 1@15 i4:ǀ1 uZK:J9&HH: 6j19RQFk @1S^rbA1&g4`r,wg@%EUn9b3xfԘa3cӌ=Kۧ46ebF#UZy2J 1+*Ȕk@hrLY'PG|+fLYCF@1c@p119u0 jJQ15]O9vc1໨S>1ȖC#F7 +14l> cʐcq0Ƕ э~¨Fj (q, Ơ!ZqV0#Ft "D0lWJABо@27r0IN=^BH n`K>9RNP ڥ@xb mW"pd%dTJDݱVp!R9@3Q$fOD'!5 .&aLY2"{D3JaDb=hz"DhHZ"!AHlףSȦD1x="'2G#ҥf}t#A#؈35" +]6|FJđsj:Fx}G콈IlkXsVEA2"NLH֤CBH$'\dl$JGD"Iwdz$AD1 AN(IF=: IZBP% 2D S¬JVDJV?^ɆbX2Ovp". !(<tfJ/dL0! b61,Θ@PuѫH 0{ aaw&hT4f/5PX܄M.Z0l‡r|k{hBNVzu¼z貜 +7;ٚyVyȐ092CW XP:}\K>L_JTLd3;YٹJq);* @k[>.x2;NV;<9L;? +i݁dNU١;@:8N$i |ƿ$W@=:0 ͡`/w"sd(ȁda zkN6C␇lp@NHdoNF4pP o:iuN |bAf6*'d? 밁ekpXLH0H s? \]6 rrdJ(Nhdra&$'l"1Bu2ڲ8* w2yɰÿja[CXaZD6"4jb9Lq!"`CC`^b6 +C!П ic;Beb P165FԂǼP:Fk @ 8g! y1y Eh?@?2#JF+Rc2X|u2~`Dy7;/3 ,_m0R~De68/3l,N(b]7{(rfACiFiங%ی)G +8\G49Wzss;8(L33=C80d Q&h4wʲXnseڀfU6X4KVN ZrX>jPёyP ( ivn3s͠O#0 +5ۖ娑H/5$T3R5Ӎ/Z /.V. +k% 60P0k\  $W]z|Z3kX* +e.7\@/2k` 6O "J^x͐euc.NXC\ ( +X +V*pl6Nd#*hղ6S0he +pGmJ A +NYqQ@؆Y 7 o3 7DAl07 =}n.Ivaw7X)tϛY @~oO *ԁlP}6g<_B!8ɓ~␀ +f)y/F`C 8gR:p_{*rǪC"(U]'9o'gP|K92ۭ͹s% +,%@< :i͉2  =#@s8C9#`Gt(FgxPF:opX:xD#c[~$ׄKcLJ !"hZjw)ksLbwiu!!7mC R-u#<,+B67'(}djUh?aNg@>g @|LB 0sP ¶$hcH'Id'"3Q +} Aܽ^خmR} 팑'nyIJ\ Ts*#D +d40cĊ*Y _~ هNWc5˞Aq9&ʥG&u OHqCWVr@9գpkXJ61;Z%?1!g..}@)> |PGo{=Hz*gȃ=Е,O`c$aoX`f`S|+Q;Ήh5I^uf@vPS +nG,B@E'$3aL& +w`V +R!o:uu ٳ},;j{9n,rA0.(I`aʁj4~8a& FĶlzq+@N֡46Ў{Uץ9 p&6:m@qYh&al 14noB,(x^UvuC M2 eho[1Mxh@:30a#Sp, ľ +Afʀ_sc F֠d1kd0ꭤ$:|sӽDcR|gL] @5Zq\NCW@ ueC5$/pќ дܑЪP(zoKeaO~ #E#':,Qri (0T2R + KzJfnPd3PdsD\Cґ.@P# ̥## КYlm\Ə +FVw+"nRD $>>ݕcHS|F-gB *97:]Q>6B],T{q # y,|X> #l\O%ePIAW 䀮~w;%#1ۀ_lҒ\Ka. hXZmN#;hkN5B1@-Gmh^|@L{-%Xx-e=(?LT{`N,pvtToC6?ж?1Cr[CFՙO-PxFN sB+/z>χ+c+[ʴZ#aJxgP@wŁңY.r4`*D4`'PEGL_yݕ Yxgdox|&9e -o03~.ߤd*`W~yu jArC;Z7~pYuRڟY-;&LOO=kL"|}U d7|Ďu~~6; U?ϗ&z:gy4ԗd% ┢C#TѩbR҃oX&-M^!G*Ћ'~l= jRo9V M-ѣ +۳t$yºS7*|CÓ>;M=B>@3_*}ݿ4\$ւ~i@+b:ܐz}PT R?aU݅X*Y҉23~EM}-D ;mPHow S>l{'2X 1q +C>a%o~@0Gu=}1A^Շ Ǫ}OmA +2?}Q*PE1Es:2^ri|td!E! +1Y[^[~Ü%Cd_׫L*~ݘL=( Yy~T$b1jN:?*‹U8pƅL,uLa_/T19?l`D˿3tE2|~)=~mNg8˧B䣔bj](:Ri[ݑ(IzcUFC (U`A5=' JO7+wB-$Ay{I}0a8R^K+*7hlds*(b9/"~u3e_&?3nmPMx/f鞼>yG|>r||ʦ:s>o+J,@㻨3 +&>ѩ,!(~~>+mf1K~08>>e0/_l  XcAωzh-$c skțXb=j3R1&Ȃ8 F&çܫW0b, +@·*4I>tyFY>]Iաhi/*M"@Z\L,(V294+!P:BnX_{Zҥ'd c=涉޻WIu-y +"'ч]=sZ)%Eg L$݃Djk֭Qr_DVj{d>ōmN> ZL2H8{T_pGCa|j_&2ʯ Zگi_1S(%KG/ՃL̾<{w%n{R+Pt;s/8A9+ ߻^Qv1zѱF9gʞ}^RMe[/exOVA֗Y_(nK MM{bX%+:Շ=z\gQ[*h8/7zà۷~%(N:7<{&hG%5K?#\l<+?c1鯜$ށ#TzHR,u:^{ˑxs8n'h5XM^2j>UVJX٠@tPmq=F=K`G"u|>XaHMRZ>3罇#BR42ݜ|H=T}rW35*yÎPN\EoqvHfҕ7ͼ_g.BL4|5'd#zytOzMy'Oطɶ'vڣ{x[|z^R#7uQO(M^WgakpÚC"\P\Fk~<|X!23 ?O0VaPJ Hfc`αwSL]*6!zLa-娿h11ܰ4(at.ޯ-g +/d"xyu/N]wU^IZ'bvZP19!(xų& +BmRU>u ZĖp6!hԿq(&]Uݿ{p';|2;*xB-Pr뻿>j fQV{:q)%ϲ~/t*d dp:/{}ۻUU;/6k::%]/:>Q.K, զ븎h.peNq# j->kat^)|bw +Pp禳} /̳_[pcζzfy+1KWmaDrQӉB:0ok6$r}6N Wnv7U/l"d9n4P*VX{Zç9mWD,&~իnR lo=|vu%i;f!]{ .(/~(4b2ڃN +LA-!}@vHտvi' v}v*V0C ٻcK.2fzgHI6`?%*pڭl )uK n#XC^*fBLI!T96\?z.:` \ !{Sʢe`op%kuf5DA9GR: d2]g`[?ֺd’=xz)js[@>N\Wxrn]'[I`'[O.uBB{/S%p,(xIA8SR1,7vDՙXXˎ.Qj@QLX6m<2zT Iآ{'ПpACV.fzXt[&/{~]|k +kr; حOon~F|< y}s 킳ϑ$@'k\V dcR^`Ec[X:nQ}Lt~b3֭u?T MO5+yu(q6rZ؜YkN +۱lB9(iF5A |ft9WZn [*Y[(>:H0tI2&J =K^>V`n(6lsQYI\^;l9eˑj'Џ 16&˕7(&d嫁)DPV:D9*{c*ev:)J%UTr$d3!YBU%#Qo(o.t}r)AX@ft[El*@odUqlLysZ'L<-t$ .JNfy8+@j+UUGTk7"E~ꀇU[9`+_nltÖer}D IA05@^}*~9׿),IM!F[ :n͍˲ou +l5jFOv8ݮUqC}nSX ޝSAy(iM:f- ծ޽f^nx9sQ岙O[LY&GB2Fiip1KJE~ˆtԭ]Øj G|D[q*o.ix' /"J]ydU`{,ѥ뒢mfStk…&~E6>?7{2ZP5 78$ި/mU~ޥ1Y9xi'U=oyG:AL"CYyۨbaHWx6N=zdX+ez¸ @Ȍ P`9ehZFGfԆ KQ&6r"]-vk~ +T-rlv\>mEE>Xzs|RUZ]^;p" u\2 ]K{$DF:ϭ1LrM#L%lܱ`ʾu=L9cXŽŦErZa2e:ce}5B!ˠcn"m1w>bn s~Y=*]{p7N1*-ܩH1^s,3O " endstream endobj 19 0 obj <>stream +z%es_X—lnIêO57ٖYgsQGܹ2/:XeSp +l@WRc+5w1AY[!M;$X3Nz?P3й&߱hseҷ~zALVLrtnNθѹiZ!xh>:7έʭ竑 X܀)Mۚ}T2>w-sn]]w"Dh?!%&u9I:+SsK4)~%so5ՒQܺoH*so,ܐ#^'PnEɜt>#Jœӆ"9wb97HC>̹=MjnIriv^m>*b *[3jnI<'IGہ8DtQ}>*{8ܒ.k[6>7a_׫#ro:7A5;E˶IIS*XA%]&, 9}tw=Ȑ1wƯv4DYd{Q< w2ؒ%avVs8$T@i E"v2ׅE,GD*@oh16յkGD\@t{mǀ1}xM#0!"SkbFNd̒Ǎ:-+hE^GG->DY=݂&<6mX$9:tu>C*6m<󘩩>b˖_w;&M\7&6wO#HhSKpXm[m sNe _Ƨs'(>Bj{v) J٭>0[Ϗ/}hc9ˮ>jyUv WQb,@W1=*;Hv5w$ t}𛰂$" +VcPk . qn[Pn,Dd샒;q m@d+ZڨJbLȞbD6taA$JG"2} UAjjuu6+iܖKs;o +9xGy%s+ {Zu}vAunD?}*h5KhkgFCx +^׸e\ײ^R"*Ip?77V(V#w j;wCvmR/7ĆNת>\Suyf\irGR>!vHݹz+&>"`S,l@y@VB[&{Cu2Y{8ueLW!Æ~xmJq`tW^r2fyXԖPgsX{a]&#pނb O?Xs'ݨ== U=_Ne!RIMrCtD /Wt .;t*Ԧ-o\I[ӵd,dp"My{7Gx!Sn v< +!B]/OfK4ezђA@4{0%KWMG&_ƭ[+vI?)!)]ExzN P: +ԓDKkiqŹl"TBC(2F-J􎐞L,-k] p {O;oTw"?h|~袬I58MuG~_DFAͩL9hC3 ]fKrR^щn3VQAwk[N26:ʔ#C;V [H|)BZ&wjX恥+BDý[!q0-73M tK)bteőFPMpL\Mp-Ã` +z$Ϛ)&ˤM:6$|Xz|{ h7];Hqgv3* <74&d,>EX5Ѩ#9$@4@﬑F3`2h7,R:#ɦiϔYd(f69wsb7BpqyE2L hH1;Oe +Z&Pd +%8W`B"SqN">3dtgBp6t; o.R +/-U@7wd?M(\[ٝ]XKyS \TA*.utEqyHfs^0 FN5SS\ ˾)p477\_f@L\?sV4>~9P)j~fxʠAySUz?3PD:3jJgnDm =9jcUCUu f['ʹV9>= t/)`uJ1sLĜ@Q3FRW1s9@y3#" kf+GVgFQɮ34yE|D3'P ȭ3o O(E3 Z$0r hg3  0әgXfz"Bj{="éH3hXi&zIiME$M;wX,c3 %9 h+۴CtA"6JC7 tz 9|H9Ka̤/y2Ld~bNfP@*\0{\0[ 3g\/+_oH闝7{HF@|Y>d(I')XgJX>DhTk&nӭ58X]ddIZh1tdl.,G;!| Nb[h|jsvツa;Чghb ]w sgB4uD>Ut; $E+JJ 8ih?1Q`F.7%Ӊxt`G[;f@*>!4`<[&iz+n2b.%-y'-Cg)5SR<Jh;Tz߬tmΉXzZ.%޻a*den f*2`> +MsSu/i b"A'6l))LRO%\:d+!6~54* g!? ܿV'k1IGE8tT(c .SޟZ:\'(pTTّ䎇 "EUd^|Yn>~yT뚪-ZUd1jw1X02{ iUJ`Ooí.ٴ$*&!ɐB*-=vWsX)n$YprU4-E"n(R/x-Κr6׃2"%zqH*Idkn +3x>u[g)ZpqlnfKuINF +HWh35S ^oBd׹:odu Խ2gnWM }#`&0e[-+`GCvݸe[LX +;vrX= +_(/y>,\VC KFt>7iaȆK#pbH"xR5Һ)˲ʅNONf}u^aͧP fǷ3KvdUoYۗ$o@2>k㖝}4ϭnc{;F0o{ "< +aͣbqr>@gAR\X22TЍ;.~x]MkH2J:ڶB,_t`5 Jn/F3ħ.ƌO]R9^]*q(WFNv;FĖvL ݃r6wb:GޕG>ou&%}yo/+^ +ޟ#0SiPJ]/[rfלcF{.w>,;Hx%nC"sm}e ~]HE_$y]]vOы+iW9,c_JSDj$9`MwN*(#OxBKsL?4LtM]KFo j8-w2`,:xíĘ + Mηд#%yKkF/䬬a.B$:WW߽hE/X6|D=W0_ɌpO!Kě ^iؿ5 Nϸr\E,ɽrߌ09Mv3:$!m^l&y2MԳ_\.@wFE_O1Lo`ߓTJ:QwBۙCrY'歿ZlR6wJ> (jS 3 _\,d{g+wZ0L:U~A3:- Jl@0b\Tly:ɡwV{>_\&u旄aBv~dDҪ] }ů&r߫}Qv;;F=ōܷgإs_I3ܗe1~`g| hm16U'y-lo]oo iExOvM#_1 n2#$$|1/4*̽#S4J\#\|ZjpL{e}>'AZ`JI}ԗi&7=e)7Ei~>(Ά_ #"UFÏ3ʄ4]__Cܓ1Լ#3s_ёEEKrGUE*rpEK;Ffˢ-]64G5 +Sݨ.P@}QAz}Էt<@}0}ѓS"/2ҲoAеպ+oRTO`P δgvܰI Xb&d_\iP FUEwWsҾI(JMd}1m4B6Pu7 2ݙ a`?PA0@.$oR`s[i_$v}Z'!UkS(@Y$sB"~wE͇ ګh-)ɑM<FNE1Ϳ9/QJ}":*aFi 1}ҥ]|jX_ޥ__vϕ(~qYöǰgt;\[[ƅa4dJ>?ߓ5?"͈'oUg}Ŭ! t#lPVSDU3yo0y S<%@ô/4uwS +O' s&לq%ha #wKGإa ၵϨz$^k"(z:bV`=?nM ;@1qI!X}PEb$r܊Y>$aMK:n8|Xt/~M"&c[3iCEF:ōucJ' 28frf fa1ݙtqdyy BqJ,ౄCAiup7DFn7mdpj ӣ8Ipʶv䗍O }쀗\5NxbA[gR@~6䅭 g'pz=xϒvڅ%b.U!7w?8PK=|XAЅJp aB*43o4*y&8Z*Y![乎=>bJ.Y%6%۪Kj%O +-@"Y-2%TJ'K>K- .Yt %_TfJ)Qߒdx1vKϏr` MN5id2ɶJ'%C/K6CxЏq"Uy d-!sɾ+ +5 ZjXۻqWC&^I%`, 0PK:yٶXdo +6afrqHr]XN'2muH9`Lyg}hURTyh.!W_31Wcy}3$Zߵ<I']v2߽q<Ba:_3}?Yu[+23ӝYRP"74jRR:I\ lY +$-!:7i&L2v^;zYTpY(۝bqŏ)Y!b@yíOr41Z,kThQfP3wSN4+pW4a+Z&Qit1b8|+q [C^beJ!š-]Xf<i +PTuԤ*Ҿ”]zN-mPM?&˫+\lbEȤ݄ 굩R8,!5:E/zv0A,/r,N{0CqEL-&dARԹK`VgNxgԢV)!uo̐n*\!C4ݾt哢b=Y?iBAa~򀥬ZU0۷FH=ӧ+y!ܽ0DnKwRo-O*RQ$ ^Է`s#rRw^I 鿗;_jK#fR?&(@ǥN2Hg5lRq^TʏOhcROd}ϒtx@ZO%@.-u@;eb(V]퉵w *IQRcH!yȤ^tD4I-݀I9 @Z02H*I0 NdM Oj]5j=߆cªS򨑶/G ^멬2:1,<HjǠWzCTːΣ!6^3~G o[Ԫ%E}cQ=EkS(jFQۍ^ 5[F".Qrt8=P^w[?ϪS[osFF7'X v4wkBv+/P@mB[+ O3+ Pp,!vzϚU@_p$PcgCx*P.#&M jר7:a[IVNSso<咀:N %Aq kURXt/J+74&>iY#NLS@*Ա1z܉Q*V$uސ +mZZj=ޚ3p$`?%" vc[6u򷡪O yH{mTt;Uv j n[<ȗ@'][GA X O-FֳSB"(lٸ`h4h.[k UZZ Ecw+Fym~d6k, +{BY[4{lZ +(Z`ҰMf3^g?LV0tGjRQfDFΕ +xf>^!xNmm:$$7I1YTAuќښr_?us];g!;~X=ꜣ~vwtɓE4?u<woRyfjm:m@25$PVxݭSsαE֣gsV,H5d,HYzW\s8i%5ŵ]7xO[u Zg@$;z\\W/W*ݺHB3K$^q R*'G[nj| + 7[/h D__d[ +['zNICcwhzy]%5חvl)[C h5ݾay@ška RQy^ys/pR-3d6ۣ^y؎pd+Ʈ{krL014,~\ ߠ[6OOf' /@m_g˞>= + sjsqI%ٵ{`#XE^(Mfi{o99m.D a=tu;x?h;!aq/ +b}}W=GR.7TGL6߇^ `|r1TKpaa~[0w8߽ B9R3EyIOx;o!Q?5wu>wxhZv a%u[ %f&8 gX!<\R|k]xVM%;\ȿ<~y: R}iD{~a` B#Z?qՉ0-q& 9ҎXǍJq OLO`X7ߟJErlbXwwͅ<ˑ9+v.ЃaM7(Ls3H ~/4̇"+(sqg_+6Ycv5mOӃ^9t>r9i@t@{,弑7:cN)+DճeD!zr8-L^a깎'Y뀽Tzvi( )/&#]LsC>8 T2 Aj58+wCMMe'sLmp$_ ȅ6A܁IA~iAW]\/>Z>\9`F{5NyO !Vrσ5+* +v, +b@X qעyӉre8]D@sTH&l/>RXįǸlp bvV_?k8Y+3!z@1AbзPUGx*jɔ5g` j~|ۘ0< ht_6Pb?z (\Y=ȓ[`n`*) +0Xg-o1;̀ټkD:?=iMMrQ sRdSf>otUAtR ŕ QmOyO6( ǣQ26R}gޛW,a&}&:^% +{="p*'=@N{H֠II}UkOr-ЗjgW[w;z?;?h\&ޭ~pBHũ?^7ަ؍1J +JN&MDW[>>2}+sVD_ͨ:"@wgxH58^)|t? ɞm3y5RX&e8 ,', +$4e i%tȝ[LOrn1Ue3n{i= ip)~hr>B8nGL\ |6$ p7_bX&a,bZc7NF@WΤc!eqibX D}UR iV&3eչUe.">q}ު:lB) @(y{4}ׇ5%/~9wZBV/d5< :3ڃ ONeþLԊGG&N?PzNgjluFH@~91tf3c_G`2vnZ9{k_ +Nsuc8zn taۚ7~9\ öLEI y ꙿ9lكk|!MTi@ZxK|ӧVf/NH~"=No9g3ymwVoU"|ηt> ݷs4{mq5݃gcSJ3|СYb-8؏"ߟ0$tZ)sxK] FP"5:Lggifû 緜zYӆmyK@EzMu.#utQf}5|'0h_Ḭz;:gB9/gF'yv^ Nb?އz?.$<*BYȦc_AzS;hAYKICI+D(~׃&>ɿڽk$MO\A{mL[m(Wt>=aZ̵o'ήt2wi3NW ei+u~fqs?| R4(}gpt|c g7hO#{xz^z܃us \}#o !炍aۺ6G,׃0g&X豏+;h:ÿ)4C!J=xQ*e7q?k=2Yԫz84i: w3qqu=&9uRT +qs4MbX!ܴS4 ŎĢG@bF&JǘzjZ@v}~tZ=2k*h;ẻ 5B#Ƴpn`jТ*O8*GPA}~ţspd$tf3Oޟ[gh¸ gp \I,w~b}0ݳxbsg y;{ޚ,ӗ(VЭ*j‹)Y]EF=jY;X`]zN >ô;:;Ik0!i +=w>b kl㺑gwB{%P_7P!l;Iױ~L +K@@%oD}AJʆ%}@5|8}>R&0=L`)4\L,}$}(m qA|7)Y n-H)vdT;'>+fcQY/yoolsW6|?ЏHT +2{i3F_HŽN)>'t굛BxSLXG#HXozY<4gd4{l]mv*P毭;{y6h/xX;!Yk!pN_F##_O$Q"';jW Ǿ߳xu(h4NG~OiĻ( m»I>p-wU +D6 "N"ݏC5ݵN] Im2q9Yv#y¼4zlPQ +=j.ss6i}focjny96l+D4GND)8I_ opo +X,3$i'=^opF0,q~Fz{}Mu]"h'HؗD?{{ EG9 IZDc0$tnB =R$j}.Y̳FvGP"G&5}p9EAz74xn?zO7s'waN ""I߻21>xXž >Tif웭b0teZIA-X#d- L6|z!7*lHv?8g*[ SN PLg4yxE5~o#_s@$t>Ph49hME$]4I06gJ/%b'(>!ߍC(ijC>{'|ssx4mN]} aʶ5p3&g +-B|F0@:xt͟[]y&Opbgx:A ;T9lP}q?jO=\MD)(TgIAv$ߝӥ2ukL^[|~ӕy{ǰ%?|M#Xw Eq0$tٵ( 0!=zgܱ2wiϣ^C_Ḥyܩm,4 a%T%[/-#rQ$㭛T䓆 +q42y5_#qoᜧk0udۘ<3l;M`689F2b~+gmgdEڦVإmc83:ƮL:sQ#AϠdWb2yhb7 H^]me웽}aYlܭ#edL{)^ضb?z|G.4st5<& 9ߺn3mẎIB豯|#:kfsaȴ1xi[;r'yv"Kcvj'T9޺oi:kW#08KPR)8=M4j0m!I@qsq]!ʿww6o?9_*!h3 E|l6o(*$NȱuΓ׃qse +Q-4 qs}OmqMb'yvR3!9Kx# Tmܸ^CD> H@ӝ=zA~#uo?xv\n\-m~Y,7;& @|F6cK,Fg>A~%`M9XmNc 5E8 J>ϣG$u)>BOGeE"ݗ;aȶ0sfbb˚}/kCgQ yK/4eֶ0teZ::?z'yz=3|lb]?*L,z!zG 8o#kN]ILFح3}m`D"vbC;;W'k(5z`j ?|RKEIRU VD^T T;[qq~,q Ż/g] +EqsP Icxqq^l$d/IJ_ +Fzl8~2=V5.k#mEOaq굟P GbjԻnܷl1we3 []NŞTXL)-fvՏNaIgb/;t?(+hXcȤ̐l `4zɿ[{y ~}"JxO?Ӆ:9 g}ûOGü0pdNE?zȒ'q}c^-65oNa^퓸1>|Oy]ݓ0*?z؉,N;vQ EE?J%cg@}^ǰ8y%obMR$=e +v$/F]*cd@Ec2إH,z7A}?iH z߻RaAI<w*H^g0yE;SHy do +-D DSh{栭 0mFxw#azSCۀ6 gMDvQe6pFPd99{G&C}6R&鵍$ Ǿs7T gt8[*M6o5CgFèݳ]Y~88U5;$4~%OM_2?6o]&R)?sh>5YFMt>|u7 9u4kW4ymun]كnu?7_gzLCg6@Ōξpk-_-4iּN#al9LGm@&p)D~;xAy ȾcXg?;J7Ed:MJ*7o]J`?b=J]48w%o" *ʮg0E_0?@{簮x_u<7ӝ{~^\[oO"߽굥F$ R<~(m(D[ik+q~'C)(3FFQ\M$g@66f#iz=}C ~ꡝD}@r6oMrywF>{6R& o>Voal9ƮL;tg˜ކ<}E;?ӧF|<9a!;D}|NX0t S}żڦFشzoþ`ʶ1rj3̛vF/\4}l=fƲ[ڽCgj: Й2rl5A}Pĕ0t9;~Nx }AODIXCF03|h}+w8rO-$g~ e0uGkj:{r {9ӊݲ [}Ӆ5k gh[þ,ݭuʽn3m]^豗4Y,&9rLCVܝmn+>Pn_̶c gcrLw[:ٻ5:7h.ly1B$*e~L\ӕ>P#߯{=z {\}#8{{^i{(5Kkjl[bnmN. ;VNAf?jFϢ C?dgGP k/}D<3HW!LAǿM4w>}GLwة1slsbI5o}A}RhF;ufpyJ;)sK$h7]o'eh7+ԥ2xft A~Pcw:v\#?N0usN\D;f/;n׍ ƥIx}ˮ} h6|qZ6!ǿ;ȑF$F7$o-dgs& z~NW3}ktLMin^ڙLa+wAto r4t.ֵ3hFkm]o;|q @bظ.<{׋YVem,c`/juî}1lYa$N_ Y>wwwl6_d xC"後fL;AERо4{p^Zr/e[-vl#m ,Fܱ3znsO"0qf2rƕg:ceh=wVcѾ/ivO98utbaԳy +woB}dU +LߛR}3(m͠Mߛ*m$ٵkM`7 |4l4܃.Ёc^dc_1}eY6&M}a, '~mor2L٘ Zv恋mǸ4kn ソ`o;Løv^ZvFpm;3!\gjڗm//հ/ka ^^0eŰlKAx 2tqCVVVUS<3 UM P5QMTM dU5TSMTMTM_n 2ipánjJ sgPGu,S:rMTWU`aA3kJґut>PY5zU` +˪UUVՃUXm=ں²j`! +4x,VWYUXXYZZZZZZZZZXY[@ TYi]Yi=ze%Ճ PYe=zU+,+*YΚN*,+XWt2h]Y5Ҫ`ՠ +*#.e$(tjNA1zcS ȋLT=(V +Xqr% ˯_40 s@4a&Ep^:ָ Oh6@,c 1|s 2]F+Y$-dd qnPɕS@ʲ +|LozB0M<qm 9p"4[BԒMc^X`*^M?RtAy !:xj)v2dkMJ',ZP/ed9L RKp=B?%:Imc3@!aLPE N [H_% xTMo)G>Í87H<'"r9`mc|7q ~āxL|(oJZWK0BPlR^'Ĥ~NfL--bJ288B`s + YĔb'(=NQ*X![Ēs:|$cR4s1g&JL16L-)[^) K6$8jO Bˢ8+*Εc"\q/ +SƁ'r¸=l^bY#Kk$f`MbL6A,QMoXvăl7's5ϵ!UA :.?dp6at҇2U4?L/=d\}8 r0/HĞ(u8l]vjܨ0b.[jT,O],[/ } 1,RX$HU\c BD~qN~ l m$Y+3q!g!MN׽(r-UBv'BF2zS5Jhe/)F׽MV4w\A$Y5N&~/2;H|r< =mr$ 6JYҿNxG#WnRN'CRA`b"!Mm2W vZz+b~O@tH@6<@7<@`n 1qxk[1d* ,ae-I'g$'k8*YO"cU`Aa?`L1PC̱Ւ S@$Sh.cpc)c/eyD_x%K', ,i4mJGzꊦHixޏNܒNܗ3E½\}}er)Nӈ G*fX禢GN#1yX@/K111̛(i6!kR0I +/SKB$$,ؘkcүɎI%!D< &]EPB@"y xDV*\^ 8@{v2{Xd24Us`؇N!%dcc_,UhF"b GDšD38f"|lDBk)bE JO !a[9/BPdvq)8Ć%a+ A#(*^"6q4TFKF7eǭ兽u]3h93pY9-H1{Ѥ, DH+͊lK sQƬB(5,RMG#DD3CL3FH1BXX +1$ߒ~MxL91dGRdHnZ>,a-rgP% U5Thd[$lrvh"N)a9HHH&H9/ꁮCJZ(n8IVSBb Nz +Z˓:ƕL X @òRa\ظGnH $  [ī\(k(gW*.ZF'~Mݚ̰ظ̸͸PmB`.in2T,1H"fȧeJ$#M*5$N6_be M|DQ8Q!(=.dIV*\}ΘnUr]d\fxDXn1@uk%i [Aas0u9W(ٽ%d_2OT*-וZuS!XePXa 3X[h+ah1 s +iPQP)$po:$ı3\s)g7f9,@U1+$abLόQ%MF+uIzPqP~[1Вc+GHxʍʙ"*va'H2R: x +Vm#A"e Hݨĸ(*C\Ka"ǥIiJ;Ȗ{D7LV"[ܚu ^!)0BV7Tv^.tM7.d)ei6 +a4 Hps5J\)]I?1qxEnps,) KքsWm 'nNBMD˼A m ۉDchy L(P5^fmyJ *j;]戂&CJY[ubL|L? %{qa-LRhn0Aٸ$يt%sp!\).S^'(׺Q4kdIqA ˴ܠ"OE$\ -j+"bʛ+fF\_$J`"WiQgA,BFAa-a5מ ;%F͎ C;KŭVnH dkM +aYF_`nHT=(,#jݔ"4 +kGtL(] Bf vtrV|XGp%X2ٴ2] 8}3 QٓN[HI +z~$]ǯSb{ u)#<"3ƃ˽6$'e~Nضe͆{IR[.6Zu 5%t 2ȕ2S"]p3KPiDRH0r[d+[K~J:.cl/Yh4bhJҹUB\.-+3,4ؓT?&eWw\veaA>Z|Ia&?Qz"#(ʲCRaZ/" JT jݛf-l[NSQQ(,Ne +QDe]V-ٔj!Fb)bkʐOƖLvnٶQEѤ໖47ˌJonAox! \̕}r@rڻ\$$Ԍ0"A効~' +jēB9YJ$-*"|(ۮTe.f~9l iaV~5d~ßm@c64rHH&TS|%\MQ.U`I+KuK XeT3-5+yY(j٭-[V~/) dO"WxX9*"tKA +> X/ؽv+LXYXx+)~~~K'u+^0(4ژAk +C1o 0c5V!VS@ CVRE~u ++%J[RIOXdNhY>()S `(ZXY[p"J7(K0T<$ 8Ր K3«(3G(T=.Q6gr{(+"̚il#}5խ,$=;1ѐ̸{VlT?&*!s]eE/sPRߺdIPܾ +_C)k(+joOveFaU9m,ZyJ7Yᳯ%j/,4))BM:Oy9OyЙQ+)Is]\Si 3)rVLځ JjaOBWɐ P((hC9-Q +tقkFd"9kn\I]TNi |]ZJ/,)J>I HlE#WPYO@:Wј? Az7g[f2ҳOdl^dS̀N$z'%'='P H&fp崶ܸ\eZ+&zYt҂>3 L,]Bp٪Rf +BgJ=yϪGcmD7(N ^ fU Πd7$Cηȕis6FL& hrLJߞg?6pNSaȴ2}j:7 Zon}/m6|8a$ÞA}P>DiH_Do0t my[I@W;0i?|sЎ"pF2S."֏YQيF&-E±J2؛NU6֐OyH<7;)gx*kxrJgФH*O`3XٱvdW4*{U.e#mzp\X;2g*).&~ރSO'g?{> +bZ*{MY1$):V<*ƤRf>@*>Tm*^ +Ɵ>ľI~(yy^(@jqFy*?jFS,U H[FR(=*(Ŀ +Nb#?zs'쓸l9[.t#0X_MO&cȲX?3WA2vnZ9I/y@Z/mYH]` J"H3쳲aIcZ;.jXQ_ZM }0GЃВ5\1L$B~_i{#e*xKY3X"`HןVjdX66(=^JĢiM49 Z-w6|ѥ`}J >{͟) E9"Y}C< ]AǺJeR5>m$}Gr1j'&>IհܫX~Tz5 eT~~R=JLi FB# uT(CR#= Mzʄ?Kb[3p RxP+o$@ _1>{a|GXX8.k" +?$_-&c챗z,McxG4?z4@#_IfHR_QVID?>/(2}iۡL@J>u?oF>o%1Y'P`$A9K`)G0Ii5訤'- / 3 E@4T*3yk͟![`ѾAMIw6sg'uxQ,i<׶!luvI_p*sز:AQ^VN}%S ĺ)U\+hX/hحlX B0A~N)ģh^KV HJ]CQ%z[h팑3$+{QSNB$N??zFnrrT?59t*D(~Ұ-$gĝ0m\9}N9x~ ٌuZZ$`P%$m%&col4=2gBPE ŴS8"*7v$M/ zSX:*kVN@|*~v 3vl5g-o.!$ؿ0>琁uqYp\#7P8rr*a +bI@58ݺ~Gy5&'izm} Tqt5(#޾[aޅ&*4? G@)sq!v RFk& m"L@)_jʾCKh够P$0neޚcyudKqγL060jh;yRwAҩ}0Nwk:zK'}E)GXA~I5LC0)orJIAMREJh=ǽD^ɨ$WPn E>ISLأD Y622*{ ݄ Fی#HstZ I@F,m0rN$[H< [Fl)np2@|)%dZNV<6I_)U\/xIsmI'/ +zE j@UޜXpTWA,&:Db:*c*sRBک3Rh0w,'ӂc9ẹ Gc0uL`6i ?}4ML#&HI` 6tffB ?}%Ѧ쫉2 (S?0A4[2u{rnCv rV{#ZKeRT)"KiTj\Yr[}1(lW 8f +DhwJ9[bT}*hf?־C[)&"Ko}@5dUfeDJ9i-0f reYf]O$#>ҙ*e-%±kxrJ{=Y/$+4) .Dө;xQIKh 6~l76?6?6{b_3؃,f4,7sǖ su]NmE|ւ |5C_QiwR2jĦa|;%sǶ}*XcH`U +VTI۞<-Q( !hRpfQf.X ^Dn[y!_";Er;uteDbb檂:M!QQAcx% 2e368 +ݥn^R K_ڧoc/ntMwm̛Lɻ&cz8`0pVi5,GxV`jPtfTn---5VtHKn;d9%, %,Ƞ0k;F6Sx@2ҩM*S3 i EFzV.0;Re18n6CW8$`OPFIK5nÀ<|*0.;GeAx \Ù=PQ( s$3{!lr4 ʹNJrI0QؙX i[PVe*e@[ΰĔ&>}5+t%ڟG9PŚ*݆OСu?Hg 7|t~/mƮequNc̷x +\VޅBW;ӧ&ءigݲ'1L: Φ1 lB!,,5h \p"doKdXT4]؋Yc('c"cPY2,R͵rC۲b{a1HBjv`n.gȾHN +P"g,sWU ,'gaˈ슢k2Zuv&YG`9'~iw/~i4k'eOyQOj8}2zjZž}pBZ% + Ӱ@. +@-. jB$@Ri! GUEuu|?AaEi\8[  0/lQVB pP''l"-HT';໛G|wqk Ye~OanleYOk/8=\`v&?{ۗ4ؽ9 &z7`R c:(&:-A33+(dNBT`\\P4i8`ZRa gNE>.Ym,iUkPbWKFX&BEY\}YPy9 @8i4* nP&6Z{Jfه(])3l'QPd XfOe;wf0h0-1L`6`<@bp{x- +u`ean0~qa,}xb jl1/10yllc`]r !JivLd=uu)'H RI{mIgKXܷmȌ;I^!I ք0TVwf(O-uiWp2*@HyXZ7`uYNg8W+v^p5Ś`QD6u[.a wia .tqC_0#~؀,xռ <,8ܬu0a,Ãl "g;Ĥ +pTF[ZtJ +ӞİV?Z'v! !gqDjXB8F_ڨK7n4B*]PN3hD:X41ygY:{CGF9޻z/m`a^\K gR9x;Cҁhk\#}m}|0fb܅=x c">d!`ć0N!aB,JiXY" \B%4! +Y6s2Q(\UD3#f-:VQ6aTJB4J +VV!g˘e,l V/6+:8U;6H4֌Ś6WW=d8G'DEhso." ԀFv4Ʉ*3P(,]V*sp0ajbQHʳ+ne[Xp(ν@Z|eu) ᘥ +А #,ʪ̝VfM;C-2l4j pb ޖ7ɉ9*uRiFpv +))bEAw%bjf}8&Dvc7w9tr$0aO'XuWAHTʦ +"R"F& rBO`P p 4!dV&ĬA1ehk\dCڸJ^&ZRhnP*߹zVݕ +)eʨ"P$?4چ-Jh-}ŕ5\ `R-w, +c&ip]ɼȅZUQG21ȉ߁yo 'n+˨/.\0DxJ#ރ|;sG= vL +2cPbQ2*P!0~B WRHsf,i~O?YVՁ,V@}Cs p,bxˇ1cܱ_F4 za[|/8i0u@lJ e꜁ܥm.c|AW-J.zURxK*kdCD B, %bOtC'VLJ-2?zfbˆ0;hنp#utzHĵ0h \"(mt/06a %&IԊ)@8_ +8wJ5=< YBU=n {rvjVxTv*1&f>,I +:̀Ƥ\%dSۻ; :c)6X2@p(Y/-h)ME0CU\hc&b A>!`Xɇɗq !Aj<P P+O9< q h׾TN%4Tg2.-V-އrN: U7* NJeA6i>λ6xqV\X82 c $!(` nl%k6v 0j삃?EXFo {YX;$ĥ1@ttYpEb x !m,F0G#Ha sCN'fpT"Z=|Gp p@6`P*fgQLBU4(k=!NI *l#}P.E"b ,m?*b@$ye-.YHH ;# q86ɀS EĒE@%P Y?.JhG)LV4gdI#8t\lEXHظx+^ ڡ| +h )IHY.l6bl+(ppFG9,|bW \nx^g!TÏ`` cc`<ݐYXx3dt\\0݂ܦ@?/fI1)WJB;񕯰^d0` CC6,ViR!ɽ[G &*l`װ4fR F8̀ \Lw1 EDwC2xLö 눻Ȳ9Iiċz#󰇧&q~(c&_J8 0 +y c%00GU+"Vk#͐C,bb9! `TLAr )ođh) +0蚀 $[ `JA!? +qòCe KO'w/HӍ05V!F +50r||HBraXp0%5ai,I +148ɈO&N$ÉbxRW ;88DGgC<S sI!CH-|1`1duTʙxsՉ*iK(KjY]0d0u4~!mWn;(8!poħ~x%N&hZPoV5$!q/T 72VEJ?[`abk\@B14;| r Y#ᡋ(dlV8<pT Kcn^'j>hsG 8K=0iJ<@@+! FI<Ȉg!q"#U4-0 LF“Ĺx$;@h:6~2'a*^#WB :$K`*G^5>huóǢ"8@Lk\1k&X0(DZ\./"8zB` +xMK= JDxLw Ǒ+ D2D4ug&L"қCH7L"b&0oLF"3!k0nD%ZXkSˉ[=q/'W4q&u[ q=A)';8EɆa8>&!jx/:܁D/TK!L*[wpz:\yA##!%Ut12GQ^6`-)J%إ,0o(*Dq8\6+ !+ #+R6D< yrA|a ' y## ]H4V"`R]E\΁nHeRI ">Ljo )!@G7! +r87>p,3x`zy($ ˃f@J5{^lfCsQ&&$.)q :O%ɯ)^#Hx{2F\BPB p"{lj 5\pd4 B$Æ_<8;~pv 1G' 688xq |8Jz{7,_ʉkH*r g@ĥb8G{8:(Ƈ7p.m`xD0pz~y&b5< GR):^MÕ`xы8|pq +ia#AL1!bZ!Ĵ$Bxic'xG^ :|u,QCӰE%4Nj[q )G>ca -T._>>3;bdz{I8$ɧ7h!ϯ&^K4;|HIڄNǥIOWScdLEp[ +!.q=ofc0EFJ4}\x% -h`%~~IP!b sH2D7M$B1}pj6 +a$$mR +3I\&#HK/VA@0f[j$Hq DHIƇqqx 10,4pm <"p+aW1PKo+KB")0ER3@j.FnN_.縡Yh&XG M^16 sꐡ)q"gd!Vx+ 8fHsX}X  hZʇ1F ؖChv4q!cťwL b<81 wfH,qc3S'gO, xiD + 6`LK +Wa5p_Epm ᜿År +VI825ȍf}ԛr3IxL(p@8/?PXvy09p23N"bJB5*,ZձScS8W-XI;"djiX 8".uvpBu8 1|9c9 s9nq 2c9 8<'ZMc:s:N[\MXTUԨaQQMmaiMY]MQMeQYa]YA:Bšzf5ufv5f6uufueeBV**MUWXMdP:⸰֨ l=J[ӂ0eFj*+kl, j+JJ63gUc3tZۜ)ZTδΪ֦ Le]MA:˚BR{pU%gm3kʻjzЀJ9TdYdTdRM4x?ܩe;:fyf溠ZxH$?V  _h@TQ~ +:K?((\#7?`\"FP*h1~F!I?nA,9V42iPbf" R@:i88k*tU Kzc*Gs|d`U/&]Hv(_DI4U/}!η05a!˿$ X0z(Tg{v~^\&fzH~І2], X0,1KIqF/:` Ǿ/Eb7m0ݏq;ٯ05s^PhS|3z GWo=b;+?Ա3> ?Q7o*e:M6~r6qaʡ)g#HHMI@(s +%գSn:6孇 lUò mUzikoƼUA @7$J^hG@I`\Yv.:\ٖcaswCbDH2A|݃gl"@{Ϭqgn :#~tz+qqY:< 2$?I>#aI2rkD +:=} HϙBedsv)ť.0 gC*~74;V KJfNŤIo!ȬXT;uB(~88k^Jb_ RDj/ zDZNN"v԰' YG2tK7 qox%Y@6*i"3HG8บ3k5@ZG7jĴwR%/ QIe=eBQ7~kbqY;z&oII4R}*ţ"?*%Nqik3X@V8ڝdKRgjETD +% CnC>؃,MX`Pq(5numb/gZ1[);w E NDvJ;5I2vi=Vex+id[Z H +HFe J)] ]M`.8.h2.&>juŽMZKgO$g3F)8xSxHR[4I< +xn +6o=fs9uh3@x_PM`n䠍$ J FS0rme72f&r2 OQZU*`VBv, ƹ`_0p::(LaZH綋,$$"["*D)HfdWㅻ. ZG."=*U0o"XX72i"PE-5ai;jw U0";dD o +@<{"AEB<[?.z>q ]Z=SmNح /pjԻi<=|ഇ_i{̡Ѽƒ&tٲTApP$=^?j0o6.ͳ`87AW=(u`3K˾u1 j>{D߆̙8qu]⿚I?qYC&>xK0aXT-tDLBۨRz +D ezoq3 jh7?LMH>1Z&n#A% s^,<eg9n= ;b;&l~AHRCs$QKcdE=ZkUv*ln}u T8~$6smȽ_b*9 7A: hS&"VK[AGhk+ez?zF m-Jn]{>u<^iԾmv6pe\PBDo_o|!3%Zs''(pV}1^Z?})3isX$@/eCBKr&a,Nn4o[[Lۖ>m%o3IU|ȧWk(Yqte~ +~y~TBnVIi{ݾ\O>aAGv [0Q! +W4& FPOξ ݹOVs;Lsz,גZGΖk9^8 \mt)\8- Ho8t HE4'ZdCj?qY#`erXz)VEhRGw ґIA|PdVL$M1on2aޑݍ}cuY-#ahƦo5x9fq jx?&$MCK-aGsr&/4[]i82ϭ,;ÖA{FSuX72i,(fTqT1A:lI Ž),Y[pJcx%9@)#Je_1{ףV ǗZ4l3T*ʨא6iպMq +~$BkH59tX:K6kxx2zfP%_2.y?:C 6-~{Bl{{)eb<]SH *iA NE\L ]ٖRV9*u?*E{;W&G(3A(N:/zCc*bwΎ33|g>w7Xz ׷2yjۘ=4MMBkj=;1IX?#)3@yy ZtC,Mbюn)+cg.ܫbSF$89V +R&}R緃vD hLȶʸh@[kq_h*'HXK`؀VU=(g +P@g)t +|h݅Ĵ:byHzc 2vf5|=:`; ƢlCP 1zdOdgAIOE2zf<ַ:vAEۃPnO{cζع)|,]A'ÏD :~_G,aʶ2|hF9*"- GT*gj٩4<ӧFü;_<{ h<|\\GeUT% A$AcDR 0H [_LIUl|cY݀}9I]B+:I!ɧգtzNgQQJ\L›^ +%4=Qu96Ϟif%QpX Mt/H2оAYw82j[HZq +ƺӨףP T,oMk;|ٝD:jbtg䗶mNnI{&"OAAə̉ +[ n=R+b}P'{(rVU'P4EdJAٱ|T[A:.#FJ&vD*BJWm>LWCD +E>{WZRѻ+(}@b7 XO)(1Z"J'm~`[]#, +D fcW2N!&!I|Uò3{Уo +DDKF7(“ޠHn}:X'zk,4)5'SzoM KJ'4byhrYXvV {vQm+(eDF0bp8BTR\ӅmaʶXw̆pN> Sx%H]qX;{&(aXk*KX*+y]uCd +%hmX,nHMCCQ:Qn-hU3.#VBWHCUmQ&!}@uMZt9))jq%F OHx2)1:Xs,tSӮUz{!9ZAg:8*t! NJV;: X@F8L@``dWX8D>E.g +QJtp]3uYT +@  `S@0 \֍Fɜc1 ӞCͻs*QfϚBM{!c̈́aD +5ƻٺ] {su/9k3×9[&?* o0i"\23dF9?{Y973%~sI {f'x >#NMtBĊn/K%B\>"s__տ)'l_gY)p/MrNb5F}Jf]M'i[_ͺ].Y*%QAR$sq$Ye5qB?wa{wW?q}V3PcǮ:dɷ2Dɏ;a>勘K*O.GkG_'cmpOW% ͵ +UwSyH/?\^iiV/9rߑ/~ɹD/$DΒТNo؝LB>νϪ:QW95u>MlezTFy;0MarO AbpM<*Yw]G|3K +V:CF>¸v?'Os! /<~?}1qcYg18 ?é|̂ɎL^^> Cqu--e+47?'8>l YYrVY0 1?)?|ōw8~%!s[i;p5Y#?>uۖu{^|.16gٖۅZ ÁQvmϋSp:_8w&p\w℟5q>V{5m/Bq3#mKa֤I8߉3Cjq{B4cN +`ruT%,n<ԝ`gϹV?1x;+~81o㰟FXB2 0%Pι,_6 [wU y lI u@a<>ۃ6ÉL=jO[zKl9Eq?a?aaVR {,6 ##YE(wq v-qڢiخ;_]/t]Ǐ?B$sR{؟?;g^x/;w =I=֭/bܾ!l:1CiO=c֧WG4*Jv}9 j\O7Ȭ$m¬;]{"^ rLhト<[m{~o=̎_'k&}!?{jcT25?|szU8oQLRBp g^MbS+0^`Eykw;j>o_Aߤ8 z]=č 1KiBm7e4էbrI6n/lN"ykE(kT^4Э*ߡ_*;yst}<6.]^e}ôepEpL9ASlz!a:Vl_=vg^l=Ύ-†х$r 0֥l4ߜ"2O7wn,o᧒x&>qUS+c >;,mF;XX}OGY?Qwsr\^-WOޯf KxPJ][*Ʈ͆NCݺm;엏%&;X/Ә<$^h8PWhQLb@ggha[عAy54km17OEL}|>=22H8*O+myEv^ƳFjP]n˗,"_U|=hη z,Fƚ$SW;T鯛|a$뼋Cb\[ʼn;xu䐹~IRK9y jI$޻&ܤJ[noџ?~v@GfNtBaR<rťA@455/iIubjɎI $1_G +t]@/A^/\,Vۻr0*v9E`{ w{ko{FBka1.,Q'm$J+Gq^3>k7?};κz5N{A _T9DU0: > T!5o{oU#݉nLGPfb*, 1#$&\MwT3,= Z35Q7c&[tv즸7  '_u3ZSQrt. LtxMhaNJH6Ȃ*rCxl5dOT1$s6l| ]Rx҇I?Xtf$eM1y. I[_uzEb + CYΆ]sZKApkGb~/u6fuRF \#97}Ye[ݭcFKWXRÈ__o%ͦ$ +MlMIh;Gcyr ]z.sDlDGN{d߳R⋛ief+{~3w%hNSq 2~k U<KB-ɣ9p EcΦ07Guϐ:MՌ̹aRkaye4C%j,CDa (ڭx?'ri#&┏=o6F] aQa4 +)0?J&P8p)RRI){ā4}鴓><}sg2R5Ӕ2,.`m +?0z+}& +" *3GH]35%{URׇ͘(CS21=BU:'x9icA1 5E+3$:EP͈B8b%0m\U+ldě*F`'Eg^{J&Sں@BJM+!>;؊m@T`z9 z{ƛT\\孱%-gzC>P'M2Bo2ks>{n,Z>Erk]wvݵwV;bs9kk+{9[փqV\-g  [E"^ d5l5aiȊ͊L8wO:Ozt\7[7.Fdk{/x*j>LM5 +#"VKX,,ybUg޶XX@e<pn9cDa5YZ l^cdڢbV4=DwZ?wM:isNƆ]*SHdP@( +FœMs%{@P$cP @ @ P (X .pQnpym#ugXŶR+KDmӋh+rBh(r1AcC3v䑶A(x$8d[Sj-7D XB|FphGv_['ryP2yoϣȖ[ xIT+JGۿp{Nl=qҰR˖oбsypn"^8ެiQ#U&]>6gOY(I& s'Β|nbQB^s >+] |V P/Sw*\+%v<҈&SC7fԦQɨIGaH姖ϩ LCSIB2&޽ +EGM#$ù}3-sm貛p֪11I`529;P +vʹy1<:!l>7 <-1*G)PY2 +Q 9vGD)E{3kQ9%x^Coe-yhC_d>ȧy +4&4gBɹ(8aIx#oGfǁM/ʺnM;4)ס& +]vG\|+&!b#Uٖ(qatw݈f t,JltVEy-u|A/c蘿#,=n[#EqP.y2[kW޹Fܶ!%u1lt%5 +:,;ՁMQd",u⠞ك!DɈ+c=J>&1ǖ!ч[yEa:dOY;M4v܃ߕ+s. koM (u6Gр(I$HOA36I_uort +cexuhbZ ^+C_"w[@u z7Hyeհ憶Ӑ:?3c,41@bfqh:``9~I)3`D+M"ti83ˣrZ =|2//[YsЩE}O^ɩC]U>P>=*Ձ+Q}vJ^1ӟŔe,],wWBϏ>$*MsdF<93ȤF@fW_n. Cޢd_a6\$"5:V0 c#DzӲͲ^#'%8@/p6$ԆVv BKqKQ0QB8X:_YLW{[ E\{ 搩IemC![QWHL"NE<`n+rԧ ]"1:oo64٥cCl,ǏIbx=+%t|Ѿ=GcCX֟x#9k"-!H8ko[Gy9? +AYb\KEnːK~r^/! A@fJ^&s2+Sܚڄᘦ4a&46v+ϰ5ns=euS2:cu$k"ֹyB81坕C5bA^,&( csQ̖jLԵGk|hEr Jłx9+VAZ2/ pS\ 72EU7^ @ IJvvډ^?y, +Zn^6Daaիœ)Ag}_,Еn@kS3P?1Cy*!* S%~,֮gkK@'Ű3,=d؏욉[ f]9IspA5s&Vdvc=3Po3M^/Tm3r}b,kͭd2 BEBh3ay1M&f7[ȨLl:QMqi_B׸S` +O*i4,<4~M4O&/ZubZkuopeRcDI} +PzPv +3!&/e2Jst[tI( 6QtC#LX-v([nmL%04AKj6E$K,}!~F/w9םyim-|_؎0cjma?D/._\k bHw(=b]NL4QabDc^b>`#6PP#V]SzwY<>h }8iG|2r 5qK|+Np)bv{Pņ.^|LsެLdNUrShWQ܀Գ.;$NL@Pn7EhFtg +LJ(T ގ*Nqc+\:yq`Qy\mMl&.x~b Iqˑ+NQ #qk8}۹ HBFdAC98uSaSXFl"{ k.Fy8FFI JjSEI1ؔK` CnTpo?SLt=# +7]xM߸uas:Qu;CC40WJvyA\1|F#ŧr8bՎ8[!54)f`mTS"ܕro ޱ~!CSՒ +EX%hrZNMLW$FsHsZ$QKq]0K[WvIp? k/S6?&yK` H +:P:URPšW!Q(z "p*7BVEGc.=Y4ycԌH y/Vq3r sc`s16Kqx[GچMY]IYZ2BnDv48qNĉ3JA+"Q y$oȶ\SH6/#^rBNK|7|j= +ubomҐ5 + 0EP&Fr=r /;phξFn8K X&|F8/D-F' ^l5B|31  - H D>{-u2nA+&"D~5M'Z1-s+xZ/ZPao.E!K@԰ПlhPs,S +:ԮU*Z;UN:zMRl^siibi3$@~ӨamNN2r +2h#lD0NAР0YE~?ƴNlMEqV1f +6bJ)Re[녫c & +L ߋ卡5y..8 +F/pϏ|~xfPG Gq3SaLmm A$),cITv`NBW+CAL P C,c/KϥT! +|dDF0'yw}oΝf]+f.豸\cPܔ'`(P2Y`\kcIBi:$XXIV0Sa5X11dddw^' %$~Z,ui2$H\36Fv$Qce杙̛y﬒lRkYg3ϗd$XJ_U+~JaZfT%X#Cnq;*ۿCY[v-gqT?l3T!ȚWX1g~:wfZS㺌g{i~f&k;޿3ϯ֎lDZB|כs^^[[ݼfךirrg[mfk $;y~iqlKOyy76VssgjҞm}/}Õws3gkj^דml7&8ZKjygSKk~lq꿝n[s:{͟(;BgK}ݙeLint?֚um3O/77w[L|.su'Ts߻5o.s=q):͝ڟϱP;Ϫ'}-7Wvfſ+ܴw\縭ߌ39Ky~^^eVӛm~>{g拷fޕӜVKu]=Se݆kjg]]uv:l;&w֭i~.2j7s13|7XSo':g9iO}6{Mk[Zs^W^kuW-7gWl实Lu3Mo_rي=ܳnoe?'vr6^󟛹it/۝TYWyϺVg۷u-O^fn@2ܓD.Le_r\͟nnjk7Z;{/}{{^V*72l^ݦݦ3mi:fڲδe]gLƴlyL˴Ss)1?\v\]UNr;Y}8ܮs);}͵wv;/]M6ݦt٦w޺{oi߼6ZZeuT߬ۖrγ.ɳ(#+IDW!"+6_DmDG + +Ae Vfeꝥbe7~C"DV^lz݄ʏ%y@@72F%nJF2e>)U_Q4K+1 @T}<[)U_;ϣ(<1qr}w (*d +y@ \`$s¯,=je҃č#J}WFK|.P%" +dI'eZK0@u PbGr@W )=GLS +?7g=3y*FFX~vNovn 9f\K$,cb %I.6m"ʃ*EI"Bot'SY(+F.$>G0T.}.8nJWS}(5z3?WldB3X(# 1m )F"!(&Ru/,mRaLԠ@"-Jt*S!x*?@K2`"C2ts^YP40md>GJ/Kbںi3mĴLi며c1$iD;|<׿nm{v{nb/=]ۍwϜZͷֹ^|4z'!E3-2h>^[׼s}:3qkX;kޚ׍mq;g\1c߷lo9[k_Kk6[|K_Fkr̵9X~3\{k~sy5[gW\͵~]f~$Xkn9߬5Uc9s/YWs~XIƼ#͋:Xj~1׻v}sX+/{^ݷsŗm|;:c1}gw՗Wurkz:|޽3~w_9v뾷[Vc^3}^ub|o+缫bq/y[syb>֋W3f|3hfw+[;ʹ\g/_><_]ro֜_m}\}{۷֧+\ojv]u޵E3Ϻ߷5_c|L5̵ƺu8wq7e$/w?cZת{s[g}sh&mslkֺ̯w]sVv~λնʱ~15{9|ڭV[|fŏ7{_6c߬/֕q6z[v9}m{+z:ʫޏsZ{̳^4sǝUjiX[k|㷶oqZ[ߋfr{}7ΜV\_g|~/y_Fo+߷zչ9;)(x)/~A`}0* Ad@Xe 䃵-`i=FO=~Lǥ%=Ue%,X1CwGTfQ43/qhV d-sB}PBqDXCs7~>-D##2"x7~`ѽj (IS!F0pD}Pg^;3 +c +,]8*~sRWP8;w4f 7VnR 9~%#g0 :#?05?4/~_i2^<$] o#4_w.W!LT@edRCZf-t;$J-۪ +Vȝ$ͅ\Ҧjʮ(!!K C(CC5 O[9քB>cBc9 LeRTtLDP0o"& mZW--R,} +2Lr111E2P!M0lfYx-l#,$`ޅׂ`.(G(/[Jɑ !ґ 4" F"/ȑ `ӄnii|uVrC(1VaVaEpZWD?%b)-y~R%bCo<=mtE{`p,:a ¢NF@!-rHQSـ`JDV\1FwV46( +&(OECg⡁)ABT1 *3bd :&+عBO<!Wh +ܵHS0X4 j6ZZACC ,8>t$^ VJxre$݅\BD0p Pƈ!5Mh6ݨ:K>C>teԄZFэtX*)Pu ?11QMIj4}0Ѵaa9ǫ8ǫ8ǫ6,$&$$&ȑ xU:pSJ/,:وQ|`L"-JHDDLDL$d(I|_ $U7Hn$TlY}%`H P-n*"1y_eU. -j 0䡀5-#P endstream endobj 20 0 obj <>stream += -KIJLCʖ!OJpOʧD,[X"ʯ&S:߲A:##pxyQ~rOȲlf)TȀ0 +Le8BL2&6"*>JBE_*T D nE `*tÈ,dv0& ƊUݲdžŇ3hh(/&䀹-{8HZd`4 KDl$fZlB0J̤P7HBE|:Q`*, d(y +SC[<*ERSɖA$0 A$ p`*/IF"Cy͈DcW ",-w=4&,Q퀵%_䡷AA^P`þF[ {dF 2ק5QwI4  ʴRS_ p"ьqd(?9]@#t.816qHC^`Xȉ.ed@.q͈CyZF5X@ ia*@i* !䄁߲>X,@?VFBY%+R2;0Ј-uDIT&4@4l8x1 q|^J1 ?`8HZLRXрDl*mXlb8tXKъ9ri!TF1jB!ASG%^0KQ^R-@Rrа%^IRQj[".Ccx r#6!%-)*Dp/2ՑP<=`t8CĐTlB\AlҰ`/F +ҠsP>* n$"Ҟ=" AnYǧfd2$ OݲhTO!0@a2lBŦ<(JB; `9 +[&"1*3.$bI#oY$hL%ƻK(GDp'd8 ` [*wY%`n8Tu}2@11&b9 |&އ :6BGP1{p|>3>Jn4) ؂). +ۈ2k!c*!@H`$ې`U8[AV(dF[i⣐2!Q]bj)7CSXIJX:c \odpg(U2*:bC&!!E$>D +'c*  +5 W>Vu@ !sfg by;UH,KNZ1U.BUM p nޒP*j9*;#c*Y(#T}!؍" z"s }x$PV |GeЃ$VBђtBo$Μ>(#- X G6:\& BrXjb*ԆU 6OC"r]ߤJXbQOP[V*8jt'Q)U:D#ӃHDҨt(TDQhhdH0x(Ʉ$VȬdP.|PD0P FRGqAdQg:S0t/PWjHΉ-ă)?fLL!׷oh;.2냷V2'kyG)^xZ@-!Y`,kRrA!h -^lY>&tIp$)GɠU=nbvMu9cEFXv#Tߋf 'M2Wȼ@eƥ4fZP*}xL?GJ#6M]o9HϐQ2g EiOB]'8ȁyB +^xi]JC= &~Apmy?l2[>9헉565ϡte }~zo+[ރdv)~s#It8xaɌxem>Lg"+L7=!vmjr vlLp+Obtޙn{R,Qܩ%_eĹg(R4#'aug$J_?-(U#RځDN7q9=XNʠ3v]WF{QY.ݒ*yNnPZ0b\_Z. WR R1tf{ıx447D鮋(WhUN9QP bUJW,|n0'~j]>$c*ҍ͸k9, YiӥλR3'įgLohAmoP1vāgv8 q<?U9^E٠e,܋ Q!Ug>Dh3Ѣ akȩfߩn<>=O|䪊%G&D -CH_K-WlPelc{x)G?KюwC{h Crk'Zբq T  On}p9e^j/F2:R*PMR?R%8-ZjT)9%CXytk *BwR<0UZ| 6 q6& CEZ{/1vʠfCD6F!alB,od%@JiZ4ߛn&#Wv!$4qL"VjI42Bnʋe@g~Zret^"jʗpY2u+5d }ҵY+ Hm԰n.Q8kldRs:).dpq;83zMjYzۦ@~#}L۱ gm:0n~U`w·J3P3qkoPpu#s>a|h85ɇfkɔEK:xGҊq63;~.JzLhAʰeV6Bc;w%BYʴTzӼF|6uU0Xoo[Aֹ'53r0jöK|V?+M6Y Wmy +|=mu}[ԡn(qڗHZlc qbuc'!MoV~,Qzvwc @ R9M/pG0w $ѠTُsl>g0@x_`h޿z11#j^q܀9" pcPӘ"65-%>0&پ,E 2zrzdcʺ߰)t KD_ܕToQyO=p:O`NKu:ujI Y-Ty2- F 3&AsԵpלDc[đF]>FQЍ,pԯ|M00F ︃T.$h +qK,Hǘ/SZ\xX}KZǚRH3i.g3!~l@rhED-Fx+BD92B7h 0i\o)I}'clSYp TΤfGb&$Uҭh9gFȶ`}JCRt0뛜w1Vw33|d^ĪEl4}_ jN +lxL!`Bf u_1#WTqBkx wdi 7!z0Vq #ZjNtZ]42eyCO΍abyzE vJ<\Ӏsܹhl[WİB&:މi U=G"5*xRy2d1(lb4_xŴBda9ɤD̸t5JV#U*|zIWRfC ʡG +^Oӫi)\)y H6b5_@(*e o0^* ;drΑQX'Ix{S!~)0[mkЌ `&|j< kg2M-w3>)/to| &1d4l ѕ`pɢ<(ʏ5:;FS=3 #jRFGӍ]{l,6Sm%wΩlqT/ yM@:nMB{(wBub9(subܬ=SWtk u\Y#ݵר7H'oTyrs[lOFzB/JAd(j?eEބAϷ cۅozkz6oORacW0oW1PB^Ш |^X0Kƀ^KxSդpMӭ'р&?P*fpEʝ˷ Y5ȹ usTi<8|#9JnaLX**k$VA +-ѵXJx9@^(AezQ=o2u=d{e ӿy)DkrE Ӽ>T3Q; ? OX-˟B(!LxzcŴUhŶ\U"3W'YBm.źjSU䊤8/*LBѝ1;-6ε}W': Ý`qM‘Qϭhzz.Mi<0lQQ`/|c@|U93nX8Ydf7s'.>@]wt#&|x+E KÅS 5x% Kڔ#8\zwFS'-DوRAV:[%kg_I qg¸am>)ֈ%LZisL#L +ʁ5'Brivp:7EhC3Z !:!=Ai1©U'U\]?< #DA[:e(xgΓGbGC0ҫ]PMKlbTj]rb, kr]4M+tlkƝ\;i3I"6!jbې2[aȃKo4q]nNEz;s&4LZX0DL\AЌ;d3{a{;0l:Xk4@\>ar1S~E9K#9AȜ} +]KY8pj^>,();bJe(׻Bm[:Q(N 8umʪg^DuǺ6/jUZ2i_Fhbl` 3uLM 3Ci,Kګ4+,|=-F₌07Ȅ4B#L7X6t[}\+ŏ:0BS"Y1$奲 Z/F6i] dWYTCHedB%v-``0@vy v0ʮlcf0؃lXu~GL2D61} RT d%1`OY~PIa`aT'+AP;K5 tp(O;bT 0az}Rf4ofBRx+w2m:ꮗ3'%9-cȤQEkԒppMPcj&ý(챈*]os~(bɓfc^%Vt|6sޱ[NcE&k*Hm9mꤋ2UêԶXx5{ {ZƇeGTΐgݖ>gV50"@/~X +?][ÝHKw3*zէ4e];q6B +qޠ)щ|<v)3ڰDG6P#W¥MI0ݴ.,w8Ig%[p]`EVlc;"H!^l@,]NIù3y_],-8+Whϴ|.'JV}K/05H5#h2w`OucHKvKwP$ZN(P 4b6espf_;3,Ii\x;@vߚ皕Cln|'}#198 BG]GQL^=W]d\x B<:1ˈQ @ .&?S~٪>A7Mn!c}pԾVIWGkFҌΗ>Y=^őp !牚.H:AuY|\?; !T%Hl/RL_XU# {BRfXAɭ *fXk"So6#ۘ{VcYNsՖד^H]+n%!Y K?!PM +2l\1nit ۫E*_Ls)gўյ `M>hm-4yjh%acJŽuh +fHKA~cpL᜵+AxePYԶSl),8. @e\s1:zoܹ=t|$0X 侨@X 8 + SZ,3d,$E&VEvA#w%Ý*|ʟG}"Q5MUd]B/k"` +L͹Nhsms("?Onpi"a֡;^R縐$ i6:1.ݔvדNRee[*ɜ^.)qGT^lZYA%SK[9C _T^dR,KLVÔ|~\gy;NOy;!͇Kw> PKJ$Jw*O]y1҅Tl^B8Dw^B[&6~JɻBQyZL= 6rɁIHz| 9|HHPL PT"?R݈{0vPgwjMQRҫN9ua9|D%ùv՗V+&iLK%٘adJp+naxnB. "-f7@Q6[y⒒u2(b78]@̸/;O6vL}`jK0K{Tf$$V1+HBb&wR %OFdsWWޔ~k{е(J4yGRv vʥstMsҒ1[ll0!ue@WFm(53! 7H9I;胅(?&eUh*F}{O@ift}0jwԾT $ _ɓF6|Nk I(0*j\߫(aid6XA{Mk&hjդK<^Ժ}5eQKX(l_qN| 5r{QL +־(_/P0\bJ|]1>-LʪdxO%1ռ+ Iimv+v%w )=.#]k}/b)ocٹԑ?H r +,Wd- xs6P1ܔ/m\ؠH5QH4- :#/fb)#2mT`y)_10‰QEץĥSDʼn_!K wkt([<rRsSjfsˀ]ssqQI@o/&+|0Y$!DJd_v%R#H>\*}q / vdPIi;GU f5gcxfT4o2Xe +p5WL9qHզn XA7~솶@6@X/,מ)u0bJ C!Ye1˜~{. gfC=SAܖ,|']-'=A˔-@8<t]А䂏3,Us@ NJX0=ʪl~`H; }JsIsΪIk~I&̫#m@;BZOu@DlY +#-}]m@%K eG̔اC~~gH)PNpFuupPNZ4x|6 B""2I.ƐHR<^lgg͖ D\:s ;VC*rM~)k-B A,^P>rU@3t& (xqi3ߤ Z,)D"2-nb͘?ki/i(s1$AsB"R*T pڬH[B0,Yf{R4V\0O7R;T驇8%QCG;l8ԋp5J;ȶG +2c,4cm%ZX7Kכ'pMQ82C)S70A5cNeZ?wal(*Ӗ]9եC'+9]d-Kw mH1)MN͚rvV2#MgVWU1S +62H֓=2u* "D=!˹ʵ,Xiy"pc%X,Gv@}LĠ*꿃8'@xOۚ(jF+U $b|Ɓ+I:GYȏ)0gMvcƪVÿJ&ݛZ0EWeo_~.(̻Oq9%ӄCcL{jY(jJ*.dB[OH)jx+W DŽ+9V$ܛ1P:IXO$1Z'+$42ǽ'Pm-`!y1P +-nu(]8.uCޭ]zPƏQν>&4~x$Y?lf!8qv0|_PmC?g/@@^j@@LLRjSSʂŇܳޏq6A^)!8TV|[la^jA}%!:@ apS{LQߣ,HxW$oN@xQO/$n%^3ZTT oL&,mJJ 7tgs▐sv͂fO +TA?@Ww 0I x5^ߢ.#O1SF Xh, dmu#o8U# wTCgervǐʂZpv4Y +Hy#GV!C2߁;? Ty *OOqk#a6[gYat @sXB4p*)0V6‹paT}V0CE#*!–GsH.x1lC7Q/$jxE%!AR +;FG}'ci뜊P~dZym1JyٯKԏҪ"]enJp,-IpRxpV5fqwBfZ^d>E)ʡ)g;F*- j.%s[ĉPi,h.IE,bls9"8kȏ׼ Z!4-Ph*I#uvzaՄ)|d #ʧ;pyw;i29H"5H@PLd4p#AqG MҰf'f<.D9Cl7!휌u`!A*H*=N1K@(U&tOPAUpNT#@ 8.u ^[F܂)oQZ)lg-8ڥl}Alr' %/a33R9 h78dM6ʄhAL=p϶T iwrU'L ƄT'WA ׊RJB]}IjN4gaDd~icZkS4+6؝>lٮ!'oS4gտ4 |Sh"ʷ \jkw?L$.5!s:AHIE0v + .2[~o\?wʅũ~tY;@o>u0g|^(ݯ `aΈ po*>JI&,e'㧣ĄFp>n0>J%Z,L +>4oI@Z@0(^16GxdKF>a^p#0p DQK), ]GEqdd+d9'IΉL「aKe2Oȼ-|` awpa;:vlQd7haذq}?8 #,hkLhZCf7j1% ]MՔAK# bi*@!s'BkbV)cȒnu+H9=)@ӵ͍e7S]Q vXIiIP懋`OWpVWfPKJFL\! n2@Er 9H\MEW'=C恔SY +p1]P-@xRg^PbS@߆$ po.O ÄV7zΒŃtYcN~ )GX)ԴLbZiLŐh@3ޖ|N2~.Cm+YaZx*n3wE Y>.B,gR$djAkri3@1iM;K㗿wP=|msyL8Zce˔\ *ˣ뒛[q97!n|`8 #[G<ʞ24Q&a:4G8MHlZ6oIy5ѭFRZ qJVi,4* +2+l3.ڵ.$=vҤ1O< hl +k ~rǜ?C7ط2<ӏگ55z2`d m^lX @j*l4gͣofF/=k0i18gQ³@~k1}] -[ ,I+R"$x Gp 4Y zACC:; +Kk"&9iv_MGw61- +fZr8ϽVq' h5Ĩɞ!DEv>i[R+@K|%+!'si(J#hEC *?+揀_ +ڈcꃨ"E;Rͩp?%L r۰vz8)à1eԠl$FԈPs"yl,`؝'!ğ͊ƈmQS%$Gr(uj2V2 +`ˏ]sUBr" y )wҭK_RҘ;TH6XKG-40Mܮb#$:0*kiyv?qCmrc+mwa({ gVm8B[YbzlTc;M92{0^4u@T͆UŵBZVhأ 'Kwۋ]*N&}o~RpZuh܎Xje,#/* @Ir](*J|JEI6ˀ3 _"|Qf5᪙=.duas"3hvAl].0&BC,W.2ǎ^w_SbHu3 gr -}k*h9]M uVBk]hVIyѶ8fw17mL `lm3 A\eWQNk;z@x^.>81v0A o\i}?j;.$F͟.d +)(A];[)Q*:V}޸RoM0c-~q9 +C1 +B91qΧrP )m`HGسT:hS9?/KHCҷ4Ϊ{UQ"UBjCY1螢;G+ +;=Xc~`%LLOJѺà֮,Ivɂ{ S`$T2w鷧`\t؂ꝙRTj&Qt+cM)+P)"CkaḢkiĖ髻A{)&>^[ݪjAיH/(;UO5{T%Mn?S!*18j{?xFp"80ޫ,4= oJE` %99[WcuZHExV8zC &_]"Խ +a4 49MDe1ZYR;T*@۳4~Kz]|5K-t#izEAUQ#mc v&Z +$"B@4R` t3"Ѩ*S`n|BJV1PiEҭ Yb0ցK>.$, А @ a(P11DeBG,p8PHjbtL!  01&*LgT:PH'+ ƄDBVCk#d7G@;pۨCH!I"\NJ>yQ$Jh c!бO ù Li.D lļL48:v221Q"0hCc@Ei.RRsRIlf@,aC#abqݐ- \)%Ԝ`BKP52Ё +#dR00J 7PŇ!d #$ŧ7^fD) \\8qi&$!2C1b44JDSs𗗉& ̐)0X l + `x çP`e\ŅI,Gd $P>@dI@Hp0$LX1p:F3Ӧ.(PH`!`9q@5YO ̬RߎD BP11`gd3 !O.2Ф8 `la$td$ qLt:I |!-|H$<`t: K.FcB@́B&,|Á :) 0@BzIt8@c"@,`!V HD +XHˁʍAh44 eJD<"QH3E(`1,Azx @lԩqH)HSJ62D8DpdJHL OCA'$"m>":OȼɸOsHHȌ@ +İ`X Ԝ`  H(J-RH>:M3#!!jEpñx0`Ch +/ +/  ǁ.t4!L 2Zjh"rN.Z.jJJTaD!v, '%i H(%V\xE@ +CIЇ3<@څ$dΚ4>@^>42t OHDj xЙOɸ PX /ˆ$`lJ$D>GRj) \|H*21]lWkxpݽiv߲Lꏕ׷+bڎ\TŇͩHqo^2VԺj}5u߹+ʝyRF5⮶mim~:gD#-|c^wJ;ϝyW5{#f;g].4gǧ>͟sX7mQպmOFn{_vvmZ1.Ƕks]WQʯP?1WS̮Ԙotkū4[9#0Poڽ+ +0{fS4NL[eElVVϥ~vTi{2;Jysw2ݷ眨dճsN_^S7|rdG57K^ʽ|Q]3rEi1nvgj3.n(U|F4Sg\_hvWmj{-6{d]jմ4f9~yeWԫ6?-6uݼ]Q˸ {WYi_Wԝo]7^^bW곣MOW-Mgɣt:HBOu2+ +v\:XdHױPHt$H D h8mTn*HLGO߆l"D£t8F|xPY::ϧe:(+lt! + .K]T% éPHa!KEB-kPHA 1$X0 t3CA1ЀHl$@v<|:#5+\pLHDD0ĈŇ _8Px Iu0faQ DP"5 8@g B&if8BN:ÁP: +qbRUE89G +Ŋ$ M3b1Ō,OG pLdp> q8HpԜ`  M]B3^*2]G:B" +J +IReFF<İ\ QbR, +XlFdS1lBƠ,FSO5"3"5Zt +틍pX\\xS1*IQm;YчaNV1B%X σ,:yǸ*pIpH(@Y'fFV񜬈>6Y'4j#*QR"rs)G`E8P 7됩43tl[̋+JOZ|b\oRܻ';Z^K6zyR̺ǾǿifoffRlu̾~eyl_Y^/ٹK.}E2^IsRݞ췞RMcSG=\[>|(on廔r2/} 1m*3V*|=5najֶuJU +xu~E2m]_Qm}|~EaZ2Եukm_|Je;aJju~|=9YQyv%ņyʷhy|l{_QH={-(fwNl ۛϗW8K>>]o4D[__QϋǿQjG)ۛk[ZQjfh+*Y6+o_Kmo45y]W/vdMc^xָuܶʗԼ ug_\QxdžFsvފ7_eGug̚k^MݮyynE1]:W/hm7u.nE.Dշ٩۽m+LsVj t[3Ֆm.}D6eM=s~exfܶmo\;][{]YQmyx̛zYQiYQnhYQjֲZwܻŶunY){v5pcE'c7%nru_]^j3u~f|+s=wO# mP.ym:2e/3<}eWTvafw+o~DnlMƫ(fKE][^hyn٠T{%bE2r&g ]9]v6uT.:gņ~w˞}y}m9=&cc/jK +ϔ[M+cGgӜ Nᰴ &A BcSBBȰبP,Piа, + ڠTϚv=m5wdO]whTu[eVnk9%Z݊jccyK{+*O3Wofwm{ź VZrrΊW5Μij>4+}+}jEw3/ggb:˼VoW'+U׫ZQX))3j[ +*vv2;=V?\ Ϙ?7+&Sj8ͶQM],x};+]/{}1gKt5c +r6X0{9L~NuٔcEu^&f7[)NJ[ۜ=+ +{1uLS1uLS1uLS1uLS1uLS1uLS1uԜ͜\wn.Nݼؗf}xPVnاj)EKl۽;W'?.su/][߫ƟK)>_ZQ݋cּf￿kw^׵=/sN^RM}[u>vƋеVMYWᣧ-ޣ}楣f*N7׿V޳:εŶkh˼V]wne珹.b܊ZDv}]?wWVT"lPlh\Qw 9#E{ǖ)w>4]z~ټ,q%?yxx2sN΋|+CK^~2d|Ů\>ltmt\}nr)Vn>uFU=}\NŨ:޲G\k?skΈJuw왽蝷-8Zy19!wc.l[g޿m\QݹX?-߻c[mS{}?m_gv~;um1~[QjT3.cƜSYwފjVK]Dɜq/nIVkWT#wuj7Q~ں^vǖvԹ);r}vl5cm_֯E37u/ev1f/7q17̶v+0n?wZmjQUskv2T>[MizNsc{ؔen~ƭk5Hn/Q$yks!$.8)YWG^] *r,s(֝TJz\l m)Nm$It=!l}?:shC6[.ұ7T)UC)3f^SX19?P8hp}{d.'e-ZerUS?,Q'et(sS ?a+lKGZy6Ȣ!7E 4r!)sXf([okSh= RAǣނ̄|ib$=zt>56}'/k]F}@x\!W- +#$h!~+ov8x '7!M&zfӷ:I55-ZMȤ&znN7&4X}{:=l5)3Xt!)"Hɨw{Lrz{[tfz>J)dVWW"dasؔ[ARQd6:]E}jEXy[.npK6+r{_(^s9/ErA<~x̍-')ho Txy7*4H=KK牗PC\u(^!d  1qGzF+@#U4o(wBEzh%/}޾ߒMkEq]kf $35M8?h g{*j^$k87J]zJ-Hkm`nm_-.z4 f.cċFm?T hڹ"ۋ[F8/Lh_ +XYXlNC=ONet5VH;Do 0'[tj0?ɴT65 +4l)o]ɸ8Ys^I{=c} AX Vx}^OY8f 2`wMB.5yM%a`< +/s +HfG[TayM}#hcbFKā$tgAk&5T{d}ܖ<\d#s^E [oPI~ke}KZW8DJ4O<ɬRd@ƛjWW#ƚc,`#! hA62L}߃S렑O2w kf& u>jwqKtF!&ѧfn0.ZY7+b- +G~&7%~U7<A 4ri2k4kG8`̔62d4W]L{G-Eǵs8$(,{]/>5@hYFomBy$#09Vc`5Ңfi>Mb`t GBםL +2c#񼱥y+k-=`ocnCT0Zt~}yW1 6ڡl0&fV/-7S2  ]+&4Lk# "5*g +#\gG=(ä$2n^47``p|MNA bQ}kT:.}:Ӹ>LQnV# +9w^:+ +ӳ<ԈUSu{' )N͎hSBcɰ2p )HU vCAP~ pQ)N6f.Dc9g%#eCwVzz9 B,ۀnb懽gZZ] trc&ZN4j/2~r>`t:&xN>dW8mcudZs#Ԏ:pKrN +4lsQJ:!ȷN,zˇzZEJGBtjyDŦ%b~:cH i} Vu0&wn?O[AO| '_&ԭro)`Sk^)OFudZm fsʐ~w1ҍcFݥ<΍w I-)fQTrqoUUr]FhO6P*\jx G0-O>w4p A"PUpkGj7M5,kXTdftjQSE6 +^kRNPtiW5 _ SXv_=.~P:52Sj 7Ö}kDW܋5[O*Lj:))AcdySDm?b=50PczPѰ{L±q ])[ grA1[+rxsujwFųyq:_+Sk73 8ܙ)J!NCRxD4<1d/2p4%׽$.Xvˏ3y J}BS 8ʟ/CoC+2PdCV]g?3kD$rR;-8b+dh,>#!]0Pf~&J0zfdQ z3@.(Bmn"?[yHHnJH2KB;HIp>D@,7K +Maw%n$-sm0'[/By#dDw K3Fx{L__ώ@ 8᪍9SĩM_z4R#7ܱU*<8~t,RchyNGnxj:zl_AME'%g l̈ ꧬt!qz$mn`s`Z D/K^Bp;g]%wCm?%[5~2_b2B#T;X:rϕ7" q%((I Ub}LGkڦFx K)rʾs8mOqMՑn1%52 V J)(~' H#_xS`I ]@ )Ha]r \BHD A?@;S? Q/HBufT-..0 q4`4^P{: `E)XHCD_HPf>)"UT!Qm"AJ7} >") _@#'m'e``Y曼5t0%݋[ws#ݹ6*r> L-LV97 |$M +ʹ;XPV!(ƌϼBA)d0JXG?ΝUĥyB'7ۯL8Y|0M^ (\<q58^h6،b]PJ[=}ci(23Œ#wab0A˜2\0)eJ@􂟫8e#܉CΌb_,_Fƀ )5FY^@Ս:2;jIcr +w 1Re9PdT3ST˽im*7F#2RXca+ -h,^iAN@! ?n`|{)\ @7R4n<&O,LÛͨ=H[Srd,?c3Cxe ` S/8h)yݘ^įMy EQ#A5Ymua;xcc]UwC nj-ˡÂFDJ %- [7pX $η!qaP슒LJ>u]I QsHrȑ&w(ҜJJ:1LFr5Dd7$k(>U#Z%HDf 2u_1jG0ݩ J>t Ts$^P\Es^!Wn0s8[kXqr'<+ 6<oXTvFe>&pd~tȝ4;2l5ǪCP#{ΰpVÃHKkLB9 +1%68IS$AA4&4FCLPP&|c%r\X8i24n? a 8:SQafBK&`Ī|_IbɈg$zcumla5äU\u ]=t>5;ʔY+F۔ X)w4:r\!~Ԫvܨ'헰'5O +a}#3To%M%6 lTٹP:K<<É_JTu"k tgv$Kb,R2)f7'/ +"=pq fM4 .Üv^j2۪ywJIs.f)mk|h9/ޟbYI&^Ǝ -#+"7E2mI7[SL8s ^p)xDZaN^Ǡ~I/TqH9JA5haЏaC,億\YUy@n+nR!rU'!+&GtqshA OȷY."}sKO"Og !P<8_ZZ dXolC%TjKch&hDsNg!:#1)>*Qdfj6UT.,-{GhnU6#Ckck|/, /hwv%a/DXv$rjp:1Az>Z`4?EDZTz$xrNN0Z5:Z +`âRk1aX qsk~m%<%#R"3ZCy|-tFRj|a``-3:Y< /z'E=0#},bvg#ov +fztiD`F-d~̷ֹPEX{eIO0<ɆLlA9 Sխ/ƋPlP>5Y8Dg|̿RW5@!A +}نǓ+Ysxr[Z&gטK!=8a4|"D,J'4zT퇘{-+큜4&rVdIMn~74v~P7̋YW ju^| M>!'@_0e6blxi~{p9Ic+c|1!QXX5ϸt)@]?Yl!4 KHZSqn/%Ʀf#_i6GX":,ЄYșVċcT^;M)Jԍ*UPΚE)#c^\粌 Ap͕J@\\I*]AU 7 +F7*hQ^4a9z[v"oh9#:2pֳEQ2kW=PcTb& Cyf)`UXK^zPgJ hg<. :q.wpQ2%Ě^bZ+% ZR_sM!>ɁIdPQ|zU{˽P3- ɡz6PI^)zp K9æ"yV s 4 EI-p7+RuGkcDj~נ$T__~!a lIRelX,e^W`/ւ+,_Rʁ)ϗvp2eRMtwbItOr儍d5#ʿmQ[wu):4n!rt-,w / $+'Penxup*?:PuA Uٝgxm7&ʕ1;T.P1C]fo4YAhYzx{&v j"@]0( J%~w\ Жȳp &^fr0x~TC +/z lp#>+,NcfvPei 2 {"VkzJ[j}dlunZi46r ,8sq̓q&=7rA|\~? ikzx [.腰blGSǺ$mϖЦHT)0EpaPFzi cA唺"Cv Bd4ߊ"`5v@(ȍ( B*bko%3h{nxGh)),^E-ģ͛wO0gon` ok*[R,YhSFsKMͪ#/;9Uۿ;EV$Bh}eDT""weav3mJ\HΉ[ene ĉO=UpqƲ! E,Ӳ,Zc ~WQu!8`]^/Qlv+~;b J4&[+)FX86c?y )W)EX nŎ3gE,O|b0 +{,*oC -:cBωm Zď=N}, ҝ.UI.͘l(dn*ة xlfYG !B/4B XctJf\[bYzR|';=];Uod4=O:WԸ ^T'Vc氲4<^~*%@@Y&jO"tminkE$$T6jh{_,#0yG+lV'#/O4K7 "Vcʽvlz_PX\[{xN +ݖ-;{ 4]\1~C<0lr6ܤA:Tw-Fx*iJ"w~a>R3'tuJ%$ЉmR#h25֛NTVT" [Ii'Rӳ3d"!:g}v͟ E?,3K]XX`lF-] I +^L-)pwhƾdWccPga C[,K@4V$޵Z3/mo"4/DB1u" +P c~w Q뼻$xw1gpB݅#/'ݽ7G8Xٿb͓D&0P<_+6w]|]TҐE|1wu|"($ޭL& 025YW zws6t f8b~ϒy6 endstream endobj 23 0 obj [22 0 R] endobj 33 0 obj <> endobj xref +0 34 +0000000004 65535 f +0000000016 00000 n +0000000147 00000 n +0000053354 00000 n +0000000000 00000 f +0000053405 00000 n +0000000000 00000 f +0000061435 00000 n +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000061508 00000 n +0000061704 00000 n +0000062855 00000 n +0000128443 00000 n +0000194031 00000 n +0000000000 00000 f +0000058452 00000 n +0000230347 00000 n +0000053794 00000 n +0000058752 00000 n +0000058639 00000 n +0000056802 00000 n +0000057891 00000 n +0000057939 00000 n +0000058523 00000 n +0000058554 00000 n +0000058787 00000 n +0000230372 00000 n +trailer <<99675286E35B4B338630FB1C02CD964F>]>> startxref 230599 %%EOF \ No newline at end of file diff --git a/src/res/jamulus-server-icon-2020.svg b/src/res/jamulus-server-icon-2020.svg new file mode 100644 index 0000000000..404a6da3f8 --- /dev/null +++ b/src/res/jamulus-server-icon-2020.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + diff --git a/src/res/mainicon.png b/src/res/mainicon.png deleted file mode 100755 index 1fbecddb3e..0000000000 Binary files a/src/res/mainicon.png and /dev/null differ diff --git a/src/res/translation/translation_de_DE.qm b/src/res/translation/translation_de_DE.qm index aefa7c72d8..e71793848e 100644 Binary files a/src/res/translation/translation_de_DE.qm and b/src/res/translation/translation_de_DE.qm differ diff --git a/src/res/translation/translation_de_DE.ts b/src/res/translation/translation_de_DE.ts index 8474e0e189..e5346600c6 100644 --- a/src/res/translation/translation_de_DE.ts +++ b/src/res/translation/translation_de_DE.ts @@ -4,114 +4,135 @@ CAboutDlg - The - Die + Die - software enables musicians to perform real-time jam sessions over the internet. - Software ermöglicht Musikern über das Internet in Echtzeit zu jammen. + Software ermöglicht Musikern über das Internet in Echtzeit zu jammen. - server which collects the audio data from each - Server, der die Audiodaten von allen + Server, der die Audiodaten von allen - There is a - Es gibt einen + Es gibt einen - client, mixes the audio data and sends the mix back to each client. - Musikern sammelt, zusammen mischt und wieder an alle verbundenen Musikern zurück schickt. + Musikern sammelt, zusammen mischt und wieder an alle verbundenen Musikern zurück schickt. - uses the following libraries, resources or code snippets: - verwendet die folgenden Bibliotheken, Ressourcen oder Codeschnipsel: + verwendet die folgenden Bibliotheken, Ressourcen oder Codeschnipsel: - + Qt cross-platform application framework Qt plattformübergreifender Anwendungsrahmen - + Audio reverberation code by Perry R. Cook and Gary P. Scavone Halleffekt von Perry R. Cook und Gary P. Scavone - + Some pixmaps are from the Einige Bilder sind von - Country flag icons from Mark James + Die Bilder der Länderflaggen sind von Mark James + + + + This app enables musicians to perform real-time jam sessions over the internet. + Diese Software ermöglicht Musikern über das Internet in Echtzeit zu jammen. + + + + There is a server which collects the audio data from each client, mixes the audio data and sends the mix back to each client. + Es gibt einen Server, der die Audiodaten von allen Musikern sammelt, zusammen mischt und wieder an alle verbundenen Musiker zurück schickt. + + + + This app uses the following libraries, resources or code snippets: + Diese Applikation verwendet die folgenden Bibliotheken, Ressourcen oder Codeschnipsel: + + + + Country flag icons by Mark James Die Bilder der Länderflaggen sind von Mark James - + For details on the contributions check out the Die Details über die Codebeiträge findet man in der - + Github Contributors list Github Liste der Mitwirkenden - + Spanish Spanisch - + French Französisch - + Portuguese Portugiesisch - + Dutch Holländisch - + Italian Italienisch - + German Deutsch - - About - Über + + Polish + Polnisch - - , Version - + + Swedish + Schwedisch - - Internet Jam Session Software - + + Slovak + Slowakisch + + + + About + Über + + + Released under the GNU General Public License (GPL) + Unter der GNU General Public License (GPL) - Under the GNU General Public License (GPL) - Unter der GNU General Public License (GPL) + Unter der GNU General Public License (GPL) @@ -160,12 +181,12 @@ CAnalyzerConsole - + Analyzer Console - + Error Rate of Each Buffer Size @@ -173,207 +194,305 @@ CAudioMixerBoard - + + Personal Mix at the Server + Eigener Mix am Server + + + + When connected to a server, the controls here allow you to set your local mix without affecting what others hear from you. The title shows the server name and, when known, whether it is actively recording. + Wenn man mit einem Server verbunden ist, dann kann man hier den eigenen Mix verstellen ohne dass man etwas daran verändert, was die anderen von mir hören. Der Titel zeigt den Servernamen an und falls bekannt den Aufnahmestatus des Servers. + + + Server - + T R Y I N G T O C O N N E C T V E R B I N D U N G S A U F B A U - - Personal Mix at the Server: + + RECORDING ACTIVE + AUFNAHME AKTIV + + + + Personal Mix at: Eigener Mix am Server: CChannelFader - + Channel Level Kanalpegel - 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. - Zeigt den Audiopegel vor dem Lautstärkeregler des Kanals. Allen verbundenen Musikern am Server wird ein Audiopegel zugewiesen. + Zeigt den Audiopegel vor dem Lautstärkeregler des Kanals. Allen verbundenen Musikern am Server wird ein Audiopegel zugewiesen. - + Input level of the current audio channel at the server Eingangspegel des aktuellen Musikers am Server - + Mixer Fader Kanalregler - 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. - Regelt die Lautstärke des Kanals. Für alle Musiker, die gerade am Server verbunden sind, wird ein Lautstärkeregler angezeigt. Damit kann man seinen eigenen lokalen Mix erstellen. + Regelt die Lautstärke des Kanals. Für alle Musiker, die gerade am Server verbunden sind, wird ein Lautstärkeregler angezeigt. Damit kann man seinen eigenen lokalen Mix erstellen. - + Local mix level setting of the current audio channel at the server Lokale Mixerpegeleinstellung des aktuellen Kanals am Server - + Status Indicator Statusanzeige - + Shows a status indication about the client which is assigned to this channel. Supported indicators are: Zeigt den Status über den Musiker, der dem Kanal zugewiesen ist. Unterstützte Indikatoren sind: - Speaker with cancellation stroke: Indicates that the other client has muted you. - Durchgestrichener Lautsprecher: Zeigt an, dass der andere Musiker dich stummgeschaltet hat. + Durchgestrichener Lautsprecher: Zeigt an, dass der andere Musiker dich stummgeschaltet hat. - + Status indicator label Statusanzeige - + Panning Pan - Sets the panning position from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. - Legt die Pan-Position von Links nach Rechts fest. Der Pan funktioniert nur im Stereo oder Mono-In/Stereo-Out Modus. + Legt die Pan-Position von Links nach Rechts fest. Der Pan funktioniert nur im Stereo oder Mono-In/Stereo-Out Modus. - + Local panning position of the current audio channel at the server Lokale Pan-Position von dem aktuellen Audiokanal am Server - + With the Mute checkbox, the audio channel can be muted. Mit dem Mute-Schalter kann man den Kanal stumm schalten. - + Mute button Mute Schalter - 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. - Bei aktiviertem Solo Status hört man nur diesen Kanal. Alle anderen Kanäle sind stumm geschaltet. Es ist möglich mehrere Kanäle auf Solo zu stellen. Dann hört man nur die Kanäle, die auf Solo gestellt wurden. + Bei aktiviertem Solo Status hört man nur diesen Kanal. Alle anderen Kanäle sind stumm geschaltet. Es ist möglich mehrere Kanäle auf Solo zu stellen. Dann hört man nur die Kanäle, die auf Solo gestellt wurden. - + Solo button Solo Schalter - + Fader Tag Kanalbeschriftung - 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. + Mit der Kanalbeschriftung wird der verbundene Teilnehmen identifiziert. Der Name, ein Bild des Instruments und eine Flagge des eigenen Landes kann im eigenen Profil ausgewählt werden. + + + + Grp + Grp + + + + Displays the pre-fader audio level of this channel. All clients connected to the server will be assigned an audio level, the same value for every client. + Zeigt den Audiopegel vor dem Lautstärkeregler des Kanals. Allen verbundenen Musikern am Server wird ein Audiopegel zugewiesen. + + + + &No grouping + &Keine Gruppierung + + + + + + + Assign to group + Zuweisung zur Gruppe + + + + Adjusts the audio level of this channel. All clients connected to the server will be assigned an audio fader, displayed at each client, to adjust the local mix. + Regelt die Lautstärke des Kanals. Für alle Musiker, die gerade am Server verbunden sind, wird ein Lautstärkeregler angezeigt. Damit kann man seinen eigenen lokalen Mix erstellen. + + + + Speaker with cancellation stroke: Indicates that another client has muted you. + Durchgestrichener Lautsprecher: Zeigt an, dass der andere Musiker dich stummgeschaltet hat. + + + + Sets the pan from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. + Legt die Pan-Position von Links nach Rechts fest. Der Pan funktioniert nur im Stereo oder Mono-In/Stereo-Out Modus. + + + + With the Solo checkbox, the audio channel can be set to solo which means that all other channels except the soloed channel are muted. It is possible to set more than one channel to solo. + Bei aktiviertem Solo Status hört man nur diesen Kanal. Alle anderen Kanäle sind stumm geschaltet. Es ist möglich mehrere Kanäle auf Solo zu stellen. Dann hört man nur die Kanäle, die auf Solo gestellt wurden. + + + + Group + Gruppe + + + + With the Grp checkbox, a group of audio channels can be defined. All channel faders in a group are moved in proportional synchronization if any one of the group faders are moved. + Mit dem Grp-Knopf kann eine Gruppe von Kanälen definiert werden. Wenn man einen Lautstärkeregler einer Kanalgruppe verändert, dann werden alle anderen Regler der Gruppe gleichermaßen verändert. + + + + Group button + Gruppenknopf + + + + The fader tag identifies the connected client. The tag name, a picture of your instrument and the flag of your country can be set in the main window. Mit der Kanalbeschriftung wird der verbundene Teilnehmen identifiziert. Der Name, ein Bild des Instruments und eine Flagge des eigenen Landes kann im eigenen Profil ausgewählt werden. - + Mixer channel instrument picture Mixerkanal Instrumentenbild - + Mixer channel label (fader tag) Mixerkanalbeschriftung - + Mixer channel country flag Mixerkanal Landesflagge - + PAN - + MUTE - + SOLO - + + GRP + GRP + + + + M + + + + + S + + + + + G + G + + + Alias/Name - + Instrument - + Location Standort - - - + + + Skill Level Spielstärke - + + Alias + Alias + + + Beginner Anfänger - + Intermediate Mittlere Spielstärke - + Expert Experte - + Musician Profile Profil des Musikers - - + + Mute - + + Pan - - + + Solo @@ -410,66 +529,88 @@ New chat text edit box Chatnachrichteneingabefeld + + + Type a message here + Nachricht eingeben + + + + &Edit + B&earbeiten + + + + Cl&ear Chat History + Lösch&en des Chatverlaufs + + + + Do you want to open the link + Willst du den Link + + + + in an external browser? + in einem externen Browser öffnen? + CChatDlgBase - + Chat Chat - + + &Send + &Senden + + Cl&ear - &Löschen + &Löschen - &Close - &Schließen + &Schließen CClientDlg - + Input Level Meter Eingangspegelanzeige - The input level indicators show the input level of the two stereo channels of the current selected audio input. - Die Eingangspegelanzeige zeigt den Pegel der beiden Stereokanäle der selektierten Audiohardware an. + Die Eingangspegelanzeige zeigt den Pegel der beiden Stereokanäle der selektierten Audiohardware an. - + Make sure not to clip the input signal to avoid distortions of the audio signal. Man sollte darauf achten, dass das Signal nicht zu stark ausgesteuert ist, um Verzerrungen des Signal zu vermeiden. - If the - Wenn die + Wenn die - software is connected and you play your instrument/sing in the microphone, the LED level 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. - Software verbunden ist und die spielst dein Instrument, dann sollte die Eingangspegelanzeige flackern. Wenn das nicht der Fall ist, dann ist wahrscheinlich der falsche Eingangskanal ausgewählt (z.B. der Line-In-Kanal anstatt des Mikrofonkanals) oder der Eingangsregler im (Windows) Systemmixer ist zu niedrig eingestellt. + Software verbunden ist und die spielst dein Instrument, dann sollte die Eingangspegelanzeige flackern. Wenn das nicht der Fall ist, dann ist wahrscheinlich der falsche Eingangskanal ausgewählt (z.B. der Line-In-Kanal anstatt des Mikrofonkanals) oder der Eingangsregler im (Windows) Systemmixer ist zu niedrig eingestellt. - For a proper usage of the - Um die + Um die - software, you should not hear your singing/instrument in the loudspeaker or your headphone when the - Software optimal zu nutzen, sollte man sein eigenes Instrument oder Gesang nicht im Lautsprecher oder Kopfhörer hören, wenn die + Software optimal zu nutzen, sollte man sein eigenes Instrument oder Gesang nicht im Lautsprecher oder Kopfhörer hören, wenn die - software is not connected. This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). - Software nicht verbunden ist. Das kann man erreichen, indem man den Eingangskanal im Wiedergabemixer stumm schaltet. + Software nicht verbunden ist. Das kann man erreichen, indem man den Eingangskanal im Wiedergabemixer stumm schaltet. @@ -487,225 +628,426 @@ Verbinden-/Trennschalter - Push this button to connect to a server. A dialog where you can select a server will open. If you are connected, pressing this button will end the session. - Drücke diesen Knopf um sich mit dem Server zu verbinden. Es wird ein Fenster angezeigt, in dem man den Server auswählen kann. Wenn man gerade verbunden ist und den Knopf drückt, dann wird die Verbindung getrennt und die Session wird beendet. + Drücke diesen Knopf um sich mit dem Server zu verbinden. Es wird ein Fenster angezeigt, in dem man den Server auswählen kann. Wenn man gerade verbunden ist und den Knopf drückt, dann wird die Verbindung getrennt und die Session wird beendet. - + Connect and disconnect toggle button Schalter zum Verbinden und Trennen - Clicking on this button changes the caption of the button from Connect to Disconnect, i.e., it implements a toggle functionality for connecting and disconnecting the - Wenn man diesen Knopf drückt, dann wird die Beschriftung des Knopfes von Verbinden zu Trennen geändert, das heißt, dass er eine Umschaltfunktion hat zum Verbinden und Trennen der + Wenn man diesen Knopf drückt, dann wird die Beschriftung des Knopfes von Verbinden zu Trennen geändert, das heißt, dass er eine Umschaltfunktion hat zum Verbinden und Trennen der - - software. - Software. + Software. - + Local Audio Input Fader Lokaler Eingangspegelregler - + Local audio input fader (left/right) Lokaler Eingangsregler - Reverberation effect level setting - Halleffekt Pegelregler + Halleffekt Pegelregler - Left channel selection for reverberation - Auswahl linker Kanal für Halleffekt + Auswahl linker Kanal für Halleffekt - Right channel selection for reverberation - Auswahl rechter Kanal für Halleffekt + Auswahl rechter Kanal für Halleffekt - If this LED indicator turns red, you will not have much fun using the - Wenn diese LED rot leuchtet, dann wirst du keinen Spaß haben mit der + Wenn diese LED rot leuchtet, dann wirst du keinen Spaß haben mit der + + + + This shows the level of the two stereo channels for your audio input. + Die Eingangspegelanzeige zeigt den Pegel der beiden Stereokanäle der selektierten Audiohardware an. + + + + If the application is connected to a server and you play your instrument/sing into the microphone, the VU 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. + Wenn die Software verbunden ist und du spielst dein Instrument, dann sollte die Eingangspegelanzeige flackern. Wenn das nicht der Fall ist, dann ist wahrscheinlich der falsche Eingangskanal ausgewählt (z.B. der Line-In-Kanal anstatt des Mikrofonkanals) oder der Eingangsregler im (Windows) Systemmixer ist zu niedrig eingestellt. + + + + For proper usage of the application, you should not hear your singing/instrument through the loudspeaker or your headphone when the software is not connected.This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). + Um die Software optimal zu nutzen, sollte man sein eigenes Instrument oder Gesang nicht im Lautsprecher oder Kopfhörer hören, wenn die Software nicht verbunden ist. Das kann man erreichen, indem man den Eingangskanal im Wiedergabemixer stumm schaltet. + + + Clicking on this button changes the caption of the button from Connect to Disconnect, i.e., it implements a toggle functionality for connecting and disconnecting the application. + Wenn man diesen Knopf drückt, dann wird die Beschriftung des Knopfes von Verbinden zu Trennen geändert, das heißt, dass er eine Umschaltfunktion hat zum Verbinden und Trennen der Applikation. + + + + Controls the relative levels of the left and right local audio channels. For a mono signal it acts as a pan between the two channels.For example, if 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 + Mit diesem Einstellregler kann der relative Pegel vom linken und rechten Eingangskanal verändert werden. Für ein Mono-Signal verhält sich der Regler wie ein Pan-Regler. Wenn, z.B., ein Mikrofon am rechten Kanal angeschlossen ist und das Instrument am linken Eingangskanal ist viel lauter als das Mikrofon, dann kann man den Lautstärkeunterschied mit diesem Regler kompensieren indem man den Regler in eine Richtung verschiebt, so dass über dem Regler + + + + Reverb effect + Halleffektregler + + + + Reverb can be applied to one local mono audio channel or to both channels in stereo mode. The mono channel selection and the reverb level can be modified. For example, if a microphone signal is fed in to the right audio channel of the sound card and a reverb effect needs to be applied, set the channel selector to right and move the fader upwards until the desired reverb level is reached. + Der Halleffekt kann auf einen selektierten Mono-Audiokanal oder auf beide Stereoaudiokanäle angewendet werden. Die Mono-Kanalselektion und die Hallstärke können eingestellt werden. Wenn z.B. ein Mikrofonsignal auf dem rechten Kanal anliegt und ein Halleffekt soll auf das Mikrofonsignal angewendet werden, dann muss die Hallkanalselektion auf rechts eingestellt werden und der Hallregler muss erhöht werden, bis die gewünschte Stärke des Halleffekts erreicht ist. + + + + Reverb effect level setting + Halleffekt Pegelregler + + + + Reverb Channel Selection + Halleffekt Kanalselektion + + + + With these radio buttons the audio input channel on which the reverb effect is applied can be chosen. Either the left or right input channel can be selected. + Mit diesen Knöpfen kann ausgewählt werden, auf welches Eingangssignal der Halleffekt angewendet werden soll. Entweder der rechte oder linke Eingangskanal kann ausgewählt werden. + + + + Left channel selection for reverb + Auswahl linker Kanal für Halleffekt + + + + Right channel selection for reverb + Auswahl rechter Kanal für Halleffekt + + + The + Die + + + + Green + Grün + + + + The delay is perfect for a jam session. + Die Verzögerung it gering genug für das Jammen. + + + + Yellow + Gelb + + + + Red + Rot - + Delay status LED indicator LED Stautuslampe für die Verzögerung - + + Opens a dialog where you can select a server to connect to. If you are connected, pressing this button will end the session. + Wenn man diesen Knopf drückt, dann wird die Beschriftung des Knopfes von Verbinden zu Trennen geändert, das heißt, dass er eine Umschaltfunktion hat zum Verbinden und Trennen der Software. + + + + Shows the current audio delay status: + Die Status-LED für die Verzögerung zeigt eine Bewertung der Gesamtverzögerung des Audiosignals: + + + The delay is perfect for a jam session + Die Verzögerung ist gering genug für das Jammen. + + + + A session is still possible but it may be harder to play. + Man kann noch spielen aber es wird schwieriger Lieder mit hohem Tempo zu spielen. + + + + The delay is too large for jamming. + Die Verzögerung ist zu hoch zum Jammen. + + + + If this LED indicator turns red, you will not have much fun using the application. + Wenn diese LED rot leuchtet, dann wirst du keinen Spaß haben mit der Software. + + + + The buffers status LED shows the current audio/streaming status. If the light is red, the audio stream is interrupted. This is caused by one of the following problems: + Die Status-LED für den Netzwerkpuffer zeigt den aktuellen Status des Netzwerkstroms. Wenn die LED grün ist, dann gibt es keine Pufferprobleme. Wenn die LED rot anzeigt, dann ist der Netzwerkstrom kurz unterbrochen. Dies kann folgende Ursachen haben: + + + + The sound card's buffer delay (buffer size) is too small (see Settings window). + Der Soundkartenpuffer ist zu klein eingestellt. + + + + The upload or download stream rate is too high for your internet bandwidth. + Die Upload-Rate der Internetverbindung ist zu klein für den Netzwerkdatenstrom. + + + Buffers status LED indicator LED Statuslampe für den Netzwerkpuffer - - + + C&onnect &Verbinden - + + software upgrade available + Softwareupdate verfügbar + + + + &File + &Datei + + + &View &Ansicht - + &Connection Setup... &Verbinden... - + My &Profile... Mein &Profil... - + C&hat... C&hat... - + &Settings... &Einstellungen... - + &Analyzer Console... - + + Use &Two Rows Mixer Panel + Benu&tze zwei Zeilen für das Mischpult + + + + &Clear All Stored Solo and Mute Settings + &Lösche alle gespeicherten Solo- und Mute-Einstellungen + + + + Your sound card is not working correctly. Please open the settings dialog and check the device selection and the driver settings. + Die Soundkarte funktioniert nicht ordnungsgemäß. Bitte überprüfe die Soundkartenauswahl und die Einstellungen der Soundkarte. + + + + Ok + Ok + + + E&xit &Beenden - + + &Edit + B&earbeiten + + + &Sort Users by Name + &Sortiere Kanäle nach Namen + + None - Keine + Keine - + Center Mitte - + R - - + + L - 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 - Mit diesem Einstellregler kann der relative Pegel vom linken und rechten Eingangskanal verändert werden. Für ein Mono-Signal verhält sich der Regler wie ein Pan-Regler. Wenn, z.B., ein Mikrofon am rechten Kanal angeschlossen ist und das Instrument am linken Eingangskanal ist viel lauter als das Mikrofon, dann kann man den Lautstärkeunterschied mit diesem Regler kompensieren indem man den Regler in eine Richtung verschiebt, so dass über dem Regler + Mit diesem Einstellregler kann der relative Pegel vom linken und rechten Eingangskanal verändert werden. Für ein Mono-Signal verhält sich der Regler wie ein Pan-Regler. Wenn, z.B., ein Mikrofon am rechten Kanal angeschlossen ist und das Instrument am linken Eingangskanal ist viel lauter als das Mikrofon, dann kann man den Lautstärkeunterschied mit diesem Regler kompensieren indem man den Regler in eine Richtung verschiebt, so dass über dem Regler - + , where angezeigt wird, wobei - + is the current attenuation indicator. die aktuelle Dämpfung anzeigt. - Reverberation Level - Halleffektregler + Halleffektregler - 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. - Der Halleffekt kann auf einen selektierten Mono-Audiokanal oder auf beide Stereoaudiokanäle angewendet werden. Die Mono-Kanalselektion und die Hallstärke können eingestellt werden. Wenn z.B. ein Mikrofonsignal auf dem rechten Kanal anliegt und ein Halleffekt soll auf das Mikrofonsignal angewendet werden, dann muss die Hallkanalselektion auf rechts eingestellt werden und der Hallregler muss erhöht werden, bis die gewünschte Stärke des Halleffekts erreicht ist. + Der Halleffekt kann auf einen selektierten Mono-Audiokanal oder auf beide Stereoaudiokanäle angewendet werden. Die Mono-Kanalselektion und die Hallstärke können eingestellt werden. Wenn z.B. ein Mikrofonsignal auf dem rechten Kanal anliegt und ein Halleffekt soll auf das Mikrofonsignal angewendet werden, dann muss die Hallkanalselektion auf rechts eingestellt werden und der Hallregler muss erhöht werden, bis die gewünschte Stärke des Halleffekts erreicht ist. - The reverberation effect requires significant CPU so 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 switched off and does not cause any additional CPU usage. - Die Berechnung des Halleffekts benötigt etwas Rechenleistung, so dass der Halleffekt nur bei schnellen Computern angewendet werden sollte. Wenn der Hallregler ganz nach unten gezogen ist, dann ist der Halleffekt ausgeschaltet und verbraucht keine Rechenleistung mehr. + Die Berechnung des Halleffekts benötigt etwas Rechenleistung, so dass der Halleffekt nur bei schnellen Computern angewendet werden sollte. Wenn der Hallregler ganz nach unten gezogen ist, dann ist der Halleffekt ausgeschaltet und verbraucht keine Rechenleistung mehr. - Reverberation Channel Selection - Halleffekt Kanalselektion + Halleffekt Kanalselektion - 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. - Mit diesen Knöpfen kann ausgewählt werden, auf welches Eingangssignal der Halleffekt angewendet werden soll. Entweder der rechte oder linke Eingangskanal kann ausgewählt werden. + Mit diesen Knöpfen kann ausgewählt werden, auf welches Eingangssignal der Halleffekt angewendet werden soll. Entweder der rechte oder linke Eingangskanal kann ausgewählt werden. - + Delay Status LED Status LED für die Verzögerung - 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 large for jamming. - Die Status-LED für die Verzögerung zeigt eine Bewertung der Gesamtverzögerung des Audiosignals. Wenn die LED grün leuchtet, dann ist die Verzögerung gering genug für das Jammen. Wenn die LED gelb anzeigt kann man noch spielen aber es wird schwieriger Lieder mit hohem Tempo zu spielen. Wenn die LED rot anzeigt, dann ist die Verzögerung zu hoch zum Jammen. + Die Status-LED für die Verzögerung zeigt eine Bewertung der Gesamtverzögerung des Audiosignals. Wenn die LED grün leuchtet, dann ist die Verzögerung gering genug für das Jammen. Wenn die LED gelb anzeigt kann man noch spielen aber es wird schwieriger Lieder mit hohem Tempo zu spielen. Wenn die LED rot anzeigt, dann ist die Verzögerung zu hoch zum Jammen. - + Buffers Status LED Status LED für den Netzwerkpuffer - 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: - Die Status-LED für den Netzwerkpuffer zeigt den aktuellen Status des Netzwerkstroms. Wenn die LED grün ist, dann gibt es keine Pufferprobleme. Wenn die LED rot anzeigt, dann ist der Netzwerkstrom kurz unterbrochen. Dies kann folgende Ursachen haben: + Die Status-LED für den Netzwerkpuffer zeigt den aktuellen Status des Netzwerkstroms. Wenn die LED grün ist, dann gibt es keine Pufferprobleme. Wenn die LED rot anzeigt, dann ist der Netzwerkstrom kurz unterbrochen. Dies kann folgende Ursachen haben: - + The network jitter buffer is not large enough for the current network/audio interface jitter. Der Netzwerkpuffer ist nicht groß genug eingestellt für die aktuellen Netzwerkbedingungen. - The sound card buffer delay (buffer size) is set to too small a value. - Der Soundkartenpuffer ist zu klein eingestellt. + Der Soundkartenpuffer ist zu klein eingestellt. - The upload or download stream rate is too high for the current available internet bandwidth. - Die Upload-Rate der Internetverbindung ist zu klein für den Netzwerkdatenstrom. + Die Upload-Rate der Internetverbindung ist zu klein für den Netzwerkdatenstrom. - + The CPU of the client or server is at 100%. Die CPU des Computers ist voll ausgelastet. - + + &Load Mixer Channels Setup... + &Laden der Konfiguration der Mixerkanäle... + + + + &Save Mixer Channels Setup... + &Speichern der Konfiguration der Mixerkanäle... + + + + N&o User Sorting + Keine Kanals&ortierung + + + + Sort Users by &Name + Sortiere die Kanäle nach dem &Namen + + + + Sort Users by &Instrument + Sortiere die Kanäle nach dem &Instrument + + + + Sort Users by &Group + Sortiere die Kanäle nach der &Gruppe + + + + Sort Users by &City + Sortiere die Kanäle nach der &Stadt + + + &Clear All Stored Solo Settings + &Lösche alle gespeicherten Solo-Einstellungen + + + + Set All Faders to New Client &Level + Setze alle Lautstärken auf den &Pegel für neuen Teilnehmer + + + Central Server Zentralserver - + + + Select Channel Setup File + Auswählen der Datei für die Konfiguration der Mixerkanäle + + + user Musiker - + users Musiker - + The soundcard device does not work correctly. Please check the device selection and the driver settings. + Die Soundkarte funktioniert nicht ordnungsgemäß. Bitte überprüfe die Soundkartenauswahl und die Einstellungen der Soundkarte. + + + D&isconnect &Trennen @@ -713,367 +1055,372 @@ CClientDlgBase - + Delay Verzögerung - + Buffers Puffer - + Input Eingang - + L - + R - - Settings - Einstellungen + + &Mute Myself + Stu&mmschalten - - Chat - Chat + + &Settings + Ein&stellungen - - Mute Myself - Stummschalten + + &Chat + &Chat - + C&onnect &Verbinden - + Pan - + Center Mitte - + Reverb Halleffekt - + Left Links - + Right Rechts + + + MUTED (Other people won't hear you) + Stumm (Die anderen hören dich nicht) + + + + Update check + Update check + + + MUTED (You are not sending any audio to the server) + Stumm (Es wird kein Ton zum Server gesendet) + CClientSettingsDlg - + Jitter Buffer Size Netzwerkpuffergröße - 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). - Der Netzwerkpuffer kompensiert die Netzwerk- und Soundkarten-Timing-Schwankungen. Die Größe des Netzwerkpuffers hat Auswirkungen auf die Qualität des Audiosignals (wie viele Aussetzer auftreten) und die Gesamtverzögerung (je länger der Puffer, desto größer ist die Verzögerung). + Der Netzwerkpuffer kompensiert die Netzwerk- und Soundkarten-Timing-Schwankungen. Die Größe des Netzwerkpuffers hat Auswirkungen auf die Qualität des Audiosignals (wie viele Aussetzer auftreten) und die Gesamtverzögerung (je länger der Puffer, desto größer ist die Verzögerung). - 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 below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun took place and the audio stream is interrupted. - Die Netzwerkpuffergröße kann manuell verstellt werden, jeweils getrennt für die Applikation und den Server. Für den lokalen Netzwerkpuffer werden die Aussetzer durch die LED-Anzeige unter den Reglern angezeigt. Wenn die Lampe rot anzeigt, dann hat ein Pufferüberlauf oder ein Leerlauf des Puffers stattgefunden und der Audiodatenstrom wurde kurz unterbrochen. + Die Netzwerkpuffergröße kann manuell verstellt werden, jeweils getrennt für die Applikation und den Server. Für den lokalen Netzwerkpuffer werden die Aussetzer durch die LED-Anzeige unter den Reglern angezeigt. Wenn die Lampe rot anzeigt, dann hat ein Pufferüberlauf oder ein Leerlauf des Puffers stattgefunden und der Audiodatenstrom wurde kurz unterbrochen. - + The jitter buffer setting is therefore a trade-off between audio quality and overall delay. Die Netzwerkpuffergröße ist deshalb ein Kompromiss zwischen Audioqualität und Gesamtverzögerung. - 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 Auto check is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). - Die Netzwerkpuffergröße kann automatisch eingestellt werden. Wenn die Automatik aktiviert ist, dann werden die Netzwerkpuffer der Applikation und des Servers getrennt basierend auf Messungen der Netzwerkschwankungen eingestellt. Wenn die Automatik aktiviert ist, dann sind die beiden Regler gesperrt für die manuelle Verstellung (sie können nicht mit der Maus verändert werden). + Die Netzwerkpuffergröße kann automatisch eingestellt werden. Wenn die Automatik aktiviert ist, dann werden die Netzwerkpuffer der Applikation und des Servers getrennt basierend auf Messungen der Netzwerkschwankungen eingestellt. Wenn die Automatik aktiviert ist, dann sind die beiden Regler gesperrt für die manuelle Verstellung (sie können nicht mit der Maus verändert werden). - If 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 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 with a red light. - Wenn die Automatik zum Einstellen der Netzwerkpuffer aktiviert ist, dann werden die Netzwerkpuffer der Applikation und des entfernten Servers auf einen konservativen Wert eingestellt, um eine möglichst gute Audioqualität zu garantieren. Um die Gesamtverzögerung zu optimieren, bietet es sich an, die Automatik zu deaktivieren und die Netzwerkpuffer etwas kleiner einzustellen. Die Werte sollte man so weit reduzieren, bis die Audioqualität gerade noch der persönlichen Akzeptanz entspricht. Die LED-Anzeige hilft dabei die Audioaussetzer verursacht durch den lokalen Netzwerkpuffer zu visualisieren (wenn die LED rot leuchtet). + Wenn die Automatik zum Einstellen der Netzwerkpuffer aktiviert ist, dann werden die Netzwerkpuffer der Applikation und des entfernten Servers auf einen konservativen Wert eingestellt, um eine möglichst gute Audioqualität zu garantieren. Um die Gesamtverzögerung zu optimieren, bietet es sich an, die Automatik zu deaktivieren und die Netzwerkpuffer etwas kleiner einzustellen. Die Werte sollte man so weit reduzieren, bis die Audioqualität gerade noch der persönlichen Akzeptanz entspricht. Die LED-Anzeige hilft dabei die Audioaussetzer verursacht durch den lokalen Netzwerkpuffer zu visualisieren (wenn die LED rot leuchtet). - + Local jitter buffer slider control Lokale Netzwerkpuffergröße Schieberegler - + Server jitter buffer slider control Server Netzwerkpuffergröße Schieberegler - + Auto jitter buffer switch Automatik für die Netzwerkpuffergröße aktivieren - + Jitter buffer status LED indicator Netzwerkpuffer Status LED - + Sound Card Device Soundkartengerät - + The ASIO driver (sound card) can be selected using Der ASIO-Treiber (Soundkarte) kann ausgewählt werden mit der - + 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. Software unter Windows. Unter MacOS und Linux kann man die Soundkarte nicht auswählen. Wenn der selektierte ASIO-Treiber nicht gültig ist, dann wird eine Fehlermeldung angezeigt und der vorherige gültige Treiber wird wieder ausgewählt. - + If the driver is selected during an active connection, the connection is stopped, the driver is changed and the connection is started again automatically. Wenn der Treiber während eine aktiven Verbindung ausgewählt wird, dann wird die Verbindung gestoppt, der neue Treiber ausgewählt und anschließend wird die Verbindung automatisch wiederhergestellt. - + Sound card device selector combo box Soundkarten Auswahlbox - + If 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. Falls der ASIO4All-Treiber verwendet wird, kann es sein, dass dieser Treiber zusätzliche 10-30 ms Verzögerung hinzufügt. Aus diesem Grund sollte man bevorzugt einen nativen ASIO-Treiber der Soundkarte verwenden, der mit dem Produkt mitgeliefert wurde. - + If you are using the kX ASIO driver, make sure to connect the ASIO inputs in the kX DSP settings panel. Falls der kx ASIO-Treiber verwendet wird, dann muss man darauf achten, dass die ASIO-Eingänge im kx DSP-Einstellungsfenster verbunden sind. - + Sound Card Channel Mapping Soundkarten Kanalzuweisung - + If the selected sound card device offers more than one input or output channel, the Input Channel Mapping and Output Channel Mapping settings are visible. Falls die ausgewählte Soundkarte mehr als zwei Eingangs- oder Ausgangskanäle unterstützt, dann werden die Steuerelemente für die Kanalzuweisung angezeigt. - + For each Für jeden - + input/output channel (Left and Right channel) a different actual sound card channel can be selected. Eingangs-/Ausgangskanal (linker und rechter Kanal) kann ein beliebiger Soundkartenkanal zugewiesen werden. - + Left input channel selection combo box Linker Eingang Kanalauswahlbox - + Right input channel selection combo box Rechter Eingang Kanalauswahlbox - + Left output channel selection combo box Linker Ausgang Kanalauswahlbox - + Right output channel selection combo box Rechter Ausgang Kanalauswahlbox - + Enable Small Network Buffers Aktiviere kleine Netzwerkpuffer - + 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 Falls aktiviert wird die Unterstützung für sehr kleine Netzwerk-Audiopakete aktiviert. Sehr kleine Netzwerkpakete werden nur dann verwendet, wenn der Soundkartenpuffer kleiner als - + samples. The smaller the network buffers, the lower the audio latency. But at the same time the network load increases and the probability of audio dropouts also increases. Samples ist. Je kleiner die Netzwerkpakete sind, desto kleiner ist auch die Audioverzögerung. Aber gleichzeitig wird dadurch die Netzwerklast erhöht und die Wahrscheinlichkeit für Audioaussetzer erhöht sich dadurch auch. - + Enable small network buffers check box Aktiviere kleine Netzwerkpuffer Schalter - + Sound Card Buffer Delay Soundkarten Puffergröße - + + Central server address combo box + Zentralserveradresse Combo-Box + + + + Fancy + Schick + + + + Compact + Kompakt + + The buffer delay setting is a fundamental setting of the - Die Soundkartenpuffergröße ist eine fundamentale Einstellung der + Die Soundkartenpuffergröße ist eine fundamentale Einstellung der - software. This setting has influence on many connection properties. - Software. Diese Einstellung hat Einfluss auf viele andere Verbindungseigenschaften. + Software. Diese Einstellung hat Einfluss auf viele andere Verbindungseigenschaften. - + Three buffer sizes are supported Drei Puffergrößen werden unterstützt - 64 samples: This is the preferred setting since it provides the lowest latency but does not work with all sound cards. - 64 Samples: Dies ist die bevorzugte Einstellung weil es die geringste Verzögerung hat. Diese Puffergröße funktioniert allerdings nicht mit allen Soundkarten. + 64 Samples: Dies ist die bevorzugte Einstellung weil es die geringste Verzögerung hat. Diese Puffergröße funktioniert allerdings nicht mit allen Soundkarten. - 128 samples: This setting should work for most available sound cards. - 128 Samples: Diese Puffergröße sollte mit den meisten Soundkarten funktionieren. + 128 Samples: Diese Puffergröße sollte mit den meisten Soundkarten funktionieren. - 256 samples: This setting should only be used if only a very slow computer or a slow internet connection is available. - 256 Samples: Diese Einstellung sollte nur dann verwendet werden, wenn man einen langsamen Computer oder eine langsame Internetverbindung hat. + 256 Samples: Diese Einstellung sollte nur dann verwendet werden, wenn man einen langsamen Computer oder eine langsame Internetverbindung hat. - Some sound card drivers do not allow the buffer delay to be changed from within the - Manche Soundkartentreiber unterstützen nicht das Verändern der Puffergröße innerhalb der + Manche Soundkartentreiber unterstützen nicht das Verändern der Puffergröße innerhalb der - 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. - Software. In diesem Fall ist die Einstellungsmöglichkeit für die Puffergröße deaktiviert. Die Puffergröße muss man stattdessen direkt im Soundkartentreiber durchführen. Unter Windows kann man den ASIO-Einstellungen Knopf drücken um die Treibereinstellungen zu öffnen. Unter Linux benutzt man das Jack-Konfigurationsprogramm um die Puffergröße einzustellen. + Software. In diesem Fall ist die Einstellungsmöglichkeit für die Puffergröße deaktiviert. Die Puffergröße muss man stattdessen direkt im Soundkartentreiber durchführen. Unter Windows kann man den ASIO-Einstellungen Knopf drücken um die Treibereinstellungen zu öffnen. Unter Linux benutzt man das Jack-Konfigurationsprogramm um die Puffergröße einzustellen. - If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The - Falls keiner der vorgegebenen Puffergrößen ausgeählt ist und alle Einstellungen deaktiviert sind, dann wird eine nicht unterstützte Puffergröße im Soundkartentreiber verwendet. Die + Falls keiner der vorgegebenen Puffergrößen ausgeählt ist und alle Einstellungen deaktiviert sind, dann wird eine nicht unterstützte Puffergröße im Soundkartentreiber verwendet. Die - software will still work with this setting but with restricted performance. - Software funktioniert trotzdem aber es könnte eine größere Verzögerung resultieren. + Software funktioniert trotzdem aber es könnte eine größere Verzögerung resultieren. - + 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 a red light in the status indicator (drop outs) and the higher the upload rate and the lower the overall delay. Die Puffergröße hat einen Einfluss auf den Verbindungsstatus, die aktuelle Upload-Rate und die Gesamtverzögerung. Je kleiner der Puffer, desto größer ist die Wahrscheinlichkeit für das Auftreten einer rot leuchtenden LED (was Audioaussetzer anzeigt), eine höheren Upload-Rate und eine niedrigere Gesamtverzögerung. - + The buffer setting is therefore a trade-off between audio quality and overall delay. Die Puffergröße ist somit ein Kompromiss zwischen Audioqualität und Gesamtverzögerung. - If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the - Falls die Puffergröße nicht verstellt werden kann, dann hat der Soundkartentreiber die Einstellung gesperrt man kann es nicht innerhalb der + Falls die Puffergröße nicht verstellt werden kann, dann hat der Soundkartentreiber die Einstellung gesperrt man kann es nicht innerhalb der - software. 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. - Software verändern. Unter Windows kann man den ASIO-Einstellungen Knopf drücken, um die Treibereinstellungen zu öffnen. Unter Linux kann man ein Jack-Konfigurationswerkzeug verwenden, um die Puffergröße zu verändern. + Software verändern. Unter Windows kann man den ASIO-Einstellungen Knopf drücken, um die Treibereinstellungen zu öffnen. Unter Linux kann man ein Jack-Konfigurationswerkzeug verwenden, um die Puffergröße zu verändern. - + 64 samples setting radio button 64 Samples Einstellknopf - + 128 samples setting radio button 128 Samples Einstellknopf - + 256 samples setting radio button 256 Samples Einstellknopf - + ASIO setup push button ASIO-Einstellungen Knopf - Fancy Skin - Schicke Oberfläche + Schicke Oberfläche - If enabled, a fancy skin will be applied to the main window. - Falls aktiviert wird eine schicke Oberfläche im Hauptfenster verwendet. + Falls aktiviert wird eine schicke Oberfläche im Hauptfenster verwendet. - Fancy skin check box - Schicke Oberfläche Schalter + Schicke Oberfläche Schalter - Display Channel Levels - Zeige Kanalsignalpegel + Zeige Kanalsignalpegel - If enabled, each client channel will display a pre-fader level bar. - Falls aktiviert wird eine Signalpegelanzeige neben jedem Kanalfader angezeigt, welcher den Pegel vor dem Fader anzeigt. + Falls aktiviert wird eine Signalpegelanzeige neben jedem Kanalfader angezeigt, welcher den Pegel vor dem Fader anzeigt. - Display channel levels check box - Zeige Kanalpegel Schalter + Zeige Kanalpegel Schalter - + Audio Channels Audiokanäle - 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 mono-in/stereo-out mode the audio signal which is sent to the server is mono but the return signal is stereo. This is useful if the sound card has 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. - Hiermit kann man die Anzahl an Audiokanälen auswählen. Es gibt drei Modi. Die Mono- und Stereomodi verwenden jeweils einen oder zwei Kanäle. Im Mono-In/Stereo-Out Modus wird ein Monosignal zum Server geschickt aber es kommt ein Stereo-Signal zurück vom Server. Dies ist nützlich für den Fall, dass man an die Soundkarte ein Instrument an den einen Eingangskanal und ein Mikrofon an den anderen Eingangskanal angeschlossen hat. In diesem Fall können die beiden Signale zusammen gemischt werden und an den Server geschickt werden aber man kann das Stereo-Signal von den anderen Musikern hören. + Hiermit kann man die Anzahl an Audiokanälen auswählen. Es gibt drei Modi. Die Mono- und Stereomodi verwenden jeweils einen oder zwei Kanäle. Im Mono-In/Stereo-Out Modus wird ein Monosignal zum Server geschickt aber es kommt ein Stereo-Signal zurück vom Server. Dies ist nützlich für den Fall, dass man an die Soundkarte ein Instrument an den einen Eingangskanal und ein Mikrofon an den anderen Eingangskanal angeschlossen hat. In diesem Fall können die beiden Signale zusammen gemischt werden und an den Server geschickt werden aber man kann das Stereo-Signal von den anderen Musikern hören. - 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. - Wenn man den Stereo-Modus verwendet, dann ist die Übertragungsrate etwas höher. Man muss sicher stellen, dass die Internetverbindung die höhere Rate übertragen kann. + Wenn man den Stereo-Modus verwendet, dann ist die Übertragungsrate etwas höher. Man muss sicher stellen, dass die Internetverbindung die höhere Rate übertragen kann. - In 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. - Wenn der Stereo-Modus ausgewählt wurde, dann verschwindet die Kanalselektion für den Halleffekt im Hauptfenster, da der Effekt auf beide Stereokanäle angewendet wird. + Wenn der Stereo-Modus ausgewählt wurde, dann verschwindet die Kanalselektion für den Halleffekt im Hauptfenster, da der Effekt auf beide Stereokanäle angewendet wird. @@ -1086,39 +1433,36 @@ Audioqualität - 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 upload rate does not exceed the available bandwidth of your internet connection. - Wählt die gewünschte Audioqualität aus. Es wird eine niedrige, mittlere und hohe Audioqualität angeboten. Je höher die Audioqualität, desto höher ist die Netzwerkübertragungsrate. Man muss sicherstellen, dass die Internetverbindung die höhere Rate übertragen kann. + Wählt die gewünschte Audioqualität aus. Es wird eine niedrige, mittlere und hohe Audioqualität angeboten. Je höher die Audioqualität, desto höher ist die Netzwerkübertragungsrate. Man muss sicherstellen, dass die Internetverbindung die höhere Rate übertragen kann. - + Audio quality combo box Audioqualität Auswahlbox - + New Client Level Pegel für neuen Teilnehmer - 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 of that client was already stored. - Der Pegel für neue Teilnehmer definiert die Fadereinstellung, wenn sich ein Teilnehmer neu mit dem Server verbindet. D.h. wenn ein neuer Fader erscheint, dann wird er auf den voreingestellten Pegel gesetzt. Eine Ausnahme bildet der Fall, dass der Teilnehmer vorher schon mal mit dem Server verbunden war und der Pegel gespeichert war. + Der Pegel für neue Teilnehmer definiert die Fadereinstellung, wenn sich ein Teilnehmer neu mit dem Server verbindet. D.h. wenn ein neuer Fader erscheint, dann wird er auf den voreingestellten Pegel gesetzt. Eine Ausnahme bildet der Fall, dass der Teilnehmer vorher schon mal mit dem Server verbunden war und der Pegel gespeichert war. - + New client level edit box Neuer Teilnehmer Pegel Einstellbox - + Custom Central Server Address Benutzerdefinierte Zentralserveradresse - The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. This address is only used if the custom server list is selected in the connection dialog. - Die Zentralserveradresse ist die IP-Adresse oder URL des Zentralservers, der die Serverliste organisiert und bereitstellt. Diese Adresse wird nur benutzt, wenn die benutzerdefinierte Serverliste im Verbindungsdialog ausgewählt wird. + Die Zentralserveradresse ist die IP-Adresse oder URL des Zentralservers, der die Serverliste organisiert und bereitstellt. Diese Adresse wird nur benutzt, wenn die benutzerdefinierte Serverliste im Verbindungsdialog ausgewählt wird. Central Server Address @@ -1133,72 +1477,216 @@ Voreingestellter Zentralservertyp Auswahlbox - Central server address line edit - Zentralserveradresse Eingabefeld + Zentralserveradresse Eingabefeld - + Current Connection Status Parameter Verbindungsstatus Parameter - The ping time is the time required for the audio stream to travel from the client to the server and back again. 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. - Die Ping-Zeit ist die Zeit, die der Audiodatenstrom benötigt, um von der Applikation zum Server und zurück zu kommen. Diese Verzögerung wird vom Netzwerk hervorgerufen. Diese Verzögerung sollte so um die 20-30 ms sein. Falls die Verzögerung größer ist (z.B. 50-60 ms), der Abstand zum Server ist zu groß oder die Internetverbindung ist nicht ausreichend. + Die Ping-Zeit ist die Zeit, die der Audiodatenstrom benötigt, um von der Applikation zum Server und zurück zu kommen. Diese Verzögerung wird vom Netzwerk hervorgerufen. Diese Verzögerung sollte so um die 20-30 ms sein. Falls die Verzögerung größer ist (z.B. 50-60 ms), der Abstand zum Server ist zu groß oder die Internetverbindung ist nicht ausreichend. - The overall delay is calculated from the current ping time and the delay which is introduced by the current buffer settings. - Die Gesamtverzögerung setzt sich zusammen aus der Ping-Zeit und die Verzögerung, die durch die Puffergrößen verursacht wird. + Die Gesamtverzögerung setzt sich zusammen aus der Ping-Zeit und die Verzögerung, die durch die Puffergrößen verursacht wird. - 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 your internet connection by, e.g., using speedtest.net). - Die Upload-Rate hängt von der Soundkartenpuffergröße und die Audiokomprimierung ab. Man muss sicher stellen, dass die Upload-Rate immer kleiner ist als die Rate, die die Internetverbindung zur Verfügung stellt (man kann die Upload-Rate des Internetproviders z.B. mit speedtest.net überprüfen). + Die Upload-Rate hängt von der Soundkartenpuffergröße und die Audiokomprimierung ab. Man muss sicher stellen, dass die Upload-Rate immer kleiner ist als die Rate, die die Internetverbindung zur Verfügung stellt (man kann die Upload-Rate des Internetproviders z.B. mit speedtest.net überprüfen). - + If this LED indicator turns red, you will not have much fun using the Wenn diese LED rot leuchtet, dann wirst du keinen Spaß haben mit der - + software. Software. - + ASIO Setup ASIO-Einstellung - + + Mono - + + mode will increase your stream's data rate. Make sure your upload rate does not exceed the available upload speed of your internet connection. + Modus ist die Übertragungsrate etwas höher. Man muss sicher stellen, dass die Internetverbindung die höhere Rate übertragen kann. + + + Mono-in/Stereo-out Mono-In/Stereo-Out - - Stereo - + + + + Stereo + + + + + The jitter buffer compensates for network and sound card timing jitters. The size of the buffer therefore influences the quality of the audio stream (how many dropouts occur) and the overall delay (the longer the buffer, the higher the delay). + Der Netzwerkpuffer kompensiert die Netzwerk- und Soundkarten-Timing-Schwankungen. Die Größe des Netzwerkpuffers hat Auswirkungen auf die Qualität des Audiosignals (wie viele Aussetzer auftreten) und die Gesamtverzögerung (je länger der Puffer, desto größer ist die Verzögerung). + + + + You can set the jitter buffer size manually for the local client and the remote server. For the local jitter buffer, dropouts in the audio stream are indicated by the light below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun has taken place and the audio stream is interrupted. + Die Netzwerkpuffergröße kann manuell verstellt werden, jeweils getrennt für die Applikation und den Server. Für den lokalen Netzwerkpuffer werden die Aussetzer durch die LED-Anzeige unter den Reglern angezeigt. Wenn die Lampe rot anzeigt, dann hat ein Pufferüberlauf oder ein Leerlauf des Puffers stattgefunden und der Audiodatenstrom wurde kurz unterbrochen. + + + + If the Auto setting 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 Auto is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). + Die Netzwerkpuffergröße kann automatisch eingestellt werden. Wenn die Automatik aktiviert ist, dann werden die Netzwerkpuffer der Applikation und des Servers getrennt basierend auf Messungen der Netzwerkschwankungen eingestellt. Wenn die Automatik aktiviert ist, dann sind die beiden Regler gesperrt für die manuelle Verstellung (sie können nicht mit der Maus verändert werden). + + + + If the Auto setting 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 tweak the audio delay/latency it is recommended to disable the Auto setting and to lower the jitter buffer size manually by using the sliders until your personal acceptable amount of dropouts is reached. The LED indicator will display the audio dropouts of the local jitter buffer with a red light. + Wenn die Automatik zum Einstellen der Netzwerkpuffer aktiviert ist, dann werden die Netzwerkpuffer der Applikation und des entfernten Servers auf einen konservativen Wert eingestellt, um eine möglichst gute Audioqualität zu garantieren. Um die Gesamtverzögerung zu optimieren, bietet es sich an, die Automatik zu deaktivieren und die Netzwerkpuffer etwas kleiner einzustellen. Die Werte sollte man so weit reduzieren, bis die Audioqualität gerade noch der persönlichen Akzeptanz entspricht. Die LED-Anzeige hilft dabei die Audioaussetzer verursacht durch den lokalen Netzwerkpuffer zu visualisieren (wenn die LED rot leuchtet). + + + + The buffer delay setting is a fundamental setting of this software. This setting has an influence on many connection properties. + Die Soundkartenpuffergröße ist eine fundamentale Einstellung der Software. Diese Einstellung hat Einfluss auf viele andere Verbindungseigenschaften. + + + + 64 samples: The preferred setting. Provides the lowest latency but does not work with all sound cards. + 64 Samples: Dies ist die bevorzugte Einstellung weil es die geringste Verzögerung hat. Diese Puffergröße funktioniert allerdings nicht mit allen Soundkarten. + + + + 128 samples: Should work for most available sound cards. + 128 Samples: Diese Puffergröße sollte mit den meisten Soundkarten funktionieren. + + + + 256 samples: Should only be used on very slow computers or with a slow internet connection. + 256 Samples: Diese Einstellung sollte nur dann verwendet werden, wenn man einen langsamen Computer oder eine langsame Internetverbindung hat. + + + + Some sound card drivers do not allow the buffer delay to be changed from within the application. In this case the buffer delay setting is disabled and has to be changed using 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. + Manche Soundkartentreiber unterstützen nicht das Verändern der Puffergröße innerhalb der Software. In diesem Fall ist die Einstellungsmöglichkeit für die Puffergröße deaktiviert. Die Puffergröße muss man stattdessen direkt im Soundkartentreiber durchführen. Unter Windows kann man den ASIO-Einstellungen Knopf drücken um die Treibereinstellungen zu öffnen. Unter Linux benutzt man das Jack-Konfigurationsprogramm um die Puffergröße einzustellen. + + + + If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The application will still work with this setting but with restricted performance. + Falls keiner der vorgegebenen Puffergrößen ausgeählt ist und alle Einstellungen deaktiviert sind, dann wird eine nicht unterstützte Puffergröße im Soundkartentreiber verwendet. Die Software funktioniert trotzdem aber es könnte eine größere Verzögerung resultieren. + + + + If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the software. 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. + Falls keiner der vorgegebenen Puffergrößen ausgeählt ist und alle Einstellungen deaktiviert sind, dann wird eine nicht unterstützte Puffergröße im Soundkartentreiber verwendet. Unter Windows kann man den ASIO-Einstellungen Knopf drücken, um die Treibereinstellungen zu öffnen. Unter Linux kann man ein Jack-Konfigurationswerkzeug verwenden, um die Puffergröße zu verändern. + + + + Skin + Oberfläche + + + + Select the skin to be used for the main window. + Wählt die Oberfläche aus, die für das Hauptfenster verwendet werden soll. + + + + Skin combo box + Oberfläche Combo-Box + + + + Selects the number of audio channels to be used for communication between client and server. There are three modes available: + Hiermit kann man die Anzahl an Audiokanälen auswählen. Es gibt drei Modi: + + + + and + und + + + + These modes use one and two audio channels respectively. + Diese Modi verwenden jeweils einen oder zwei Audiokanäle. + + + + Mono in/Stereo-out + + + + + The audio signal sent to the server is mono but the return signal is stereo. This is useful if the sound card has the instrument on one input channel and the microphone on the other. In that case the two input signals can be mixed to one mono channel but the server mix is heard in stereo. + Ein Monosignal wird zum Server geschickt aber es kommt ein Stereo-Signal zurück vom Server. Dies ist nützlich für den Fall, dass man an die Soundkarte ein Instrument an den einen Eingangskanal und ein Mikrofon an den anderen Eingangskanal angeschlossen hat. In diesem Fall können die beiden Signale zusammen gemischt werden und an den Server geschickt werden aber man kann das Stereo-Signal von den anderen Musikern hören. + + + + Enabling + Im + + + mode will increase your stream's data rate. Make sure your upload rate does not exceed the available upload speed of your internet connection. + Modus ist die Übertragungsrate etwas höher. Man muss sicher stellen, dass die Internetverbindung die höhere Rate übertragen kann. + + + + In stereo streaming mode, no audio channel selection for the reverb effect will be available on the main window since the effect is applied to both channels in this case. + Wenn der Stereo-Modus ausgewählt wurde, dann verschwindet die Kanalselektion für den Halleffekt im Hauptfenster, da der Effekt auf beide Stereokanäle angewendet wird. + + + + The higher the audio quality, the higher your audio stream's data rate. Make sure your upload rate does not exceed the available bandwidth of your internet connection. + Je höher die Audioqualität, desto höher ist die Netzwerkübertragungsrate. Man muss sicherstellen, dass die Internetverbindung die höhere Rate übertragen kann. + + + + This setting defines the fader level of a newly connected client in percent. If a new client connects to the current server, they will get the specified initial fader level if no other fader level from a previous connection of that client was already stored. + Der Pegel für neue Teilnehmer definiert die Fadereinstellung, wenn sich ein Teilnehmer neu mit dem Server verbindet. D.h. wenn ein neuer Fader erscheint, dann wird er auf den voreingestellten Pegel gesetzt. Eine Ausnahme bildet der Fall, dass der Teilnehmer vorher schon mal mit dem Server verbunden war und der Pegel gespeichert war. + + + + Leave this blank unless you need to enter the address of a central server other than the default. + Die Zentralserveradresse ist die IP-Adresse oder URL des Zentralservers, der die Serverliste organisiert und bereitstellt. Diese Adresse wird nur benutzt, wenn die benutzerdefinierte Serverliste im Verbindungsdialog ausgewählt wird. + + + + The Ping Time is the time required for the audio stream to travel from the client to the server and back again. This delay is introduced by the network and should be about 20-30 ms. If this delay is higher than about 50 ms, your distance to the server is too large or your internet connection is not sufficient. + Die Ping-Zeit ist die Zeit, die der Audiodatenstrom benötigt, um von der Applikation zum Server und zurück zu kommen. Diese Verzögerung wird vom Netzwerk hervorgerufen. Diese Verzögerung sollte so um die 20-30 ms sein. Falls die Verzögerung größer ist (z.B. 50-60 ms), der Abstand zum Server ist zu groß oder die Internetverbindung ist nicht ausreichend. + + + + Overall Delay is calculated from the current Ping Time and the delay introduced by the current buffer settings. + Die Gesamtverzögerung setzt sich zusammen aus der Ping-Zeit und die Verzögerung, die durch die Puffergrößen verursacht wird. + + + + Audio Upstream Rate depends on the current audio packet size and compression setting. Make sure that the upstream rate is not higher than your available internet upload speed (check this with a service such as speedtest.net). + Die Upload-Rate hängt von der Soundkartenpuffergröße und die Audiokomprimierung ab. Man muss sicher stellen, dass die Upload-Rate immer kleiner ist als die Rate, die die Internetverbindung zur Verfügung stellt (man kann die Upload-Rate des Internetproviders z.B. mit speedtest.net überprüfen). - + Low Niedrig - + + Normal Normal - + High Hoch @@ -1207,22 +1695,22 @@ Manuell - + Custom Benutzerdefiniert - + All Genres Alle Genres - + Genre Rock Genre Rock - + Genre Jazz Genre Jazz @@ -1231,12 +1719,12 @@ Genre Rock/Jazz - - Genre Classical/Folk/Choir + + Genre Classical/Folk/Choral Genre Klassik/Volksmusik/Chor - + Default Standard @@ -1245,23 +1733,23 @@ Standard (Nordamerika) - + preferred bevorzugt - - + + Size: Größe: - + Buffer Delay Puffergröße - + Buffer Delay: Puffergröße: @@ -1270,19 +1758,12 @@ Vordefinierte Adresse - The selected audio device could not be used because of the following error: - Das ausgewählte Audiogerät kann aus folgendem Grund nicht verwendet werden: + Das ausgewählte Audiogerät kann aus folgendem Grund nicht verwendet werden: - The previous driver will be selected. - Der vorherige Treiber wird wieder ausgewählt. - - - - Ok - + Der vorherige Treiber wird wieder ausgewählt. @@ -1365,58 +1846,66 @@ - + Local Lokal - + Server Server - - + + Size Größe - + Misc Sonstiges - + Audio Channels Audiokanäle - + Audio Quality Audioqualität - + New Client Level Pegel für neuen Teilnehmer - + + Skin + Oberfläche + + + + Language + Sprache + + + % - Fancy Skin - Schicke Oberfläche + Schicke Oberfläche - Display Channel Levels - Zeige Signalpegel + Zeige Signalpegel - + Custom Central Server Address: Benutzerdefinierte Zentralserveradresse: @@ -1425,24 +1914,24 @@ Zentralserveradresse: - + Audio Stream Rate Netzwerkrate - - - + + + val Wert - + Ping Time Ping-Zeit - + Overall Delay Gesamtverzögerung @@ -1450,100 +1939,115 @@ CConnectDlg - + Server List Severliste - 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. - Die Serverliste zeigt eine Liste von verfügbaren Server, die sich am Zentralserver registriert haben. Markiere einen Server von der Liste und drücke den Knopf Verbinden um eine Verbindung zu dem Server aufzubauen. Alternativ kann man auch den Server in der Liste direkt doppelklicken. Wenn ein Server belegt ist, dann wird eine Liste der verbundenen Musikern angezeigt. Server, die länger online sind (permanente Server) werden in Fettschrift dargestellt. + Die Serverliste zeigt eine Liste von verfügbaren Server, die sich am Zentralserver registriert haben. Markiere einen Server von der Liste und drücke den Knopf Verbinden um eine Verbindung zu dem Server aufzubauen. Alternativ kann man auch den Server in der Liste direkt doppelklicken. Wenn ein Server belegt ist, dann wird eine Liste der verbundenen Musikern angezeigt. Server, die länger online sind (permanente Server) werden in Fettschrift dargestellt. - 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. - Es kann einen Moment dauern, bis die Serverliste vom Zentralserver empfangen wird. Falls keine gültige Zentralserveradresse in den Einstellungen angegeben ist, kann keine Liste angezeigt werden. + Es kann einen Moment dauern, bis die Serverliste vom Zentralserver empfangen wird. Falls keine gültige Zentralserveradresse in den Einstellungen angegeben ist, kann keine Liste angezeigt werden. - + Server list view Serverliste Anzeige - + Server Address Serveradresse - The IP address or URL of the server running the - Die IP-Adresse oder URL des Servers, auf der die + Die IP-Adresse oder URL des Servers, auf der die - 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: - Serversoftware läuft wird hier angegeben. Optional kann eine Portnummer angefügt werden. Diese wird hinter der IP-Adresse durch ein Doppelpunkt getrennt angegeben. Beispiel: example.org: + Serversoftware läuft wird hier angegeben. Optional kann eine Portnummer angefügt werden. Diese wird hinter der IP-Adresse durch ein Doppelpunkt getrennt angegeben. Beispiel: example.org: - . A list of the most recent used server IP addresses or URLs is available for selection. + . Eine Liste der letzten IP-Adressen oder URLs wird gespeichert und kann nachträglich wieder ausgewählt werden. + + + + The Connection Setup window shows a list of available servers. Server operators can optionally list their servers by music genre. Use the List dropdown to select a genre, click on the server you want to join and press the Connect button to connect to it. Alternatively, double click on on the server name. Permanent servers (those that have been listed for longer than 48 hours) are shown in bold. + Die Serverliste zeigt eine Liste von verfügbaren Server, die sich am Zentralserver registriert haben. Markiere einen Server von der Liste und drücke den Knopf Verbinden um eine Verbindung zu dem Server aufzubauen. Alternativ kann man auch den Server in der Liste direkt doppelklicken. Wenn ein Server belegt ist, dann wird eine Liste der verbundenen Musikern angezeigt. Server, die länger online sind (permanente Server) werden in Fettschrift dargestellt. + + + + If you know the IP address or URL of a server, you can connect to it using the Server name/Address field. An optional port number can be added after the IP address or URL using a colon as a separator, e.g, example.org: + Die IP-Adresse oder URL des Servers, auf der die Serversoftware läuft wird hier angegeben. Optional kann eine Portnummer angefügt werden. Diese wird hinter der IP-Adresse durch ein Doppelpunkt getrennt angegeben. Beispiel: example.org: + + + + . The field will also show a list of the most recently used server addresses. . Eine Liste der letzten IP-Adressen oder URLs wird gespeichert und kann nachträglich wieder ausgewählt werden. - + Server address edit box Serveradresse Eingabefeld - + Holds the current server IP address or URL. It also stores old URLs in the combo box list. Enthält die aktuelle Server-IP-Adresse oder URL. Es speichert auch alte URLs in der Auswahlliste. - + Server List Selection Serverlistenauswahl - + Selects the server list to be shown. Wählt die Serverliste aus, die angezeigt werden soll. - + Server list selection combo box Severlistenauswahl Selektion - + Filter Filter - + The server list is filtered by the given text. Note that the filter is case insensitive. Die Serverliste kann mit dem eingegebenen Text gefiltert werden, d.h. es werden nur Einträge angezeigt, die dem Filtertext entsprechen. Die Groß- und Kleinschreibung des Filtertexts wird dabei nicht beachtet. - + Filter edit box Filtereingabefeld - + Show All Musicians Zeige alle Musiker - + 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. Ist diese Einstellung angehakt, dann werden alle Musiker auf allen Servern angezeigt. Wird der Haken entfernt, dann werden alle Listeneinträge eingeklappt und die verbundenen Musikernamen werden verborgen. - + Show all musicians check box Zeige alle Musiker Schalter + + + Type # for occupied servers + # eingeben für belegte Server + CConnectDlgBase @@ -1589,8 +2093,8 @@ - Server Name/Address - Servername/Adresse + Server Address + Serveradresse @@ -1606,495 +2110,563 @@ CHelpMenu - + &Help &Hilfe - - + + Getting &Started... &Erste Schritte... - + Software &Manual... Software&handbuch... - + What's &This Konte&xthilfe - + &About... Ü&ber... + + CLanguageComboBox + + + Restart Required + Neustart erforderlich + + + + Please restart the application for the language change to take effect. + Bitte starte die Applikation neu um die Änderung der Spracheinstellung anzuwenden. + + CLicenceDlg - I &agree to the above licence terms - Ich &stimme den Lizenzbedingungen zu + Ich &stimme den Lizenzbedingungen zu - + + This server requires you accept conditions before you can join. Please read these in the chat window. + Dieser Server verlangt, dass man Bedingungen akzeptiert, bevor man sich verbinden kann. Bitte ließ die Bedingungen im Chat-Fenster. + + + + I have read the conditions and &agree. + Ich h&abe die Bedingungen gelesen und ich stimme zu. + + + Accept Einwilligen - + Decline Ablehnen - By connecting to this server and agreeing to this notice, you agree to the following: - Durch das Verbinden mit diesem Server und das Akzeptieren des Lizenztextes willigst du folgenden Bedingungen ein: + Durch das Verbinden mit diesem Server und das Akzeptieren des Lizenztextes willigst du folgenden Bedingungen ein: - 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 - + Sie stimmen zu, dass alle Daten, Klänge oder andere Arbeiten, die zum Server gesendet werden, Ihnen gehören oder von Ihnen selbst oder einem Lizenzgeber erstellt wurden und dass Sie diese Daten, Klänge oder andere Arbeiten unter die folgende Creative Commons Lizenz stellen (Für weitere Informationen über die Lizenz, siehe - You are free to: - + Sie dürfen: - Share - + Teilen - copy and redistribute the material in any medium or format - + das Material in jedwedem Format oder Medium vervielfältigen und weiterverbreiten - Adapt - + Bearbeiten - remix, transform, and build upon the material - + das Material remixen, verändern und darauf aufbauen - The licensor cannot revoke these freedoms as long as you follow the license terms. - + Der Lizenzgeber kann diese Freiheiten nicht widerrufen solange Sie sich an die Lizenzbedingungen halten. - Under the following terms: - + Unter folgenden Bedingungen: - Attribution - + Namensnennung - You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. - + Sie müssen angemessene Urheber- und Rechteangaben machen, einen Link zur Lizenz beifügen und angeben, ob Änderungen vorgenommen wurden. Diese Angaben dürfen in jeder angemessenen Art und Weise gemacht werden, allerdings nicht so, dass der Eindruck entsteht, der Lizenzgeber unterstütze gerade Sie oder Ihre Nutzung besonders. - NonCommercial - + Nicht kommerziell - You may not use the material for commercial purposes. - + Sie dürfen das Material nicht für kommerzielle Zwecke nutzen. - ShareAlike - + Weitergabe unter gleichen Bedingungen - If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. - + Wenn Sie das Material remixen, verändern oder anderweitig direkt darauf aufbauen, dürfen Sie Ihre Beiträge nur unter derselben Lizenz wie das Original verbreiten. - No additional restrictions - + Keine weiteren Einschränkungen - You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. - + Sie dürfen keine zusätzlichen Klauseln oder technische Verfahren einsetzen, die anderen rechtlich irgendetwas untersagen, was die Lizenz erlaubt. + + + + CMultiColorLED + + + Red + Rot + + + + Yellow + Gelb + + + + Green + Grün CMusProfDlg - server. This tag will also show up at each client which is connected to the same server as you. If the name is left empty, the IP address is shown instead. - Server. Dieses Schild wird auch bei allen anderen Musikern, die mit dem gleichen Server verbunden sind, angezeigt. Wenn der Name leer gelassen wurde, dann wird die IP-Adresse stattdessen angezeigt. + Server. Dieses Schild wird auch bei allen anderen Musikern, die mit dem gleichen Server verbunden sind, angezeigt. Wenn der Name leer gelassen wurde, dann wird die IP-Adresse stattdessen angezeigt. - + Alias or name edit box Alias oder Name Eingabefeld - + Instrument picture button Instrumentenbild Knopf - + Country flag button Landesflagge Knopf - + City edit box Stadt Eingabefeld - + Skill level combo box Fähigkeit Auswahlbox - - - + + + None Kein - - + + Musician Profile Musikerprofil - + Alias/Name - + Instrument - + Country Land - + City Stadt - + Skill Können - + &Close &Schließen - + Beginner Anfänger - + Intermediate Mittlere Spielstärke - + Expert Experte - 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 in. The city you live in and the skill level playing your instrument may also be added. - Schreibe den Namen oder Alias hier rein so dass die anderen Musikern mit denen du spielst wissen wer du bist. Zusätzlich kannst du dein Instrument auswählen und eine Flagge des Landes auswählen in dem du dich befindest. Deine Stadt und deine Spielstärke des Instruments kannst du ebenso angeben. + Schreibe den Namen oder Alias hier rein so dass die anderen Musikern mit denen du spielst wissen wer du bist. Zusätzlich kannst du dein Instrument auswählen und eine Flagge des Landes auswählen in dem du dich befindest. Deine Stadt und deine Spielstärke des Instruments kannst du ebenso angeben. - What you set here will appear at your fader on the mixer board when you are connected to a - Was man hier sieht wird auch am Fader im Mixer angezeigt, wenn du mit einem + Was man hier sieht wird auch am Fader im Mixer angezeigt, wenn du mit einem + + + + Write your name or an alias here so the other musicians you want to play with know who you are. You may also add a picture of the instrument you play and a flag of the country you are located in. Your city and skill level playing your instrument may also be added. + Schreibe den Namen oder Alias hier rein so dass die anderen Musikern mit denen du spielst wissen wer du bist. Zusätzlich kannst du dein Instrument auswählen und eine Flagge des Landes auswählen in dem du dich befindest. Deine Stadt und deine Spielstärke des Instruments kannst du ebenso angeben. + + + What you set here will appear at your fader on the mixer board when you are connected to a Jamulus server. This tag will also be shown at each client which is connected to the same server as you. If the name is left empty, the IP address is shown instead. + Was man hier sieht wird auch am Fader im Mixer angezeigt, wenn du mit einem Server verbunden bist. Dieses Schild wird auch bei allen anderen Musikern, die mit dem gleichen Server verbunden sind, angezeigt. + + + + What you set here will appear at your fader on the mixer board when you are connected to a Jamulus server. This tag will also be shown at each client which is connected to the same server as you. + Was man hier sieht wird auch am Fader im Mixer angezeigt, wenn du mit einem Server verbunden bist. Dieses Schild wird auch bei allen anderen Musikern, die mit dem gleichen Server verbunden sind, angezeigt. - + Drum Set Schlagzeug - + Djembe Djembe - + Electric Guitar E-Gitarre - + Acoustic Guitar Akustikgitarre - + Bass Guitar E-Bass - + Keyboard Keyboard - + Synthesizer Synthesizer - + Grand Piano Flügel - + Accordion Akkordeon - + Vocal Gesang - + Microphone Mikrofon - + Harmonica Mundharmonika - + Trumpet Trompete - + Trombone Posaune - + French Horn Waldhorn - + Tuba Tuba - + Saxophone Saxophon - + Clarinet Klarinette - + Flute Flöte - + Violin Violine - + Cello Cello - + Double Bass Kontrabass - + Recorder Recorder - + Streamer - + Listener Zuhörer - + Guitar+Vocal Gitarre+Gesang - + Keyboard+Vocal Keyboard+Gesang - + Bodhran - + Bassoon Fagott - + Oboe Oboe - + Harp Harfe - + Viola Viola - + Congas Congas - + Bongo Bongos - + Vocal Bass Gesang Bass - + Vocal Tenor Gesang Tenor - + Vocal Alto Gesang Alt - + Vocal Soprano Gesang Sopran - + Banjo Banjo - + Mandolin Mandoline + + + Ukulele + + + + + Bass Ukulele + + + + + Vocal Baritone + Gesang Bariton + + + + Vocal Lead + Gesang Lead + + + + Mountain Dulcimer + Mountain Dulcimer + + + + Scratching + Scratching + + + + Rapping + Rapper + + + + No Name + Kein Name + CServerDlg - + Client List Musikerliste - + 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. Die Musikerliste zeigt alle gerade mit dem Server verbunden Musiker an. Für jeden Musiker werden zusätzliche Informationen wie die IP-Adresse und Namen angezeigt. - + Connected clients list view Musikerliste - + Start Minimized on Operating System Start Starte minimiert beim Starten des Betriebssystems - If the start minimized on operating system start check box is checked, the - Wenn diese Funktion angehakt ist, dann wird der + Wenn diese Funktion angehakt ist, dann wird der - server will be started when the operating system starts up and is automatically minimized to a system task bar icon. - Server automatisch mit dem Betriebssystemstart geladen und erscheint minimiert in der Systemleiste als Icon. + Server automatisch mit dem Betriebssystemstart geladen und erscheint minimiert in der Systemleiste als Icon. - Show Creative Commons Licence Dialog - Zeige den Creative Commons Lizenzdialog + Zeige den Creative Commons Lizenzdialog - If enabled, a Creative Commons BY-NC-SA 4.0 Licence dialog is shown each time a new user connects the server. - Falls aktiviert wird ein Create Commons BY-NC-SA 4.0 Lizenzdialog angezeigt, wenn ein neuer Teilnehmer versucht sich mit dem Server zu verbinden. + Falls aktiviert wird ein Create Commons BY-NC-SA 4.0 Lizenzdialog angezeigt, wenn ein neuer Teilnehmer versucht sich mit dem Server zu verbinden. - + Make My Server Public Veröffentliche meinen Server - If the Make My Server Public check box is checked, this server registers itself at the central server so that all - Mit dieser Funktion wird der eigene Server in der Serverliste des Zentralservers registriert so dass alle anderen Applikationsnutzer den + Mit dieser Funktion wird der eigene Server in der Serverliste des Zentralservers registriert so dass alle anderen Applikationsnutzer den - users can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. - Server in der Liste sehen können und sich mit ihm verbinden können. Die Registrierung mit dem Zentralserver wird periodisch erneuert um sicherzugehen, dass alle registrierten Server auch wirklich erreichbar sind. + Server in der Liste sehen können und sich mit ihm verbinden können. Die Registrierung mit dem Zentralserver wird periodisch erneuert um sicherzugehen, dass alle registrierten Server auch wirklich erreichbar sind. - + Register Server Status Registrierungsstatus @@ -2111,7 +2683,7 @@ Die Zentralserveradrees ist die IP-Adresse oder URL des Zentralservers bei dem man sich registrieren möchte. Mit dem Zentralservertyp legt man die Region fest, in der man sich befindet. Außerdem kann eine freie Adresse eingetragen werden. - + If the Make My Server Public check box is checked, this will show whether registration with the central server is successful. If the registration failed, please choose another server list. Wenn der eigene Server veröffentlicht wurde, dann zeigt der Registrierungsstatus and, ob die Registrierung erfolgreich war oder nicht. Wenn die Registrierung fehlgeschlagen ist, dann wähle eine andere Serverliste aus. @@ -2124,112 +2696,324 @@ Voreingestellter Zentralservertyp Auswahlbox - + + If the start minimized on operating system start check box is checked, the server will be started when the operating system starts up and is automatically minimized to a system task bar icon. + Wenn diese Funktion angehakt ist, dann wird der Server automatisch mit dem Betriebssystemstart geladen und erscheint minimiert in der Systemleiste als Icon. + + + + If the Make My Server Public check box is checked, this server registers itself at the central server so that all users of the application can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. + Mit dieser Funktion wird der eigene Server in der Serverliste des Zentralservers registriert so dass alle anderen Applikationsnutzer den Server in der Liste sehen können und sich mit ihm verbinden können. Die Registrierung mit dem Zentralserver wird periodisch erneuert um sicherzugehen, dass alle registrierten Server auch wirklich erreichbar sind. + + + Custom Central Server Address Benutzerdefinierte Zentralserveradresse - + The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. Die benutzerdefinierte Zentralserveradresse ist die IP-Adresse oder URL des Zentralservers, der die Serverliste für den Verbindungsdialog bereitstellt. - + Central server address line edit Zentralserveradresse Eingabefeld - + Server List Selection Serverlistenauswahl - + Selects the server list (i.e. central server address) in which your server will be added. Wählt die Serverliste (d.h. die Zentralserveradresse) aus, in welche dein Server registriert werden soll. - + Server list selection combo box Severlistenauswahl Selektion - + Server Name Servername - 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. - Der Servername identifiziert deinen Server in der Serverliste. Falls kein Name angegeben wurde, dann wird die IP-Adresse stattdessen angezeigt. + Der Servername identifiziert deinen Server in der Serverliste. Falls kein Name angegeben wurde, dann wird die IP-Adresse stattdessen angezeigt. - + + The server name identifies your server in the connect dialog server list at the clients. + Der Servername identifiziert deinen Server in der Serverliste. + + + Server name line edit Servername Eingabefeld - + Location City Standort Stadt - + 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. Hier kann man die Stadt angeben, in der sich der Server befindet. Falls eine Stadt angegeben wurde, dann wird die in der Serverliste angezeigt. - + City where the server is located line edit Stadt in der sich der Server befindet Eingabefeld - + Location country Standort Land - + 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. Hier kann man das Land eingeben, in dem sich der Server befindet. Falls ein Land angegeben wurde, dann wird das in der Serverliste angezeigt. - + Country where the server is located combo box Land in dem sich der Server befindet Auswahlbox + + + Display dialog to select recording directory button + Zeige den Dialog zum das Aufnahmeverzeichnis festzulegen Knopf + + + + + Main Recording Directory + Aufnahmehauptverzeichnis + + + + Click the button to open the dialog that allows the main recording directory to be selected.The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). + Drücke den Knopf um einen Dialog anzuzeigen in dem man das Hauptverzeichnis für die Aufnahmen festlegen kann. Das Verzeichnis muss existieren und schreibbar sein (es muss für das Benutzerkonto, das die Applikation ausführt, möglich sein Unterverzeichnisse anzulegen). + + + + Main recording directory text box (read-only) + Hauptaufnahmeverzeichnis Textbox (schreibgeschützt) + + + + The current value of the main recording directory. The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). Click the button to open the dialog that allows the main recording directory to be selected. + Dies ist das aktuelle Aufnahmeverzeichnis. Das Verzeichnis muss existieren und der Benutzer Jamulus muss Schreibrechte haben und Verzeichnisse erstellen können. Wenn man den Knopf drückt, dann erscheint ein Dialog zum Auswählen des Verzeichnisses. + - + Clear the recording directory button + Lösche das Aufnahmeverzeichnis Knopf + + + + Clear Recording Directory + Zurücksetzen des Aufnahmeverzeichnisses + + + + Click the button to clear the currently selected recording directory. This will prevent recording until a new value is selected. + Drücke den Knopf um das aktuelle Aufnahmeverzeichnis zurückzusetzen. Damit wird eine neue Aufnahme verhindert bis wieder ein neues Verzeichnis gesetzt wurde. + + + + Checkbox to turn on or off server recording + Schalter zum aktivieren oder deaktivieren der Aufnahmefunktion + + + + Enable Recorder + Aktiviere die Aufnahmefunktion + + + + Checked when the recorder is enabled, otherwise unchecked. The recorder will run when a session is in progress, if (set up correctly and) enabled. + Angehakt, wenn die Aufnahmefunktion aktiviert ist. Die Aufnahme wird automatisch gestartet, wenn eine Jam-Session läuft. + + + + Current session directory text box (read-only) + Aktuelle Session Checkbox + + + + Current Session Directory + Verzeichnisname für das Speichern der Aufnahmen + + + + Enabled during recording and holds the current recording session directory. Disabled after recording or when the recorder is not enabled. + Wenn die Aufnahmefunktion aktiviert ist, dann kann das Verzeichnis ausgewählt werden, in dem die Aufnahmen gespeichert werden. + + + + Recorder status label + Recorder Statusanzeige + + + + Recorder Status + Aufnahmestatus + + + + Displays the current status of the recorder. The following values are possible: + Zeigt den aktuellen Status der Aufnahmefunktion an. Die folgenden Zustände sind möglich: + + + + No recording directory has been set or the value is not useable + Es wurde kein Aufnahmeverzeichnis gesetzt oder es wurde ein ungültiges Aufnahmeverzeichnis angegeben + + + + Recording has been switched off + Die Aufnahme wurde beendet + + + + by the UI checkbox + in der Benutzerschnittstelle + + + + , either by the UI checkbox or SIGUSR2 being received + , entweder von der Auswahl in der Benutzeroberfläche oder von einem empfangenen SIGUSR2-Signal + + + + There is no one connected to the server to record + Es ist kein Musiker mit dem Server verbunden + + + + The performers are being recorded to the specified session directory + Die Aufnahme ist aktiv + + + + NOTE + HINWEIS + + + + If the recording directory is not useable, the problem will be displayed in place of the directory. + Wenn das Aufnahmeverzeichnis ungültig ist, dann wird die Problembeschreibung anstelle des Verzeichnisnamens angezeigt. + + + + Server welcome message edit box + Server Willkommenstext Eingabefeld + + + + Server Welcome Message + Server Willkommensnachricht + + + + A server welcome message text is displayed in the chat window if a musician enters the server. If no message is set, the server welcome is disabled. + Eine Server Willkommensnachricht wird im Chat-Fenster angezeigt, wenn sich ein Musiker mit dem Server verbindet. Ist kein Text gesetzt, dann ist die Willkommensnachricht deaktiviert. + + + + Type a message here. If no message is set, the server welcome is disabled. + Willkommensnachricht eingeben. Ist kein Text gesetzt, dann ist die Willkommensnachricht deaktiviert. + + + + software upgrade available + Softwareupdate verfügbar + + + + ERROR + FEHLER + + + Displays the current status of the recorder. + Zeigt den Aufnahmestatus an. + + + + Request new recording button + Anfordern einer neuen Aufnahme + + + + New Recording + Neue Aufnahme + + + + During a recording session, the button can be used to start a new recording. + Mit diesem Knopf kann man die Aufnahme neu starten (d.h. es wird eine neue Aufnahmedatei angelegt). + + + + E&xit &Beenden - + &Hide &Ausblenden vom - - - + + + server Server - + &Open Ö&ffne den - server - Server + Server + + + + Select Main Recording Directory + Wähle das Aufnahmehauptverzeichnis aus - Predefined Address - Vordefinierte Adresse + Vordefinierte Adresse + + + + Recording + Aufnahme + + + + Not recording + Keine Aufnahme + + + + Not initialised + Nicht initialisiert + + + + Not enabled + Nicht aktiviert Manual @@ -2244,52 +3028,62 @@ Standard (Nordamerika) - + Server - + &Window &Fenster - + Unregistered Nicht registriert - + Bad address Ungültige Adresse - + Registration requested Registrierung angefordert - + Registration failed Registrierung fehlgeschlagen - + Check server version Überprüfe Version des Servers - + Registered Registriert - + Central Server full Zentralserver voll - + + Your server version is too old + Deine Serverversion ist zu alt + + + + Requirements not fulfilled + Anfoderungen nicht erfüllt + + + Unknown value Unbekannter Wert @@ -2303,7 +3097,7 @@ - + Name Name @@ -2313,14 +3107,33 @@ Netzwerkpuffergröße - + + Server Setup + Server Konfiguration + + + + Chat Window Welcome (HTML/CSS Supported) + Chat-Fenster Willkommensnachricht (HTML/CSS wird unterstützt) + + + + Options + Optionen + + + Start Minimized on Windows Start Starte minimiert beim Windows-Start - Show Creative Commons BY-NC-SA 4.0 Licence Dialog - Zeige den Creative Commons BY-NC-SA 4.0 Lizenzdialog + Zeige den Creative Commons BY-NC-SA 4.0 Lizenzdialog + + + + Update check + Update check @@ -2328,38 +3141,71 @@ Veröffentliche meinen Server in der Serverliste - + + Genre + Genre + + + + STATUS - + Custom Central Server Address: Benutzerdefinierte Zentralserveradresse: + + + Recording Directory + Aufnahmeverzeichnis + + + + Enable Jam Recorder + Aktiviere die Aufnahmefunktion + + + + New Recording + Neue Aufnahme + + + + Language + Sprache + Central Server Address: Zentralserveradresse - + My Server Info Meine Serverinformationen - + Location: City Standort: Stadt - + Location: Country Standort: Land - - TextLabelNameVersion - + Enable jam recorder + Aktivere die Aufnahme + + + New recording + Neue Aufnahme + + + Recordings folder + Verzeichnis für die Aufnahmen @@ -2401,98 +3247,126 @@ Der Jack-Client kann nicht aktiviert werden. - + The Jack server was shut down. This software requires a Jack server to run. Try to restart the software to solve the issue. Der Jack-Server wurde gestoppt. Diese Software benötigt aber einen aktiven Jack-Server um zu funktionieren. Versuche die Software neu zu starten um das Problem zu lösen. - + CoreAudio input AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. CoreAudio Eingang AudioHardwareGetProperty Aufruf schlug fehl. Es scheint als wäre keine Soundkarte im System vorhanden. - + CoreAudio output AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. CoreAudio Ausgang AudioHardwareGetProperty Aufruf schlug fehl. Es scheint, dass keine Soundkarte ist im System verfügbar. - + The previously selected audio device is no longer available. The system default audio device will be selected instead. + Die vorher ausgewählte Soundkarte ist nicht mehr im System verfügbar. Die System-Standardsoundkarte wird nun stattdessen ausgewählt. + + + Current audio input device is no longer available. + Das aktuelle Audiogerät für den Toneingang ist nicht mehr verfügbar. + + + Current system audio input device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. Die aktuelle Eingangssamplerate von %1 Hz wird nicht unterstützt. Bitte öffne die Audio-MIDI-Setup-Applikation in Applikationen->Werkzeuge und versuche die Samplerate auf %2 Hz einzustellen. - + Current audio output device is no longer available. + Das aktuelle Audiogerät für den Tonausgang ist nicht mehr verfügbar. + + + + + The current selected audio device is no longer present in the system. + Die ausgewählte Soundkarte ist nicht mehr im System verfügbar. + + + + The audio input device is no longer available. + Das aktuelle Audiogerät für den Toneingang ist nicht mehr verfügbar. + + + + The audio output device is no longer available. + Das aktuelle Audiogerät für den Tonausgang ist nicht mehr verfügbar. + + + Current system audio output device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. Die aktuelle Ausgangssamplerate von %1 Hz wird nicht unterstützt. Bitte öffne die Audio-MIDI-Setup-Applikation in Applikationen->Werkzeuge und versuche die Samplerate auf %2 Hz einzustellen. - + The audio input stream format for this audio device is not compatible with this software. Das Audioeingangsstromformat von diesem Audiogerät ist nicht kompatibel mit dieser Software. - + The audio output stream format for this audio device is not compatible with this software. Das Audioausgangsstromformat von diesem Audiogerät ist nicht kompatibel mit dieser Software. - + The buffer sizes of the current input and output audio device cannot be set to a common value. Please choose other input/output audio devices in your system settings. Die Puffergrößen vom aktuellen Eingangs- und Ausgangsaudiogerät kann nicht auf einen gemeinsamen Wert eingestellt werden. Bitte wähle ein anderes Eingangs-/Ausgangsgerät aus der Geräteliste aus. - + The audio driver could not be initialized. Der Audiotreiber konnte nicht initialisiert werden. - + The audio device does not support the required sample rate. The required sample rate is: Das Audiogerät unterstützt nicht die benötigte Samplerate. Die benötigte Samplerate ist: - + The audio device does not support setting the required sampling rate. This error can happen if you have an audio interface like the Roland UA-25EX where you set the sample rate with a hardware switch on the audio device. If this is the case, please change the sample rate to Das Audiogerät unterstützt nicht die benötigte Samplerate. Dieser Fehler kann auftreten, wenn ein Audiogerät wie das Roland UA-25EX verwendet wird, bei dem die Samplerate per Schalter am Gerät verstellt werden muss. Falls das der Fall sein sollte, dann stelle bitte den Schalter auf - + Hz on the device and restart the Hz am Gerät und starte die - + software. Software neu. - + The audio device does not support the required number of channels. The required number of channels for input and output is: Das Audiogerät unterstützt nicht die benötigte Anzahl an Kanälen. Die benötigte Anzahl an Kanälen für den Eingang und den Ausgang ist: - - + + Required audio sample format not available. Das benötigte Audio Sampleformat ist nicht verfügbar. - + No ASIO audio device (driver) found. Kein ASIO-Gerätetreiber wurde gefunden. - + The Die - + software requires the low latency audio interface ASIO to work properly. This is not a 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 the ASIO4All driver. Software benötigt aber ein ASIO Audiointerface um zu funktionieren. Dies ist keine Standard-Windowsschnittstelle und benötigt deshalb einen speziellen Treiber. Entweder die Soundkarte liefert einen nativen ASIO-Treiber mit (was empfohlen wird) oder man versucht es mit dem ASIO4All-Universaltreiber. - + Error closing stream: $s Fehler beim Schließen des Datenstroms: $s @@ -2500,55 +3374,80 @@ CSoundBase - Invalid device selection. - Ungültige Geräteauswahl. + Ungültige Geräteauswahl. - The audio driver properties have changed to a state which is incompatible with this software. The selected audio device could not be used because of the following error: - Die Audiotreibereigenschaften haben sich geändert. Die neuen Einstellungen sind nicht mehr kompatibel zu dieser Software. Das ausgewählte Audiogerät konnte nicht benutzt werden wegen folgendem Fehler: + Die Audiotreibereigenschaften haben sich geändert. Die neuen Einstellungen sind nicht mehr kompatibel zu dieser Software. Das ausgewählte Audiogerät konnte nicht benutzt werden wegen folgendem Fehler: - Please restart the software. - Bitte starte die Software neu. + Bitte starte die Software neu. - - Close - + + The selected audio device could not be used because of the following error: + Das ausgewählte Audiogerät kann aus folgendem Grund nicht verwendet werden: + + + + The previous driver will be selected. + Der vorherige Treiber wird wieder ausgewählt. + + + + The previously selected audio device is no longer available or the audio driver properties have changed to a state which is incompatible with this software. We now try to find a valid audio device. This new audio device might cause audio feedback. So, before connecting to a server, please check the audio device setting. + Das aktuell ausgewählte Audiogerät ist nicht mehr verfügbar oder die Treibereinstellungen sind nicht mehr mit dieser Software kompatibel. Es wird nun versucht, ein funktionierendes Audiogerät zu finden. Dieses neue Audiogerät könnte eine Tonrückkopplung verursachen. Bitte überprüfe deswegen die Audiogeräteeinstellungen vor der nächsten Verbindung mit dem Server. - + No usable Kein benutzbares - + audio device (driver) found. Audiogerät (Treiber) konnte gefunden werden. - + In the following there is a list of all available drivers with the associated error message: Im folgenden wird eine Liste aller gefundenen Audiogeräte mit entsprechender Fehlermeldung angezeigt: - + Do you want to open the ASIO driver setups? Willst du die ASIO-Treibereinstellungen öffnen? - + could not be started because of audio interface issues. konnte nicht gestartet werden wegen Problemen mit dem Audiogerät. + + QCoreApplication + + + , Version + , Version + + + + Internet Jam Session Software + Internet Jam Session Software + + + + Released under the GNU General Public License (GPL) + Unter der GNU General Public License (GPL) + + global - + For more information use the What's This help (help menu, right mouse button or Shift+F1) Für weitere Informationen verwende die Kontexthilfe (Hilfe-Menü, rechte Maustaste oder Tastenkombination Shift+F1) diff --git a/src/res/translation/translation_es_ES.qm b/src/res/translation/translation_es_ES.qm index 72b0ea58ef..30f5382b84 100644 Binary files a/src/res/translation/translation_es_ES.qm and b/src/res/translation/translation_es_ES.qm differ diff --git a/src/res/translation/translation_es_ES.ts b/src/res/translation/translation_es_ES.ts index 23b8a6e863..67c39f74e2 100644 --- a/src/res/translation/translation_es_ES.ts +++ b/src/res/translation/translation_es_ES.ts @@ -4,118 +4,147 @@ CAboutDlg - The - El software + El software software enables musicians to perform real-time jam sessions over the internet. There is a software permite a músicos realizar jam sessions en tiempo real por internet. Hay un - software enables musicians to perform real-time jam sessions over the internet. - permite a músicos realizar jam sessions en tiempo real por internet. + permite a músicos realizar jam sessions en tiempo real por internet. - server which collects the audio data from each - que recoge el audio de cada cliente + que recoge el audio de cada cliente - There is a - Hay un servidor + Hay un servidor - client, mixes the audio data and sends the mix back to each client. - , mezcla el audio y lo envía de vuelta a cada cliente. + , mezcla el audio y lo envía de vuelta a cada cliente. - uses the following libraries, resources or code snippets: - utiliza las siguientes librerías, recursos o fragmentos de código: + utiliza las siguientes librerías, recursos o fragmentos de código: - + Qt cross-platform application framework Qt cross-platform application framework - + Audio reverberation code by Perry R. Cook and Gary P. Scavone Código de reverberación de audio de Perry R. Cook y Gary P. Scavone - + Some pixmaps are from the Algunos pixmaps son del - Country flag icons from Mark James + Iconos de banderas nacionales de Mark James + + + + This app enables musicians to perform real-time jam sessions over the internet. + Esta aplicación permite a músicos realizar jam sessions en tiempo real por internet. + + + + There is a server which collects the audio data from each client, mixes the audio data and sends the mix back to each client. + Hay un servidor que recoge el audio de cada cliente, los combina y envía la mezcla de vuelta a cada cliente. + + + + This app uses the following libraries, resources or code snippets: + Esta aplicación utiliza las siguientes librerías, recursos o fragmentos de código: + + + + Country flag icons by Mark James Iconos de banderas nacionales de Mark James - + For details on the contributions check out the Para más detalles sobre los contribuidores consulta la - + Github Contributors list lista de Contribuidores en Github - + Spanish Español - + French Francés - + Portuguese Portugués - + Dutch Neerlandés - + Italian Italiano - + German Alemán - + + Polish + Polaco + + + + Swedish + Sueco + + + + Slovak + Eslovaco + + + About Acerca de - , Version - , Versión + , Versión - Internet Jam Session Software - Internet Jam Session Software + Internet Jam Session Software + + + Released under the GNU General Public License (GPL) + Publicado bajo la GNU General Public License (GPL) - Under the GNU General Public License (GPL) - Bajo la GNU General Public License (GPL) + Bajo la GNU General Public License (GPL) @@ -164,12 +193,12 @@ CAnalyzerConsole - + Analyzer Console Analyzer Console - + Error Rate of Each Buffer Size Tasa de Error de Cada Tamaño de Buffer @@ -177,207 +206,305 @@ CAudioMixerBoard - + + Personal Mix at the Server + Mezcla personal en el Servidor + + + + When connected to a server, the controls here allow you to set your local mix without affecting what others hear from you. The title shows the server name and, when known, whether it is actively recording. + Estando conectado a un servidor, estos controles te permiten hacer tu mezcla personal sin afectar lo que otros escuchan de tí. El título muestra el nombre del servidor y, cuando se conoce, si está activamente grabando. + + + Server Servidor - + T R Y I N G T O C O N N E C T I N T E N T A N D O C O N E C T A R - - Personal Mix at the Server: + + RECORDING ACTIVE + GRABACIÓN ACTIVA + + + + Personal Mix at: Mezcla Personal en el Servidor: CChannelFader - + Channel Level Nivel Canal - 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. - Muestra el nivel de audio pre-fader de este canal. Todos los clientes conectados al servidor tienen un nivel de audio asignado, el mismo para cada cliente. + Muestra el nivel de audio pre-fader de este canal. Todos los clientes conectados al servidor tienen un nivel de audio asignado, el mismo para cada cliente. - + Input level of the current audio channel at the server Nivel de entrada del canal de audio actual en el servidor - + Mixer Fader Fader Mezclador - 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. - Ajusta el nivel de audio de este canal. Todos los clientes conectados al servidor tienen asignado un fader en el cliente, ajustando la mezcla local. + Ajusta el nivel de audio de este canal. Todos los clientes conectados al servidor tienen asignado un fader en el cliente, ajustando la mezcla local. - + Local mix level setting of the current audio channel at the server Ajuste local de la mezcla del canal de audio actual en el servidor - + Status Indicator Indicador de Estado - + Shows a status indication about the client which is assigned to this channel. Supported indicators are: Muestra una indicación del estado del cliente asignado a este canal. Los indicadores soportados son: - Speaker with cancellation stroke: Indicates that the other client has muted you. - Altavoz tachado: Indica que el otro cliente te ha muteado. + Altavoz tachado: Indica que el otro cliente te ha muteado. - + Status indicator label Etiqueta indicador estado - + Panning Paneo - Sets the panning position from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. - Fija el paneo de Izquierda a Derecha del canal. Solo funciona en estéreo o preferiblemente en modo Entrada mono/Salida estéreo. + Fija el paneo de Izquierda a Derecha del canal. Solo funciona en estéreo o preferiblemente en modo Entrada mono/Salida estéreo. - + Local panning position of the current audio channel at the server Posición local del paneo del canal de audio actual en el servidor - + With the Mute checkbox, the audio channel can be muted. - Activando Mute, se puede mutear el canal de audio. + Activando Mute, se puede silenciar el canal de audio. - + Mute button Botón Mute - 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. - Activando Solo, todos los demás canales de audio excepto este se mutean. Es posible activar esta función para más de un canal. + Activando Solo, todos los demás canales de audio excepto este se mutean. Es posible activar esta función para más de un canal. - + Solo button Botón Solo - + Fader Tag Etiqueta Fader - 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. + La etiqueta del fader identifica al cliente conectado. El nombre de la etiqueta, la imagen de tu instrumento y la bandera de tu país se pueden establecer en la ventana principal. + + + + Grp + Grp + + + + Displays the pre-fader audio level of this channel. All clients connected to the server will be assigned an audio level, the same value for every client. + Muestra el nivel de audio pre-fader de este canal. Todos los clientes conectados al servidor tienen un nivel de audio asignado, el mismo valor para cada cliente. + + + + &No grouping + &Sin grupos + + + + + + + Assign to group + Asignar a grupo + + + + Adjusts the audio level of this channel. All clients connected to the server will be assigned an audio fader, displayed at each client, to adjust the local mix. + Ajusta el nivel de audio de este canal. Todos los clientes conectados al servidor tendrán asignado un fader, mostrado en cada cliente, para ajustar la mezcla local. + + + + Speaker with cancellation stroke: Indicates that another client has muted you. + Altavoz tachado: Indica que otro cliente te ha muteado. + + + + Sets the pan from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. + Fija el paneo de Izquierda a Derecha del canal. Solo funciona en modo estéreo o preferiblemente en modo Entrada mono/Salida estéreo. + + + + With the Solo checkbox, the audio channel can be set to solo which means that all other channels except the soloed channel are muted. It is possible to set more than one channel to solo. + Activando Solo, todos los demás canales de audio excepto este se silencian. Es posible activar esta función para más de un canal. + + + + Group + Grupo + + + + With the Grp checkbox, a group of audio channels can be defined. All channel faders in a group are moved in proportional synchronization if any one of the group faders are moved. + Activando Grp se puede definir un grupo de canales. Todos los faders de un grupo se mueven en sincronización proporcional si se mueve cualquier fader del grupo. + + + + Group button + Botón grupo + + + + The fader tag identifies the connected client. The tag name, a picture of your instrument and the flag of your country can be set in the main window. La etiqueta del fader identifica al cliente conectado. El nombre de la etiqueta, la imagen de tu instrumento y la bandera de tu país se pueden establecer en la ventana principal. - + Mixer channel instrument picture Imagen mezclador canal instrumento - + Mixer channel label (fader tag) Etiqueta mezclador canal (etiqueta fader) - + Mixer channel country flag Bandera país mezclador canal - + PAN PAN - + MUTE MUTE - + SOLO SOLO - + + GRP + GRP + + + + M + M + + + + S + S + + + + G + G + + + Alias/Name Alias/Nombre - + Instrument Instrumento - + Location Ubicación - - - + + + Skill Level Nivel Habilidad - + + Alias + Alias + + + Beginner Principiante - + Intermediate Intermedio - + Expert Experto - + Musician Profile Perfil Músico - - + + Mute Mute - + + Pan Paneo - - + + Solo Solo @@ -414,66 +541,88 @@ New chat text edit box Campo nuevo texto chat + + + Type a message here + Teclea un mensaje aquí + + + + &Edit + &Editar + + + + Cl&ear Chat History + Li&mpiar Historial Chat + + + + Do you want to open the link + ¿Quieres abrir el enlace + + + + in an external browser? + en un navegador externo? + CChatDlgBase - + Chat Chat - + + &Send + &Enviar + + Cl&ear - Va&ciar + Va&ciar - &Close - &Cerrar + &Cerrar CClientDlg - + Input Level Meter Indicador nivel entrada - The input level indicators show the input level of the two stereo channels of the current selected audio input. - Los indicadores de nivel de entrada muestran el nivel de entrada de los dos canales estéreo de la entrada de audio actualmente seleccionada. + Los indicadores de nivel de entrada muestran el nivel de entrada de los dos canales estéreo de la entrada de audio actualmente seleccionada. - + Make sure not to clip the input signal to avoid distortions of the audio signal. Asegúrate de no clipear la señal de entrada para evitar distorsiones de la señal de audio. - If the - Si el software + Si el software - software is connected and you play your instrument/sing in the microphone, the LED level 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. - está conectado y tocas tu instrumento/cantas por el micrófono, el LED del indicador debería parpadear. Si no es así, seguramente has seleccionado el canal de entrada equivocado (por ej. line in en lugar de la entrada del micrófono) o está muy bajo el gain de entrada en el mezclador de audio (Windows). + está conectado y tocas tu instrumento/cantas por el micrófono, el LED del indicador debería parpadear. Si no es así, seguramente has seleccionado el canal de entrada equivocado (por ej. line in en lugar de la entrada del micrófono) o está muy bajo el gain de entrada en el mezclador de audio (Windows). - For a proper usage of the - Para un uso adecuado del software + Para un uso adecuado del software - software, you should not hear your singing/instrument in the loudspeaker or your headphone when the - , no deberías oír tu voz/instrumento por el altavoz o los auriculares cuando el software + , no deberías oír tu voz/instrumento por el altavoz o los auriculares cuando el software - software is not connected. This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). - no está conectado. Esto se puede hacer muteando tu entrada de audio en el mezclador de Reproducción (¡y no en el de Grabación!). + no está conectado. Esto se puede hacer muteando tu entrada de audio en el mezclador de Reproducción (¡y no en el de Grabación!). @@ -491,593 +640,799 @@ Botón Conexión/Desconexión - Push this button to connect to a server. A dialog where you can select a server will open. If you are connected, pressing this button will end the session. - Pulsa este botón para conectar con un servidor. Se abrirá una ventana donde puedes seleccionar un servidor. Si estás conectado, este botón finalizará la sesión. + Pulsa este botón para conectar con un servidor. Se abrirá una ventana donde puedes seleccionar un servidor. Si estás conectado, este botón finalizará la sesión. - + Connect and disconnect toggle button Botón de conexión y desconexión - Clicking on this button changes the caption of the button from Connect to Disconnect, i.e., it implements a toggle functionality for connecting and disconnecting the - Pulsando este botón cambia el texto del mismo de Conectar a Desconectar; esto es, tiene la función de conmutador para conectar y desconectar el software + Pulsando este botón cambia el texto del mismo de Conectar a Desconectar; esto es, tiene la función de conmutador para conectar y desconectar el software - - software. - . + . - + Local Audio Input Fader Fader Entrada Audio Local - + Local audio input fader (left/right) Fader entrada audio local (izq/dcho) - Reverberation effect level setting - Nivel efecto reverberación + Nivel efecto reverberación - Left channel selection for reverberation - Selección canal izq para reverberación + Selección canal izq para reverberación - Right channel selection for reverberation - Selección canal dcho para reverberación + Selección canal dcho para reverberación - If this LED indicator turns red, you will not have much fun using the - Si este indicador LED se vuelve rojo, no te divertirás demasiado utilizando el + Si este indicador LED se vuelve rojo, no te divertirás demasiado utilizando el + + + + This shows the level of the two stereo channels for your audio input. + Esto muestra los niveles de los dos canales estéreo de tu entrada de audio. + + + + If the application is connected to a server and you play your instrument/sing into the microphone, the VU 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. + Si la aplicación está conectada a un servidor y tocas tu instrumento/cantas por el micrófono, el vúmetro debería parpadear. Si no es así, seguramente has seleccionado el canal de entrada equivocado (por ej. line in en lugar de la entrada del micrófono) o está muy bajo el gain de entrada en el mezclador de audio (Windows). + + + + For proper usage of the application, you should not hear your singing/instrument through the loudspeaker or your headphone when the software is not connected.This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). + Para un uso adecuado de la aplicación, no deberías oír tu voz/instrumento por el altavoz o los auriculares cuando el software no está conectado. Esto se puede realizar silenciando tu canal de entrada de audio en el mezclador de Reproducción (¡no el de Grabación!). + + + Clicking on this button changes the caption of the button from Connect to Disconnect, i.e., it implements a toggle functionality for connecting and disconnecting the application. + Pulsando este botón cambia el texto del mismo de Conectar a Desconectar; esto es, tiene la función de conmutador para conectar y desconectar el software. + + + + Controls the relative levels of the left and right local audio channels. For a mono signal it acts as a pan between the two channels.For example, if 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 + Controla los niveles relativos de los canales locales de audio derecho e izquierdo. Para una señal mono actúa como paneo entre los dos canales. Por ej., si se conecta un miocrófono al canal derecho y un instrumento al izquierdo que suena mucho más alto que el micrófono, mueve el fader en una dirección donde la etiqueta sobre el fader muestra + + + + Reverb effect + Efecto reverb + + + + Reverb can be applied to one local mono audio channel or to both channels in stereo mode. The mono channel selection and the reverb level can be modified. For example, if a microphone signal is fed in to the right audio channel of the sound card and a reverb effect needs to be applied, set the channel selector to right and move the fader upwards until the desired reverb level is reached. + Se puede aplicar un efecto de reverb a un canal local de audio mono o a ambos canales en modo estéreo. Se puede modificar la selección de canales en modo mono y el nivel de reverb. Por ej., si la señal del micrófono va por el canal derecho de la tarjeta de sonido y se desea aplicar reverb, cambia el selector de canal a derecho y sube el fader hasta alcanzar el nivel de reverb deseado. + + + Reverb requires significant CPU so it should only be used on fast PCs. If the reverb level fader is set to minimum (the default setting), the effect is switched off and does not cause any additional CPU usage. + El efecto de reverb requiere un esfuerzo importante del procesador, por lo que solo debería usarse en ordenadores potentes. Si se deja el fader de reverb al mínimo (la configuración por defecto), el efecto estará desactivado y no significará ninguna carga adicional para el procesador. + + + + Reverb effect level setting + Nivel efecto reverb + + + + Reverb Channel Selection + Selección Canal Reverb + + + + With these radio buttons the audio input channel on which the reverb effect is applied can be chosen. Either the left or right input channel can be selected. + Con estos botones se puede escoger el canal de entrada de audio al que se aplica el efecto de reverb. Se pueden elegir los canales de entrada izquierdo o derecho. + + + + Left channel selection for reverb + Selección canal izq para reverb + + + + Right channel selection for reverb + Selección canal dcho para reverb + + + The + El software + + + + Green + Verde + + + + The delay is perfect for a jam session. + El retardo es perfecto para una jam session. + + + + Yellow + Amarillo - + + Red + Rojo + + + If this LED indicator turns red, you will not have much fun using the application + Si este indicador LED se vuelve rojo, no te divertirás demasiado utilizando la aplicación + + + Delay status LED indicator Indicador LED estado retardo - + + Opens a dialog where you can select a server to connect to. If you are connected, pressing this button will end the session. + Abre un diálogo donde puedes escoger un servidor al cual conectarte. Si estás conectado, pulsar este botón finalizará la sesión. + + + + Shows the current audio delay status: + Muestra el estado actual del retardo de audio: + + + The delay is perfect for a jam session + El retardo es perfecto para una jam session + + + + A session is still possible but it may be harder to play. + Una sesión aún es posible pero quizá sea más difícil tocar. + + + + The delay is too large for jamming. + El retardo es demasiado grande para tocar. + + + + If this LED indicator turns red, you will not have much fun using the application. + Si este indicador LED se vuelve rojo, no te divertirás demasiado utilizando la aplicación. + + + + The buffers status LED shows the current audio/streaming status. If the light is red, the audio stream is interrupted. This is caused by one of the following problems: + El LED de estado de buffers muestra el estado actual del flujo de audio. Si está rojo, hay interrupciones en el flujo de audio. Esto puede ser causado por alguno de los siguientes problemas: + + + + The sound card's buffer delay (buffer size) is too small (see Settings window). + El retardo de buffer de la tarjeta de audio (tamaño buffer) tiene un valor demasiado bajo (ver ventana de Configuración). + + + + The upload or download stream rate is too high for your internet bandwidth. + La tasa de subida o bajada es demasiado alta para tu ancho de banda de Internet. + + + Buffers status LED indicator Indicador LED estado buffers - - + + C&onnect C&onectar - + + software upgrade available + Actualización de software disponible + + + + &File + &Archivo + + + &View &Ver - + &Connection Setup... &Configuración de Conexión... - + My &Profile... Mi &Perfil... - + C&hat... C&hat... - + &Settings... &Configuración... - + &Analyzer Console... &Analyzer Console... - + + Use &Two Rows Mixer Panel + Usar Dos Filas Para Ven&tana Mezclador + + + + &Clear All Stored Solo and Mute Settings + Eliminar Todas las &Configuraciones de Solo y Mute + + + + Ok + Ok + + + E&xit S&alir - + + &Edit + &Editar + + + + Sort Users by &Group + Ordenar Usuarios de Canal por &Grupo + + None - Ninguno + Ninguno - + Center Centro - + R R - - + + L L - 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 - Con el fader de audio, los niveles relativos de los canales locales de audio derecho e izquierdo pueden cambiarse. Para una señal mono actúa como paneo entre los dos canales. Por ej., si se conecta un miocrófono al canal derecho y un instrumento al izquierdo que suena mucho más alto que el micrófono, mueve el fader en una dirección donde la etiqueta sobre el fader muestra + Con el fader de audio, los niveles relativos de los canales locales de audio derecho e izquierdo pueden cambiarse. Para una señal mono actúa como paneo entre los dos canales. Por ej., si se conecta un miocrófono al canal derecho y un instrumento al izquierdo que suena mucho más alto que el micrófono, mueve el fader en una dirección donde la etiqueta sobre el fader muestra - + , where , donde - + is the current attenuation indicator. es el indicador actual de atenuación. - Reverberation Level - Nivel Reverberación + Nivel Reverberación - 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. - Se puede aplicar un efecto de reverberación a un canal local de audio mono o a ambos canales en modo estéreo. Se puede modificar la selección de canales en modo mono y el nivel de reverberación. Por ej., si la señal del micrófono va por el canal derecho de la tarjeta de sonido y se desea aplicar reverberación, cambia el selector de canal a derecho y sube el fader hasta alcanzar el nivel de reverberación deseado. + Se puede aplicar un efecto de reverberación a un canal local de audio mono o a ambos canales en modo estéreo. Se puede modificar la selección de canales en modo mono y el nivel de reverberación. Por ej., si la señal del micrófono va por el canal derecho de la tarjeta de sonido y se desea aplicar reverberación, cambia el selector de canal a derecho y sube el fader hasta alcanzar el nivel de reverberación deseado. - The reverberation effect requires significant CPU so 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 switched off and does not cause any additional CPU usage. - El efecto de reverberación require un esfuerzo importante del procesador, por lo que solo debería usarse en ordenadores potentes. Si se deja el fader de reverberación al mínimo (la configuración por defecto), el efecto estará desactivado y no significará ninguna carga adicional para el procesador. + El efecto de reverberación require un esfuerzo importante del procesador, por lo que solo debería usarse en ordenadores potentes. Si se deja el fader de reverberación al mínimo (la configuración por defecto), el efecto estará desactivado y no significará ninguna carga adicional para el procesador. - Reverberation Channel Selection - Selección Canal Reverberación + Selección Canal Reverberación - 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. - Con estos botones se puede escoger el canal de entrada de audio al que se aplica el efecto de reverberación. Se pueden elegir los canales de entrada izquierdo o derecho. + Con estos botones se puede escoger el canal de entrada de audio al que se aplica el efecto de reverberación. Se pueden elegir los canales de entrada izquierdo o derecho. - + Delay Status LED LED Estado Retardo - 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 large for jamming. - El indicador LED del estado del retardo muestra el estado actual del retardo del audio. Si está en verde, el retardo es perfecto para una jam session. Si está en amarillo, la sesión aún es posible, pero quizá sea más difícil tocar. Si está en rojo, el retardo es demasiado alto para tocar. + El indicador LED del estado del retardo muestra el estado actual del retardo del audio. Si está en verde, el retardo es perfecto para una jam session. Si está en amarillo, la sesión aún es posible, pero quizá sea más difícil tocar. Si está en rojo, el retardo es demasiado alto para tocar. - + Buffers Status LED LED Estado Buffers - 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: - El indicador LED del estado de buffers muestra el estado actual del flujo de audio. Si está verde, no hay problemas de buffer y no se interrumpe el flujo de audio. Si está rojo, el flujo de audio se interrumpe, a causa de uno de los siguientes problemas: + El indicador LED del estado de buffers muestra el estado actual del flujo de audio. Si está verde, no hay problemas de buffer y no se interrumpe el flujo de audio. Si está rojo, el flujo de audio se interrumpe, a causa de uno de los siguientes problemas: - + The network jitter buffer is not large enough for the current network/audio interface jitter. El jitter buffer de red no es lo suficientemente grande para el jitter actual de la red/interfaz de audio. - The sound card buffer delay (buffer size) is set to too small a value. - El retardo de buffer de la tarjeta de audio (tamaño buffer) tiene un valor demasiado bajo. + El retardo de buffer de la tarjeta de audio (tamaño buffer) tiene un valor demasiado bajo. - The upload or download stream rate is too high for the current available internet bandwidth. - La tasa de subida o bajada is demasiado alta para el ancho de banda disponible de internet. + La tasa de subida o bajada is demasiado alta para el ancho de banda disponible de internet. - + The CPU of the client or server is at 100%. El procesador del cliente o del servidor está al 100%. - + + &Load Mixer Channels Setup... + &Cargar Configuración Canales Mezclador... + + + + &Save Mixer Channels Setup... + &Guardar Configuración Canales Mezclador... + + + + N&o User Sorting + N&o Ordenar Usuarios + + + + Sort Users by &Name + Ordenar Usuarios por &Nombre + + + + Sort Users by &Instrument + Ordenar Usuarios por &Instrumento + + + + Sort Users by &City + Ordenar Usuarios por &Ciudad + + + &Clear All Stored Solo Settings + Eliminar &Configuraciones Guardadas de Solo + + + + Set All Faders to New Client &Level + Todos los Faders al Nivel C&liente Nuevo + + + Central Server Servidor Central - + + + Select Channel Setup File + Seleccionar Archivo Configuración Canales + + + user usuario - + users usuarios - + + Your sound card is not working correctly. Please open the settings dialog and check the device selection and the driver settings. + Tu tarjeta de sonido no está funcionando correctamente. Por favor abre la ventana de configuración y comprueba la selección de dispositivo y la configuración del driver. + + + D&isconnect - D&esconectar + &Desconectar CClientDlgBase - + Delay Retardo - + Buffers Buffers - + Input Entrada - + L L - + R R - - Settings - Configuración + + &Mute Myself + Silenciar&me Yo - - Chat - Chat + + &Settings + Co&nfiguración - - Mute Myself - Mutearme Yo + + &Chat + &Chat - + C&onnect C&onectar - + Pan Paneo - + Center Centro - + Reverb Reverb - + Left Izq - + Right Dcho + + + MUTED (Other people won't hear you) + SILENCIADO (Otras personas no te escucharán) + + + + Update check + Comprobación de actualización + + + MUTED (You are not sending any audio to the server) + MUTEADO (No estás enviando audio al servidor) + CClientSettingsDlg - + Jitter Buffer Size Tamaño Jitter Buffer - 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). - El jitter buffer compensa el jitter de la red y la tarjeta de audio. El tamaño de este buffer tiene por tanto un impacto sobre la calidad del flujo de audio (el número de caídas de la señal) y el retardo total (a mayor buffer, mayor retardo). + El jitter buffer compensa el jitter de la red y la tarjeta de audio. El tamaño de este buffer tiene por tanto un impacto sobre la calidad del flujo de audio (el número de caídas de la señal) y el retardo total (a mayor buffer, mayor retardo). - 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 below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun took place and the audio stream is interrupted. - El tamaño del jitter buffer se puede establecer para el cliente local y para el servidor remoto. Para el jitter buffer local, las caídas del flujo de audio se indican mediante la luz debajo de los faders del jitter buffer. Si la luz se vuelve roja, significa que ha habido una interrupción del flujo de audio. + El tamaño del jitter buffer se puede establecer para el cliente local y para el servidor remoto. Para el jitter buffer local, las caídas del flujo de audio se indican mediante la luz debajo de los faders del jitter buffer. Si la luz se vuelve roja, significa que ha habido una interrupción del flujo de audio. - + The jitter buffer setting is therefore a trade-off between audio quality and overall delay. Por tanto la configuración del jitter buffer es un compromiso entre calidad y retardo total. - 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 Auto check is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). - Hay disponible una configuración automática del jitter buffer. Si se activa Auto, los jitter buffers del cliente local y del servidor remoto se configuran automáticamente basándose en mediciones del jitter de la red y la tarjeta de audio. Si se activa esta opción, los faders quedan deshabilitados (no pueden moverse con el ratón). + Hay disponible una configuración automática del jitter buffer. Si se activa Auto, los jitter buffers del cliente local y del servidor remoto se configuran automáticamente basándose en mediciones del jitter de la red y la tarjeta de audio. Si se activa esta opción, los faders quedan deshabilitados (no pueden moverse con el ratón). - If 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 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 with a red light. - En caso de activar la configuración automática del jitter buffer, los buffers de red del cliente local y del servidor remoto se asignan a un valor conservador para minimizar la probabilidad de fallos de audio. Para ajustar el retardo de audio/latencia se recomienda desactivar la función automática y bajar los valores de jitter buffer manualmente utilizando los controles deslizantes hasta alcanzar un límite aceptable de caídas de audio. El indicador LED ofrece una visualización de las caídas de audio mediante una luz roja. + En caso de activar la configuración automática del jitter buffer, los buffers de red del cliente local y del servidor remoto se asignan a un valor conservador para minimizar la probabilidad de fallos de audio. Para ajustar el retardo de audio/latencia se recomienda desactivar la función automática y bajar los valores de jitter buffer manualmente utilizando los controles deslizantes hasta alcanzar un límite aceptable de caídas de audio. El indicador LED ofrece una visualización de las caídas de audio mediante una luz roja. - + Local jitter buffer slider control Control deslizante jitter buffer local - + Server jitter buffer slider control Control deslizante jitter buffer servidor - + Auto jitter buffer switch Interruptor auto jitter buffer - + Jitter buffer status LED indicator Indicador LED estado jitter buffer - + Sound Card Device Dispositivo de Audio - + The ASIO driver (sound card) can be selected using El driver ASIO (tarjeta de audio) se puede seleccionar utilizando - + 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. en el sistema operativo Windows. En MacOs/Linux no es posible seleccionar la tarjeta de audio. Si el driver ASIO no es válido se muestra un mensaje de error y se selecciona el driver válido anterior. - + If the driver is selected during an active connection, the connection is stopped, the driver is changed and the connection is started again automatically. Si el driver se selecciona durante una conexión activa, la conexión se detiene, se cambia el driver y la conexión se reanuda automáticamente. - + Sound card device selector combo box Selector de dispositivo de audio - + If 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. En caso de utilizar el driver ASIO4ALL, por favor ten en cuenta que este driver normalmente introduce una latencia adicional de 10-30 ms. Por tanto se recomienda utilizar la tarjeta de audio con un driver nativo. - + If you are using the kX ASIO driver, make sure to connect the ASIO inputs in the kX DSP settings panel. Si utilizas el driver kX ASIO, asegúrate de conectar las entradas ASIO en el panel de configuración de kX DSP. - + Sound Card Channel Mapping Mapeo Canales Tarjeta Audio - + If the selected sound card device offers more than one input or output channel, the Input Channel Mapping and Output Channel Mapping settings are visible. Si el dispositivo de audio ofrece más de un canal de entrada o salida, son visibles las configuraciones para el Mapeo de Canales de Entrada y de Salida. - + For each Para cada - + input/output channel (Left and Right channel) a different actual sound card channel can be selected. canal de entrada/salida (canal Izquierdo y Derecho) se puede seleccionar un canal diferente de la tarjeta de audio. - + Left input channel selection combo box Selección canal entrada izquierdo - + Right input channel selection combo box Selección canal entrada derecho - + Left output channel selection combo box Selección canal salida izquierdo - + Right output channel selection combo box Selección canal salida derecho - + Enable Small Network Buffers Activar Buffers Red Pequeños - + 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 Si se activa, se habilita el soporte para paquetes de red de audio muy pequeños. Solo se utilizan estos paquetes pequeños si el retardo de buffer de la tarjeta de audio es menor de - + samples. The smaller the network buffers, the lower the audio latency. But at the same time the network load increases and the probability of audio dropouts also increases. muestras. Cuanto menores los buffers de red, menor la latencia de audio. Pero al mismo tiempo, aumenta la carga de red y la probabilidad de caídas de audio también aumenta. - + Enable small network buffers check box Activar buffers de red pequeños - + Sound Card Buffer Delay Retardo Buffer Tarjeta Audio - + + Central server address combo box + Campo para dirección servidor central + + + + Fancy + Oscuro + + + + Compact + Compacto + + The buffer delay setting is a fundamental setting of the - Este parámetro es una parte fundamental de la configuración del software + Este parámetro es una parte fundamental de la configuración del software - software. This setting has influence on many connection properties. - . Este parámetro tiene un impacto sobre muchas propiedades de la conexión. + . Este parámetro tiene un impacto sobre muchas propiedades de la conexión. - + Three buffer sizes are supported Hay soporte para tres tamaños de buffer - 64 samples: This is the preferred setting since it provides the lowest latency but does not work with all sound cards. - 64 muestras: Es la configuración aconsejada puesto que ofrece la latencia más baja, aunque no funciona con todas las tarjetas de audio. + 64 muestras: Es la configuración aconsejada puesto que ofrece la latencia más baja, aunque no funciona con todas las tarjetas de audio. - 128 samples: This setting should work for most available sound cards. - 128 muestras: Esta configuración debería de funcionar con la mayoría de tarjetas de audio. + 128 muestras: Esta configuración debería de funcionar con la mayoría de tarjetas de audio. - 256 samples: This setting should only be used if only a very slow computer or a slow internet connection is available. - 256 muestras: Esta configuración solo debería usarse con un ordenador muy lento o con una conexión a internet muy lenta. + 256 muestras: Esta configuración solo debería usarse con un ordenador muy lento o con una conexión a internet muy lenta. - Some sound card drivers do not allow the buffer delay to be changed from within the - Algunos drivers de tarjetas de audio no permiten cambiar el retardo de buffer desde el software + Algunos drivers de tarjetas de audio no permiten cambiar el retardo de buffer desde el software - 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. - . En este caso la configuración del retardo de buffer se deshabilita. Para cambiarlo, esta configuración debe realizarse en el driver de la tarjeta de audio. En Windows, pulsa el botón de Configuración ASIO para acceder al panel de configuración. En Linux, utiliza la herramienta de configuración de Jack para cambiar el tamaño del buffer. + . En este caso la configuración del retardo de buffer se deshabilita. Para cambiarlo, esta configuración debe realizarse en el driver de la tarjeta de audio. En Windows, pulsa el botón de Configuración ASIO para acceder al panel de configuración. En Linux, utiliza la herramienta de configuración de Jack para cambiar el tamaño del buffer. - If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The - Si no hay ningún tamaño de buffer seleccionado y todas las configuraciones están deshabilitadas, el driver está utilizando un tamaño de buffer no soportado. El software + Si no hay ningún tamaño de buffer seleccionado y todas las configuraciones están deshabilitadas, el driver está utilizando un tamaño de buffer no soportado. El software - software will still work with this setting but with restricted performance. - seguirá funcionando con esta configuración pero con un rendimiento limitado. + seguirá funcionando con esta configuración pero con un rendimiento limitado. - + 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 a red light in the status indicator (drop outs) and the higher the upload rate and the lower the overall delay. El retardo del buffer tiene un impacto en el estado de la conexión, la tasa de subida y el retardo total. Cuanto menor sea el retardo del buffer, mayor la probabilidad de que el indicador de estado esté en rojo (caídas de audio), mayor la tasa de subida y menor el retardo total. - + The buffer setting is therefore a trade-off between audio quality and overall delay. Por tanto la configuración del buffer es un compromiso entre calidad de audio y retardo total. - If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the - Si la configuración de retardo de buffers se encuentra deshabilitada, es porque el driver de audio prohíbe la modificación de este parámetro desde dentro del software + Si la configuración de retardo de buffers se encuentra deshabilitada, es porque el driver de audio prohíbe la modificación de este parámetro desde dentro del software - software. 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. - . En Windows, pulsa el botón de Configuración ASIO para abrir el panel de configuración del driver. En Linux, utiliza la herramienta de configuración de Jack para cambiar el tamaño del buffer. + . En Windows, pulsa el botón de Configuración ASIO para abrir el panel de configuración del driver. En Linux, utiliza la herramienta de configuración de Jack para cambiar el tamaño del buffer. - + 64 samples setting radio button Configuración 64 muestras - + 128 samples setting radio button Configuración 128 muestras - + 256 samples setting radio button Configuración 256 muestras - + ASIO setup push button Botón configuración ASIO - Fancy Skin - Interfaz Oscura + Interfaz Oscura - If enabled, a fancy skin will be applied to the main window. - Si se activa, se aplicará un aspecto oscuro a la ventana principal. + Si se activa, se aplicará un aspecto oscuro a la ventana principal. - Fancy skin check box - Activar interfaz oscura + Activar interfaz oscura - Display Channel Levels - Mostrar Niveles Canales + Mostrar Niveles Canales - If enabled, each client channel will display a pre-fader level bar. - Si se activa, cada canal de cliente mostrará una barra de nivel pre-fader. + Si se activa, cada canal de cliente mostrará una barra de nivel pre-fader. - Display channel levels check box - Mostrar niveles canales + Mostrar niveles canales - + Audio Channels Canales Audio - 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 mono-in/stereo-out mode the audio signal which is sent to the server is mono but the return signal is stereo. This is useful if the sound card has 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. - Selecciona el número de canales de audio a utilizar. Hay tres modos disponibles. Los modos mono y estéreo utilizan uno y dos canales de audio respectivamente. En modo entrada-mono/salida-estéreo la señal de audio enviada al servidor es mono pero la señal que vuelve es estéreo. Esto es útil si la tarjeta de audio tiene un instrumento en un canal de entrada y un micrófono en el otro. En ese caso las dos señales de entrada pueden combinarse en un canal mono pero la mezcla del servidor se escucha en estéreo. + Selecciona el número de canales de audio a utilizar. Hay tres modos disponibles. Los modos mono y estéreo utilizan uno y dos canales de audio respectivamente. En modo entrada-mono/salida-estéreo la señal de audio enviada al servidor es mono pero la señal que vuelve es estéreo. Esto es útil si la tarjeta de audio tiene un instrumento en un canal de entrada y un micrófono en el otro. En ese caso las dos señales de entrada pueden combinarse en un canal mono pero la mezcla del servidor se escucha en estéreo. - 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. - Activar el modo estéreo aumentará la tasa de envío de datos. Asegúrate de que la tasa de subida no excede el ancho de banda disponible en tu conexión a internet. + Activar el modo estéreo aumentará la tasa de envío de datos. Asegúrate de que la tasa de subida no excede el ancho de banda disponible en tu conexión a internet. - In 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. - En el caso del modo estéreo, no estará disponible la selección de canal para el efecto de reverberación en la ventana principal puesto que en este caso el efecto se aplicará a ambos canales. + En el caso del modo estéreo, no estará disponible la selección de canal para el efecto de reverberación en la ventana principal puesto que en este caso el efecto se aplicará a ambos canales. @@ -1090,39 +1445,36 @@ Calidad Audio - 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 upload rate does not exceed the available bandwidth of your internet connection. - Selecciona la calidad de audio deseada. Se puede seleccionar una calidad baja, normal o alta. Cuanto mayor la calidad del audio, mayor la tasa de transferencia de datos de audio. Asegúrate de que la tasa de subida no excede el ancho de banda disponible en tu conexión a internet. + Selecciona la calidad de audio deseada. Se puede seleccionar una calidad baja, normal o alta. Cuanto mayor la calidad del audio, mayor la tasa de transferencia de datos de audio. Asegúrate de que la tasa de subida no excede el ancho de banda disponible en tu conexión a internet. - + Audio quality combo box Selección calidad audio - + New Client Level Nivel Cliente Nuevo - 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 of that client was already stored. - La configuración del nivel de clientes nuevos define el nivel del fader para una nueva conexión expresado en un porcentaje. Esto es, si un cliente nuevo se conecta al servidor actual, su fader tomará el valor especificado si no se ha guardado ningún valor de una conexión anterior de ese cliente. + La configuración del nivel de clientes nuevos define el nivel del fader para una nueva conexión expresado en un porcentaje. Esto es, si un cliente nuevo se conecta al servidor actual, su fader tomará el valor especificado si no se ha guardado ningún valor de una conexión anterior de ese cliente. - + New client level edit box Campo para nivel nuevo cliente - + Custom Central Server Address Dirección Personalizada Servidor Central - The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. This address is only used if the custom server list is selected in the connection dialog. - La dirección personalizada del servidor central es la dirección IP o URL del servidor central en el cual se gestiona la lista de servidores de la ventana de conexión. Esta dirección solo se utiliza si se selecciona la lista personalizada en la ventana de conexión. + La dirección personalizada del servidor central es la dirección IP o URL del servidor central en el cual se gestiona la lista de servidores de la ventana de conexión. Esta dirección solo se utiliza si se selecciona la lista personalizada en la ventana de conexión. Central Server Address @@ -1137,72 +1489,216 @@ Selección servidor central - Central server address line edit - Dirección servidor central + Dirección servidor central - + Current Connection Status Parameter Parámetro Estado Conexión Actual - The ping time is the time required for the audio stream to travel from the client to the server and back again. 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. - El ping es el tiempo que requiere el flujo de audio para viajar desde el cliente al servidor y volver. Este retardo lo determina la red. Esta cifra debería ser de unos 20-30 ms. Si este retardo es mayor (por ej. 50-60 ms), la distancia al servidor es demasiado grande o tu conexión a internet no es óptima. + El ping es el tiempo que requiere el flujo de audio para viajar desde el cliente al servidor y volver. Este retardo lo determina la red. Esta cifra debería ser de unos 20-30 ms. Si este retardo es mayor (por ej. 50-60 ms), la distancia al servidor es demasiado grande o tu conexión a internet no es óptima. + + + The overall delay is calculated from the current ping time and the delay which is introduced by the current buffer settings. + El retardo total se calcula con el ping y el retardo ocasionado por la configuración de buffers. + + + 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 your internet connection by, e.g., using speedtest.net). + La tasa de subida depende del tamaño actual de paquetes de audio y la configuración de compresión de audio. Asegúrate de que la tasa de subida no es mayor que la tasa disponible (comprueba la tasa de subida de tu conexión a internet, por ej. con speedtest.net). + + + + If this LED indicator turns red, you will not have much fun using the + Si este indicador LED se vuelve rojo, no te divertirás demasiado utilizando el software + + + + software. + . + + + + ASIO Setup + Configuración ASIO + + + + + Mono + Mono + + + + mode will increase your stream's data rate. Make sure your upload rate does not exceed the available upload speed of your internet connection. + aumentará la tasa de datos. Asegúrate de que tu tasa de subida no excede el valor de subida disponible con tu ancho de banda de Internet. + + + + Mono-in/Stereo-out + Entrada mono/Salida estéreo + + + + + + Stereo + Estéreo + + + + The jitter buffer compensates for network and sound card timing jitters. The size of the buffer therefore influences the quality of the audio stream (how many dropouts occur) and the overall delay (the longer the buffer, the higher the delay). + El jitter buffer compensa el jitter de la red y la tarjeta de audio. El tamaño de este buffer tiene por tanto un impacto sobre la calidad del flujo de audio (el número de caídas de la señal) y el retardo total (a mayor buffer, mayor retardo). + + + + You can set the jitter buffer size manually for the local client and the remote server. For the local jitter buffer, dropouts in the audio stream are indicated by the light below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun has taken place and the audio stream is interrupted. + El tamaño del jitter buffer se puede establecer manualmente para el cliente local y para el servidor remoto. Para el jitter buffer local, las caídas del flujo de audio se indican mediante la luz debajo de los faders del jitter buffer. Si la luz se vuelve roja, significa que ha habido una interrupción del flujo de audio. + + + + If the Auto setting 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 Auto is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). + En caso de activar Auto, los jitter buffer del cliente local y del servidor remoto se ajustan automáticamente basándose en mediciones del jitter de la red y de la tarjeta de audio. Si se activa Auto, los faders del jitter buffer se deshabilitan (no pueden moverse con el ratón). + + + + If the Auto setting 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 tweak the audio delay/latency it is recommended to disable the Auto setting and to lower the jitter buffer size manually by using the sliders until your personal acceptable amount of dropouts is reached. The LED indicator will display the audio dropouts of the local jitter buffer with a red light. + En caso de activar Auto, los buffers de red del cliente local y del servidor remoto se asignan a un valor conservador para minimizar la probabilidad de fallos de audio. Para ajustar el retardo de audio/latencia se recomienda desactivar la función Auto y bajar los valores de jitter buffer manualmente utilizando los controles deslizantes hasta alcanzar un límite aceptable de caídas de audio. El indicador LED ofrece una visualización de las caídas de audio mediante una luz roja. + + + + The buffer delay setting is a fundamental setting of this software. This setting has an influence on many connection properties. + El retardo de buffer es un parámetro fundamental de este software. Este parámetro tiene un impacto sobre muchas propiedades de la conexión. + + + + 64 samples: The preferred setting. Provides the lowest latency but does not work with all sound cards. + 64 muestras: La configuración aconsejada. Ofrece la latencia más baja, aunque no funciona con todas las tarjetas de audio. + + + + 128 samples: Should work for most available sound cards. + 128 muestras: Debería de funcionar con la mayoría de tarjetas de audio. + + + + 256 samples: Should only be used on very slow computers or with a slow internet connection. + 256 muestras: Esta configuración solo debería usarse con un ordenador muy lento o con una conexión a internet muy lenta. + + + + Some sound card drivers do not allow the buffer delay to be changed from within the application. In this case the buffer delay setting is disabled and has to be changed using 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. + Algunos drivers de tarjetas de audio no permiten cambiar el retardo de buffer desde dentro de la aplicación. En este caso la configuración del retardo de buffer se deshabilita y debe modificarse utilizando el driver de la tarjeta de audio. En Windows, pulsa el botón de Configuración ASIO para acceder al panel de configuración. En Linux, utiliza la herramienta de configuración de Jack para cambiar el tamaño del buffer. + + + + If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The application will still work with this setting but with restricted performance. + Si no hay ningún tamaño de buffer seleccionado y todas las configuraciones están deshabilitadas, el driver está utilizando un tamaño de buffer no soportado. La aplicación arrancará pero con rendimiento limitado. + + + + If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the software. 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. + Si la configuración de retardo de buffers se encuentra deshabilitada, es porque el driver de audio prohíbe la modificación de este parámetro desde dentro del software. En Windows, pulsa el botón de Configuración ASIO para abrir el panel de configuración del driver. En Linux, utiliza la herramienta de configuración de Jack para cambiar el tamaño del buffer. + + + + Skin + Skin + + + + Select the skin to be used for the main window. + Elige el skin a utilizar para la ventana principal. + + + + Skin combo box + Campo skin + + + + Selects the number of audio channels to be used for communication between client and server. There are three modes available: + Selecciona el número de canales de audio a utilizar para la comunicación entre cliente y servidor. Hay tres modos disponibles: + + + + and + y + + + + These modes use one and two audio channels respectively. + Estos modos utilizan uno y dos canales de audio respectivamente. + + + + Mono in/Stereo-out + Entrada Mono/Salida Estéreo + + + + The audio signal sent to the server is mono but the return signal is stereo. This is useful if the sound card has the instrument on one input channel and the microphone on the other. In that case the two input signals can be mixed to one mono channel but the server mix is heard in stereo. + La señal de audio enviada al servidor es mono pero la señal de vuelta es estéreo. Esto es útil si la tarjeta de audio tiene el instrumento en una entrada y el micrófono en la otra. En este caso se pueden mezclar las dos señales de entrada a un canal mono pero la mezcla del servidor se escucha en estéreo. + + + + Enabling + Habilitar el modo - - The overall delay is calculated from the current ping time and the delay which is introduced by the current buffer settings. - El retardo total se calcula con el ping y el retardo ocasionado por la configuración de buffers. + mode will increase your stream's data rate. Make sure your upload rate does not exceed the available upload speed of your internet connection. + aumentará la tasa de datos. Asegúrate de que tu tasa de subida no excede el valor de subida disponible con tu ancho de banda de Internet. - - 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 your internet connection by, e.g., using speedtest.net). - La tasa de subida depende del tamaño actual de paquetes de audio y la configuración de compresión de audio. Asegúrate de que la tasa de subida no es mayor que la tasa disponible (comprueba la tasa de subida de tu conexión a internet, por ej. con speedtest.net). + + In stereo streaming mode, no audio channel selection for the reverb effect will be available on the main window since the effect is applied to both channels in this case. + En modo estéreo, no habrá ninguna selección de canal para el efecto de reverb en la ventana principal porque el efecto se aplica a ambos canales en este caso. - - If this LED indicator turns red, you will not have much fun using the - Si este indicador LED se vuelve rojo, no te divertirás demasiado utilizando el software + + The higher the audio quality, the higher your audio stream's data rate. Make sure your upload rate does not exceed the available bandwidth of your internet connection. + Cuanto mayor la calidad del audio, mayor la tasa de subida del audio. Asegúrate de que tu tasa de subida no excede el ancho de banda de tu conexión a Internet. - - software. - . + + This setting defines the fader level of a newly connected client in percent. If a new client connects to the current server, they will get the specified initial fader level if no other fader level from a previous connection of that client was already stored. + Este ajuste define el nivel del fader de una nueva conexión de cliente, en porcentaje. Si se conecta un nuevo cliente al servidor actual, el nivel inicial de su fader tomará este valor si no se ha especificado anteriormente un valor para ese cliente de una conexión anterior. - - ASIO Setup - Configuración ASIO + + Leave this blank unless you need to enter the address of a central server other than the default. + Deja esto en blanco a menos que necesites escribir la dirección de un servidor distinto a los que hay por defecto. - - Mono - Mono + + The Ping Time is the time required for the audio stream to travel from the client to the server and back again. This delay is introduced by the network and should be about 20-30 ms. If this delay is higher than about 50 ms, your distance to the server is too large or your internet connection is not sufficient. + El Ping es el tiempo que requiere el flujo de audio para viajar desde el cliente al servidor y volver. Este retardo lo determina la red y debería ser de unos 20-30 ms. Si este retardo es de unos 50 ms, la distancia al servidor es demasiado grande o tu conexión a internet no es óptima. - - Mono-in/Stereo-out - Entrada mono/Salida estéreo + + Overall Delay is calculated from the current Ping Time and the delay introduced by the current buffer settings. + El Retardo Total se calcula con el Ping actual y el retardo ocasionado por la configuración de buffers. - - Stereo - Estéreo + + Audio Upstream Rate depends on the current audio packet size and compression setting. Make sure that the upstream rate is not higher than your available internet upload speed (check this with a service such as speedtest.net). + La Tasa de Subida de Audio depende del tamaño actual de paquetes de audio y la configuración de compresión de audio. Asegúrate de que la tasa de subida no es mayor que la velocidad de subida disponible (comprueba la tasa de subida de tu conexión a internet, por ej. con speedtest.net). - + Low Baja - + + Normal Normal - + High Alta @@ -1211,12 +1707,12 @@ Manual - + Custom Personalizado - + All Genres Todos los Géneros @@ -1225,47 +1721,47 @@ Género Rock/Jazz - - Genre Classical/Folk/Choir + + Genre Classical/Folk/Choral Género Clásica/Folk/Coro - + Genre Rock Género Rock - + Genre Jazz Género Jazz - + Default - Por defecto + Predeterminado Default (North America) Por defecto (Norteamérica) - + preferred aconsejado - - + + Size: Tamaño: - + Buffer Delay Retardo Buffer - + Buffer Delay: Retardo Buffer: @@ -1274,19 +1770,16 @@ Dirección Preestablecida - The selected audio device could not be used because of the following error: - El dispositivo de audio seleccionado no puede utilizarse a causa del siguiente error: + El dispositivo de audio seleccionado no puede utilizarse a causa del siguiente error: - The previous driver will be selected. - Se utilizará el driver anterior. + Se utilizará el driver anterior. - Ok - Ok + Ok @@ -1346,7 +1839,7 @@ (default) - (por defecto) + (predeterminado) @@ -1369,58 +1862,66 @@ Auto - + Local Local - + Server Servidor - - + + Size Valor - + Misc Varios - + Audio Channels Canales Audio - + Audio Quality Calidad Audio - + New Client Level Nivel Cliente Nuevo - + + Skin + Skin + + + + Language + Idioma + + + % % - Fancy Skin - Intfaz Oscura + Intfaz Oscura - Display Channel Levels - Mostrar Nivel Canales + Mostrar Nivel Canales - + Custom Central Server Address: Dirección Personalizada Servidor Central: @@ -1429,24 +1930,24 @@ Dirección Servidor Central: - + Audio Stream Rate Tasa Muestreo Audio - - - + + + val val - + Ping Time Tiempo Ping - + Overall Delay Retardo Total @@ -1454,104 +1955,123 @@ CConnectDlg - + Server List Lista Servidores - 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. - La lista de servidores muestra una lista de servidores disponibles que se encuentran registrados en el servidor central. Escoge un servidor de la lista y pulsa el botón de conectar para conectarte a este servidor. También es posible realizar la conexión haciendo doble clic en un servidor de la lista. Si un servidor está ocupado, se puede desplegar una lista de los músicos conectados al pulsar el icono al lado del nombre. Los servidores permanentes se muestran en negrita. + La lista de servidores muestra una lista de servidores disponibles que se encuentran registrados en el servidor central. Escoge un servidor de la lista y pulsa el botón de conectar para conectarte a este servidor. También es posible realizar la conexión haciendo doble clic en un servidor de la lista. Si un servidor está ocupado, se puede desplegar una lista de los músicos conectados al pulsar el icono al lado del nombre. Los servidores permanentes se muestran en negrita. - 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. - Ten en cuenta que puede llevar un tiempo recuperar la lista de servidores del servidor central. Si no se especifica una dirección válida en la configuración, no habrá ninguna lista de servidores disponible. + Ten en cuenta que puede llevar un tiempo recuperar la lista de servidores del servidor central. Si no se especifica una dirección válida en la configuración, no habrá ninguna lista de servidores disponible. - + Server list view Vista lista de servidores - + Server Address Dirección Servidor - The IP address or URL of the server running the - La dirección IP o URL del servidor ejecutando el software del servidor + La dirección IP o URL del servidor ejecutando el software del servidor 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, example.org: debe introducirse aquí. Se puede añadir un número de puerto opcional detrás de la dirección IP o URL utilizando dos puntos como separador, por ej. ejemplo.org: - 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: - debe introducirse aquí. Se puede añadir un número de puerto opcional detrás de la dirección IP o URL utilizando dos puntos como separador, por ej. ejemplo.org: + debe introducirse aquí. Se puede añadir un número de puerto opcional detrás de la dirección IP o URL utilizando dos puntos como separador, por ej. ejemplo.org: - . A list of the most recent used server IP addresses or URLs is available for selection. - . Hay disponible una lista de las direcciones IP o URLs utilizadas más recientemente para su selección. + . Hay disponible una lista de las direcciones IP o URLs utilizadas más recientemente para su selección. + + + The Connection Setup window shows a list of available servers. Server operators can optionally list their servers by music genre. Use the List dropdown to select a genre, click on the server you want to join and press the Connect button to connect to it. Alternatively, double click on on the server name. Permanent servers (those that have been listed for longer than 24 hours) are shown in bold. + La ventana de Conexión muestra una lista de servidores disponibles. Opcionalmente los administradores de los servidores pueden listar sus servidores por género musical. Utiliza el menú Lista para escoger un género, escoge un servidor de la lista y pulsa el botón de Conectar para conectarte a él. También es posible realizar la conexión haciendo doble clic en un servidor de la lista. Los servidores permanentes (aquellos que llevan más de 24 horas en la lista) se muestran en negrita. + + + + The Connection Setup window shows a list of available servers. Server operators can optionally list their servers by music genre. Use the List dropdown to select a genre, click on the server you want to join and press the Connect button to connect to it. Alternatively, double click on on the server name. Permanent servers (those that have been listed for longer than 48 hours) are shown in bold. + La ventana de Conexión muestra una lista de servidores disponibles. Opcionalmente los administradores de los servidores pueden listar sus servidores por género musical. Utiliza el menú Lista para escoger un género, escoge un servidor de la lista y pulsa el botón de Conectar para conectarte a él. También es posible realizar la conexión haciendo doble clic en un servidor de la lista. Los servidores permanentes (aquellos que llevan más de 48 horas en la lista) se muestran en negrita. + + + + If you know the IP address or URL of a server, you can connect to it using the Server name/Address field. An optional port number can be added after the IP address or URL using a colon as a separator, e.g, example.org: + Si conoces la dirección IP o URL de un servidor, puedes conectarte a él utilizando el nombre/dirección del Servidor. Se puede añadir un número de puerto opcional tras la dirección IP o URL utilizando dos puntos como separador, por ej. 'ejemplo.org': - + + . The field will also show a list of the most recently used server addresses. + . El campo también mostrará una lista de los servidores utilizados recientemente. + + + Server address edit box Selección dirección servidor - + Holds the current server IP address or URL. It also stores old URLs in the combo box list. Contiene la dirección IP o URL actual del servidor. También guarda viejas URL en la lista. - + Server List Selection Selección Lista de Servidores - + Selects the server list to be shown. Selecciona la lista de servidores a mostrar. - + Server list selection combo box Selección lista de servidores - + Filter Filtro - + The server list is filtered by the given text. Note that the filter is case insensitive. La lista de servidores se filtra con el texto introducido. El filtro no es sensible a mayúsculas/minúsculas. - + Filter edit box Campo filtro - + Show All Musicians Mostrar Todos los Músicos - + 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. Si activas esta opción, se mostrarán los músicos de todos los servidores. Si lo desactivas, se colapsan todas las listas. - + Show all musicians check box Selección Mostrar todos los músicos + + + Type # for occupied servers + Escribe # para servidores ocupados + CConnectDlgBase @@ -1597,8 +2117,8 @@ - Server Name/Address - Nombre/Dirección Servidor + Server Address + Dirección Servidor @@ -1614,495 +2134,563 @@ CHelpMenu - + &Help &Ayuda - - + + Getting &Started... Cómo &Empezar... - + Software &Manual... Manual del &Software... - + What's &This Qué es &Esto - + &About... &Acerca de... + + CLanguageComboBox + + + Restart Required + Reinicio Necesario + + + + Please restart the application for the language change to take effect. + Por favor reinicia la aplicación para que el cambio de idioma surta efecto. + + CLicenceDlg - I &agree to the above licence terms - &Acepto los términos de la licencia arriba expuestos + &Acepto los términos de la licencia arriba expuestos - + + This server requires you accept conditions before you can join. Please read these in the chat window. + Este servidor requiere que aceptes ciertas condiciones antes de unirte. Por favor léelas en la ventana del chat. + + + + I have read the conditions and &agree. + He leído las condiciones y las &acepto. + + + Accept Acepto - + Decline No Acepto - By connecting to this server and agreeing to this notice, you agree to the following: - Al conectarte a este servidor y aceptar esta notificación, aceptas lo siguiente: + Al conectarte a este servidor y aceptar esta notificación, aceptas lo siguiente: - 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 - Ud. declara que todos los datos, audios u otras obras transmitidas a este servidor son la propiedad de Ud. y creadas por Ud. o sus licenciatarios, y que pone a disposición de terceras partes estos datos, audios u otras obras mediante la siguiente Licencia Creative Commons (para más información sobre esta licencia, ver + Ud. declara que todos los datos, audios u otras obras transmitidas a este servidor son la propiedad de Ud. y creadas por Ud. o sus licenciatarios, y que pone a disposición de terceras partes estos datos, audios u otras obras mediante la siguiente Licencia Creative Commons (para más información sobre esta licencia, ver - You are free to: - Ud. es libre de: + Ud. es libre de: - Share - Compartir + Compartir - copy and redistribute the material in any medium or format - copiar y redistribuir el material en cualquier medio o formato + copiar y redistribuir el material en cualquier medio o formato - Adapt - Adaptar + Adaptar - remix, transform, and build upon the material - remezclar, transformar y construir a partir del material + remezclar, transformar y construir a partir del material - The licensor cannot revoke these freedoms as long as you follow the license terms. - El licenciante no puede revocar estas libertades en tanto Ud. siga los términos de la licencia. + El licenciante no puede revocar estas libertades en tanto Ud. siga los términos de la licencia. - Under the following terms: - Bajo los siguientes términos: + Bajo los siguientes términos: - Attribution - Atribución + Atribución - You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. - Ud. debe dar crédito de manera adecuada, brindar un enlace a la licencia, e indicar si se han realizado cambios. Puede hacerlo en cualquier forma razonable, pero no de forma tal que sugiera que Ud. o su uso tienen el apoyo de la licenciante. + Ud. debe dar crédito de manera adecuada, brindar un enlace a la licencia, e indicar si se han realizado cambios. Puede hacerlo en cualquier forma razonable, pero no de forma tal que sugiera que Ud. o su uso tienen el apoyo de la licenciante. - NonCommercial - No-Comercial + No-Comercial - You may not use the material for commercial purposes. - No puede utilizar el material con fines comerciales. + No puede utilizar el material con fines comerciales. - ShareAlike - ShareAlike + ShareAlike - If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. - Si remezcla, transforma o construye sobre el material, debe distribuir sus contribuciones bajo la misma licencia que el original. + Si remezcla, transforma o construye sobre el material, debe distribuir sus contribuciones bajo la misma licencia que el original. - No additional restrictions - Sin restricciones adicionales + Sin restricciones adicionales - You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. - No puede aplicar términos legales o medidas tecnológicas que restringan legalmente a otras personas de hacer cualquier cosa permitida por la licencia. + No puede aplicar términos legales o medidas tecnológicas que restringan legalmente a otras personas de hacer cualquier cosa permitida por la licencia. + + + + CMultiColorLED + + + Red + Rojo + + + + Yellow + Amarillo + + + + Green + Verde CMusProfDlg - server. This tag will also show up at each client which is connected to the same server as you. If the name is left empty, the IP address is shown instead. - Esta etiqueta también se mostrará a cada cliente conectado al mismo servidor que tú. Se se deja vacío, se muestra la dirección IP en su lugar. + Esta etiqueta también se mostrará a cada cliente conectado al mismo servidor que tú. Se se deja vacío, se muestra la dirección IP en su lugar. - + Alias or name edit box Campo para alias o nombre - + Instrument picture button Botón imagen instrumento - + Country flag button Botón bandera país - + City edit box Ciudad - + Skill level combo box Nivel de habilidad - - - + + + None Ninguno - - + + Musician Profile Perfil Músico - + Alias/Name Alias/Nombre - + Instrument Instrumento - + Country País - + City Ciudad - + Skill Habilidad - + &Close &Cerrar - + Beginner Principiante - + Intermediate Intermedio - + Expert Experto - 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 in. The city you live in and the skill level playing your instrument may also be added. - Escribe tu nombre o alias aquí para que los demás músicos con quien quieras tocar te reconozcan. Puedes además añadir una imagen del instrumento que tocas y la bandera del país donde vives. La ciudad donde vives y tu nivel de habilidad con el instrumento también pueden añadirse. + Escribe tu nombre o alias aquí para que los demás músicos con quien quieras tocar te reconozcan. Puedes además añadir una imagen del instrumento que tocas y la bandera del país donde vives. La ciudad donde vives y tu nivel de habilidad con el instrumento también pueden añadirse. - What you set here will appear at your fader on the mixer board when you are connected to a - Lo que introduzcas aquí aparecerá en tu fader del mezclador cuando te conectes a un servidor + Lo que introduzcas aquí aparecerá en tu fader del mezclador cuando te conectes a un servidor + + + + Write your name or an alias here so the other musicians you want to play with know who you are. You may also add a picture of the instrument you play and a flag of the country you are located in. Your city and skill level playing your instrument may also be added. + Escribe tu nombre o alias aquí para que otros músicos con quien quieras tocar te reconozcan. Puedes además añadir una imagen del instrumento que tocas y la bandera del país donde vives. La ciudad donde vives y tu nivel de habilidad con el instrumento también pueden añadirse. + + + What you set here will appear at your fader on the mixer board when you are connected to a Jamulus server. This tag will also be shown at each client which is connected to the same server as you. If the name is left empty, the IP address is shown instead. + Lo que introduzcas aquí aparecerá en tu fader del mezclador cuando te conectes a un servidor. Esta etiqueta también se mostrará en cada cliente conectado al mismo servidor que tú. Si se deja el nombre vacío, se muestra la dirección IP en su lugar. + + + + What you set here will appear at your fader on the mixer board when you are connected to a Jamulus server. This tag will also be shown at each client which is connected to the same server as you. + Lo que introduzcas aquí aparecerá en tu fader del mezclador cuando te conectes a un servidor Jamulus. Esta etiqueta también se mostrará en cada cliente conectado al mismo servidor que tú. - + Drum Set Batería - + Djembe Djembé - + Electric Guitar Guitarra Eléctrica - + Acoustic Guitar Guitarra Acústica - + Bass Guitar Bajo Eléctrico - + Keyboard Teclado - + Synthesizer Sintetizador - + Grand Piano Piano de Cola - + Accordion Acordeón - + Vocal Voz - + Microphone Micrófono - + Harmonica Armónica - + Trumpet Trompeta - + Trombone Trombón - + French Horn Trompa - + Tuba Tuba - + Saxophone Saxofón - + Clarinet Clarinete - + Flute Flauta - + Violin Violín - + Cello Violonchelo - + Double Bass Contrabajo - + Recorder Grabadora - + Streamer Streamer - + Listener Oyente - + Guitar+Vocal Guitarra+Voz - + Keyboard+Vocal Teclado+Voz - + Bodhran Bodhran - + Bassoon Fagot - + Oboe Oboe - + Harp Arpa - + Viola Viola - + Congas Congas - + Bongo Bongo - + Vocal Bass - Voz bajo + Voz Bajo - + Vocal Tenor Voz Tenor - + Vocal Alto Voz Alto - + Vocal Soprano Voz Soprano - + Banjo Banjo - + Mandolin Mandolina + + + Ukulele + Ukulele + + + + Bass Ukulele + Ukulele Barítono + + + + Vocal Baritone + Voz Barítono + + + + Vocal Lead + Voz Principal + + + + Mountain Dulcimer + Dulcémele de Montaña + + + + Scratching + Scratching + + + + Rapping + Rapeo + + + + No Name + Sin Nombre + CServerDlg - + Client List Lista Clientes - + 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. La lista de clientes muestra todos los clientes actualmente conectados a este servidor. Alguna información sobre los clientes como la dirección IP y el nombre aparecen para cada cliente conectado. - + Connected clients list view Vista lista clientes conectados - + Start Minimized on Operating System Start Arranca Minimizado al Arrancar Sistema Operativo - If the start minimized on operating system start check box is checked, the - Si se activa el arranque al arrancar el sistema operativo, el + Si se activa el arranque al arrancar el sistema operativo, el - server will be started when the operating system starts up and is automatically minimized to a system task bar icon. - servidor arrancará cuando arranque el sistema operativo y se minimizará automáticamente a un icono en la barra de tareas. + servidor arrancará cuando arranque el sistema operativo y se minimizará automáticamente a un icono en la barra de tareas. - Show Creative Commons Licence Dialog - Mostrar Diálogo de Licencia Creative Commons + Mostrar Diálogo de Licencia Creative Commons - If enabled, a Creative Commons BY-NC-SA 4.0 Licence dialog is shown each time a new user connects the server. - Si se activa, se mostrará un diálogo con la Licencia Creative Commons BY-NC-SA 4.0 cada vez que un cliente nuevo se conecte al servidor. + Si se activa, se mostrará un diálogo con la Licencia Creative Commons BY-NC-SA 4.0 cada vez que un cliente nuevo se conecte al servidor. - + Make My Server Public Mi Servidor es Público - If the Make My Server Public check box is checked, this server registers itself at the central server so that all - Si se activa Mi Servidor es Público, este servidor se registra en el servidor central para que todos los usuarios de + Si se activa Mi Servidor es Público, este servidor se registra en el servidor central para que todos los usuarios de - users can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. - puedan ver el servidor en la lista de servidores de la ventana de conexión y puedan conectarse a él. El registro del servidor se renueva periódicamente para asegurarse de que todos los servidores en la lista se encuentren realmente disponibles. + puedan ver el servidor en la lista de servidores de la ventana de conexión y puedan conectarse a él. El registro del servidor se renueva periódicamente para asegurarse de que todos los servidores en la lista se encuentren realmente disponibles. - + Register Server Status Estado Registro Servidor @@ -2119,7 +2707,7 @@ La dirección del Servidor Central es la dirección IP o URL del servidor central en el que se ha registrado este servidor. Aquí se puede escoger la región local de entre los servidores centrales por defecto o se puede especificar una dirección manualmente. - + If the Make My Server Public check box is checked, this will show whether registration with the central server is successful. If the registration failed, please choose another server list. Si se activa Mi Servidor es Público, se mostrará si el registro con el servidor central ha tenido éxito. Si el registro falla, por favor escoge otra lista de servidores. @@ -2128,112 +2716,324 @@ Selección servidor central - + + If the start minimized on operating system start check box is checked, the server will be started when the operating system starts up and is automatically minimized to a system task bar icon. + Si se activa el arranque al arrancar el sistema operativo, el servidor arrancará cuando arranque el sistema operativo y se minimizará automáticamente a un icono en la barra de tareas. + + + + If the Make My Server Public check box is checked, this server registers itself at the central server so that all users of the application can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. + Si se activa Mi Servidor es Público, este servidor se registra en el servidor central para que todos los usuarios de la aplicación puedan ver el servidor en la lista de servidores de la ventana de conexión y conectarse a él. El registro del servidor se renueva periódicamente para asegurarse de que todos los servidores en la lista se encuentren realmente disponibles. + + + Custom Central Server Address Dirección Personalizada Servidor Central - + The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. La dirección personalizada del servidor central es la dirección IP o URL del servidor central en el cual se gestiona la lista de servidores de la ventana de conexión. - + Central server address line edit Dirección servidor central - + Server List Selection Selección Lista Servidores - + Selects the server list (i.e. central server address) in which your server will be added. Selecciona la lista de servidores (por ej. dirección servidor central) al que se añadirá tu servidor. - + Server list selection combo box Selección lista de servidores - + Server Name Nombre Servidor - 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. - El nombre del servidor identifica a tu servidor en la lista de conexión de servidores de los clientes. Si no se especifica un nombre, se muestra la dirección IP en su lugar. + El nombre del servidor identifica a tu servidor en la lista de conexión de servidores de los clientes. Si no se especifica un nombre, se muestra la dirección IP en su lugar. - + + The server name identifies your server in the connect dialog server list at the clients. + El nombre del servidor identifica a tu servidor en la lista de conexión de servidores de los clientes. + + + Server name line edit Nombre del servidor - + Location City Ubicación Ciudad - + 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. Aquí se puede especificar la ciudad en donde se ubica el servidor. Si se introduce una ciudad, se mostrará en la lista de conexión de servidores de los clientes. - + City where the server is located line edit Ciudad en donde se encuentra en servidor - + Location country Ubicación país - + 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. Aquí se puede especificar el país en donde se ubica el servidor. Si se introduce un país, se mostrará en la lista de conexión de servidores de los clientes. - + Country where the server is located combo box País en donde se encuentra el servidor + + + Display dialog to select recording directory button + Mostrar diálogo para seleccionar botón de directorio de grabación + + + + + Main Recording Directory + Directorio Principal de Grabación + + + + Click the button to open the dialog that allows the main recording directory to be selected.The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). + Pulsa el botón para abrir el diálogo que permite seleccionar el directorio principal de grabación. El valor escogido debe existir y tener permisos de escritura (permitir la creación de sub-directorios por parte del usuario ejecutando Jamulus). + + + + Main recording directory text box (read-only) + Caja de texto del directorio principal de grabación (solo lectura) + + + + The current value of the main recording directory. The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). Click the button to open the dialog that allows the main recording directory to be selected. + El valor actual del directorio principal de grabación. El valor escogido debe existir y tener permisos de escritura (permitir la creación de sub-directorios por parte del usuario ejecutando Jamulus). Pulsa el botón para abrir el diálogo que permite seleccionar el directorio principal de grabación. + - + Clear the recording directory button + Vaciar botón de directorio de grabación + + + + Clear Recording Directory + Vaciar Directorio de Grabación + + + + Click the button to clear the currently selected recording directory. This will prevent recording until a new value is selected. + Pulsa el botón para vaciar el directorio de grabación actualmente seleccionado. Esto impedirá la grabación hasta que se seleccione un nuevo valor. + + + + Checkbox to turn on or off server recording + Campo para activar/desactivar la grabación desde el servidor + + + + Enable Recorder + Habilitar Grabación + + + + Checked when the recorder is enabled, otherwise unchecked. The recorder will run when a session is in progress, if (set up correctly and) enabled. + Activado cuando la grabación está habilitada. La grabación se ejecutará cuando una sesión esté en marcha, si está habilitada (y correctamente configurada). + + + + Current session directory text box (read-only) + Campo para directorio sesión actual (solo lectura) + + + + Current Session Directory + Directorio Sesión Actual + + + + Enabled during recording and holds the current recording session directory. Disabled after recording or when the recorder is not enabled. + Habilitado durante la grabación y guarda el directorio actual de grabación, Deshabilitado tras la grabación o cuando la grabación no está habilitada. + + + + Recorder status label + Etiqueta estado grabación + + + + Recorder Status + Estado Grabación + + + + Displays the current status of the recorder. The following values are possible: + Muestra el estado actual de la grabación. Son posibles los siguientes valores: + + + + No recording directory has been set or the value is not useable + No se ha establecido ningún directorio de grabación o el valor no es utilizable + + + + Recording has been switched off + La grabación se ha apagado + + + + by the UI checkbox + mediante la casilla del interfaz + + + + , either by the UI checkbox or SIGUSR2 being received + , bien mediante la casilla del interfaz o la recepción de SIGUSR2 + + + + There is no one connected to the server to record + No hay nadie conectado al servidor para grabar + + + + The performers are being recorded to the specified session directory + Los intérpretes están siendo grabados al directorio de sesión especificado + + + + NOTE + NOTA + + + + If the recording directory is not useable, the problem will be displayed in place of the directory. + Si el directorio de grabación no es utilizable, se mostrará el problema en lugar del directorio. + + + + Server welcome message edit box + Campo edición mensaje bienvenida servidor + + + + Server Welcome Message + Mensaje Bienvenida Servidor + + + + A server welcome message text is displayed in the chat window if a musician enters the server. If no message is set, the server welcome is disabled. + Se muestra un mensaje de bienvenida al servidor cuando un músico entra al mismo. Si no se escribe ningún mensaje, la bienvenida se deshabilita. + + + + Type a message here. If no message is set, the server welcome is disabled. + Escribe un mensaje aquí. Si se deja vacío, el mensaje de bienvenida del servidor se deshabilita. + + + + software upgrade available + Actualización de software disponible + + + + ERROR + ERROR + + + Displays the current status of the recorder. + Muestra el estado actual de la grabación. + + + + Request new recording button + Botón Solicitar nueva grabación + + + + New Recording + Nueva Grabación + + + + During a recording session, the button can be used to start a new recording. + Durante una sesión de grabación, el botón puede utilizarse para comenzar una nueva grabación. + + + + E&xit S&alir - + &Hide &Ocultar servidor - - - + + + server - + &Open &Abrir servidor - server - + + + + + Select Main Recording Directory + Seleccionar Directorio Principal de Grabación - Predefined Address - Dirección Preestablecida + Dirección Preestablecida + + + + Recording + Grabando + + + + Not recording + No grabando + + + + Not initialised + No inicializado + + + + Not enabled + No habilitado Manual @@ -2248,52 +3048,62 @@ Por defecto (Norteamérica) - + Server : Servidor - + &Window &Ventana - + Unregistered Sin registrar - + Bad address Dirección no válida - + Registration requested Registro solicitado - + Registration failed Error de registro - + Check server version Comprueba la versión del servidor - + Registered Registrado - + Central Server full Servidor Central lleno - + + Your server version is too old + La versión de tu servidor es demasiado antigua + + + + Requirements not fulfilled + No se cumplen los requisitos + + + Unknown value Valor desconocido @@ -2307,7 +3117,7 @@ - + Name Nombre @@ -2317,14 +3127,33 @@ Tamaño Jitter Buffer - + + Server Setup + Conf. Servidor + + + + Chat Window Welcome (HTML/CSS Supported) + Ventana Mensaje Bienvenida (HTML/CSS Soportado) + + + + Options + Opciones + + + Start Minimized on Windows Start Arranca Minimizado al Arrancar Windows - Show Creative Commons BY-NC-SA 4.0 Licence Dialog - Mostrar Diálogo de Licencia Creative Commons BY-NC-SA 4.0 + Mostrar Diálogo de Licencia Creative Commons BY-NC-SA 4.0 + + + + Update check + Comprobación de actualización @@ -2332,38 +3161,75 @@ Mi Servidor es Público (Registra Mi Servidor en la Lista de Servidores) - + + Genre + Género + + + + STATUS ESTADO - + Custom Central Server Address: Dirección Personalizada Servidor Central: + + + Recording Directory + Directorio de Grabación + + + + Enable Jam Recorder + Activar Grabación de Jams + + + + New Recording + Nueva Grabación + + + + Language + Idioma + Central Server Address: Dirección Servidor Central: - + My Server Info Info Mi Servidor - + Location: City Ubicación: Ciudad - + Location: Country Ubicación: País - + Enable jam recorder + Habilitar grabación Jam + + + New recording + Nueva grabación + + + Recordings folder + Carpeta grabaciones + + TextLabelNameVersion - TextLabelNameVersion + TextLabelNameVersion @@ -2405,98 +3271,114 @@ No se puede activar el cliente Jack. - + The Jack server was shut down. This software requires a Jack server to run. Try to restart the software to solve the issue. El servidor Jack se ha cerrado. Este software necesita el servidor Jack para funcionar. Intenta reiniciar el software para solucionar este problema. - + CoreAudio input AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. CoreAudio input AudioHardwareGetProperty call failed. Parece ser que el sistema no tiene una tarjeta de sonido disponible. - + CoreAudio output AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. CoreAudio output AudioHardwareGetProperty call failed. Parece ser que el sistema no tiene una tarjeta de sonido disponible. - + Current system audio input device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. La tasa de muestreo actual del dispositivo de audio de entrada de %1 Hz no está soportada. Por favor, abre Configuración-Audio-MIDI en Aplicaciones->Utilidades e intenta configurar una tasa de muestreo de %2 Hz. - + + + The current selected audio device is no longer present in the system. + El dispositivo de audio seleccionado actualmente ya no está presente en el sistema. + + + + The audio input device is no longer available. + El dispositivo de entrada de audio ya no está disponible. + + + + The audio output device is no longer available. + El dispositivo de salida de audio ya no está disponible. + + + Current system audio output device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. La tasa de muestreo actual del dispositivo de audio de salida de %1 Hz no está soportada. Por favor, abre Configuración-Audio-MIDI en Aplicaciones->Utilidades e intenta configurar una tasa de muestreo de %2 Hz. - + The audio input stream format for this audio device is not compatible with this software. El formato de transmisión de audio de entrada para este dispositivo de audio no es compatible con este software. - + The audio output stream format for this audio device is not compatible with this software. El formato de transmisión de audio de salida para este dispositivo de audio no es compatible con este software. - + The buffer sizes of the current input and output audio device cannot be set to a common value. Please choose other input/output audio devices in your system settings. Los tamaños de buffer del dispositivo actual de entrada/salida de audio no pueden establecerse en un valor común. Por favor, selecciona otros dispositivos de entrada/salida de audio en la configuración del sistema. - + The audio driver could not be initialized. No se pudo iniciar el driver de audio. - + The audio device does not support the required sample rate. The required sample rate is: El dispositivo de audio no soporta la tasa de muestreo requerida. La tasa de muestreo requerida es de: - + The audio device does not support setting the required sampling rate. This error can happen if you have an audio interface like the Roland UA-25EX where you set the sample rate with a hardware switch on the audio device. If this is the case, please change the sample rate to El dispositivo de audio no permite establecer la tasa de muestreo requerida. Este error puede suceder si tienes un dispositivo de audio como el Roland UA-25EX en el que se configura mediante un interruptor físico en el dispositivo. Si es este el caso, por favor cambia la tasa de muestreo a - + Hz on the device and restart the Hz en el dispositivo y reinicie el software - + software. . - + The audio device does not support the required number of channels. The required number of channels for input and output is: El dispositivo de audio no soporta el número de canales requerido. El número de canales requerido es de: - - + + Required audio sample format not available. Formato de muestras de audio requerido no disponible. - + No ASIO audio device (driver) found. No se ha encontrado un dispositivo ASIO (driver). - + The El software - + software requires the low latency audio interface ASIO to work properly. This is not a 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 the ASIO4All driver. requiere la interfaz de audio de baja latencia ASIO para funcionar correctamente. No es una interfaz estándar de Windows y por tanto se requiere un driver de audio especial. Tu tarjeta de audio podría tener un driver ASIO nativo (lo recomendado) o quizá quieras probar un driver alternativo como ASIO4All. - + Error closing stream: $s Error cerrando transmisión: $s @@ -2504,55 +3386,84 @@ CSoundBase - Invalid device selection. - Selección de dispositivo no válida. + Selección de dispositivo no válida. - The audio driver properties have changed to a state which is incompatible with this software. The selected audio device could not be used because of the following error: - Las propiedades del driver de audio han cambiado a un estado que es incompatible con este software. El dispositivo de audio seleccionado no se pudo utilizar a causa del siguiente error: + Las propiedades del driver de audio han cambiado a un estado que es incompatible con este software. El dispositivo de audio seleccionado no se pudo utilizar a causa del siguiente error: - Please restart the software. - Por favor reinicie el software. + Por favor reinicie el software. - Close - Cerrar + Cerrar + + + + The selected audio device could not be used because of the following error: + El dispositivo de audio seleccionado no puede utilizarse a causa del siguiente error: + + + + The previous driver will be selected. + Se seleccionará el driver anterior. + + + + The previously selected audio device is no longer available or the audio driver properties have changed to a state which is incompatible with this software. We now try to find a valid audio device. This new audio device might cause audio feedback. So, before connecting to a server, please check the audio device setting. + El dispositivo de audio seleccionado anteriormente ya no está disponible o las propiedades del driver han cambiado a un estado que es incompatible con este software. Intentaremos ahora encontrar un dispositivo de audio válido. Este nuevo dispositivo puede ocasionar retroalimentación de audio. Por tanto, antes de conectarte a un servidor, comprueba la configuración del dispositivo de audio. - + No usable Ningún driver - + audio device (driver) found. de audio utilizable encontrado. - + In the following there is a list of all available drivers with the associated error message: A continuación hay una lista de todos los drivers disponibles con el error asociado: - + Do you want to open the ASIO driver setups? ¿Quieres abrir la configuración del driver ASIO? - + could not be started because of audio interface issues. no pudo arrancar debido a problemas con el dispositivo de audio. + + QCoreApplication + + + , Version + , Versión + + + + Internet Jam Session Software + Internet Jam Session Software + + + + Released under the GNU General Public License (GPL) + Publicado bajo la GNU General Public License (GPL) + + global - + For more information use the What's This help (help menu, right mouse button or Shift+F1) Para más información utiliza ¿Qué es Esto? (menú de ayuda, botón derecho del ratón o Shift+F1) diff --git a/src/res/translation/translation_fr_FR.qm b/src/res/translation/translation_fr_FR.qm index 6a756f74a5..88d4d5b105 100644 Binary files a/src/res/translation/translation_fr_FR.qm and b/src/res/translation/translation_fr_FR.qm differ diff --git a/src/res/translation/translation_fr_FR.ts b/src/res/translation/translation_fr_FR.ts index 3d5405911f..2957a4f380 100644 --- a/src/res/translation/translation_fr_FR.ts +++ b/src/res/translation/translation_fr_FR.ts @@ -4,118 +4,99 @@ CAboutDlg - - The - Le logiciel - - - software enables musicians to perform real-time jam sessions over the internet. There is a - permet aux musiciens de faire des bœufs en temps réel sur internet. Il existe un - - - - software enables musicians to perform real-time jam sessions over the internet. - permet aux musiciens de faire des bœufs en temps réel sur internet. - - - - server which collects the audio data from each - qui collecte les données audio de chaque client - - - - There is a - Il existe un serveur - - - - client, mixes the audio data and sends the mix back to each client. - , mixe les données audio et renvoie le mixage à chaque client. - - - - uses the following libraries, resources or code snippets: - utilise les bibliothèques, ressources ou extraits de code suivants : - - - + Qt cross-platform application framework Cadriciel d'application multiplateforme Qt - + Audio reverberation code by Perry R. Cook and Gary P. Scavone Code de réverbération audio par Perry R. Cook et Gary P. Scavone - + Some pixmaps are from the Certaines images sont issues de - - Country flag icons from Mark James + + This app enables musicians to perform real-time jam sessions over the internet. + Cette app permet aux musiciens de faire des bœufs en temps réel sur internet. + + + + There is a server which collects the audio data from each client, mixes the audio data and sends the mix back to each client. + Il y a un serveur qui collecte les données audio de chaque client, les mixe, et renvoie le mixage à chaque client. + + + + This app uses the following libraries, resources or code snippets: + Cette app utilise les bibliothèques, ressources ou extraits de code suivants : + + + + Country flag icons by Mark James Icônes de drapeaux de pays par Mark James - + For details on the contributions check out the Pour plus de détails sur les contributions, consultez la - + Github Contributors list liste de contributeurs sur github - + Spanish Espagnol - + French Français - + Portuguese Portugais - + Dutch Néerlandais - + Italian Italien - + German Allemand - - About - À propos + + Polish + Polonais - - , Version - , version + + Swedish + Suédois - - Internet Jam Session Software - Logiciels de bœuf sur Internet + + Slovak + Slovaque - - Under the GNU General Public License (GPL) - Sous la licence public général GNU (GPL) + + About + À propos @@ -155,14 +136,6 @@ &Translation &Traduction - - Author: Volker Fischer - Auteur : Volker Fisher - - - Copyright (C) 2005-2020 - Copyright (C) 2005-2020 - &OK @@ -172,12 +145,12 @@ CAnalyzerConsole - + Analyzer Console Console d'analyse - + Error Rate of Each Buffer Size Taux d'erreur de chaque taille de tampon @@ -185,207 +158,281 @@ CAudioMixerBoard - + + Personal Mix at the Server + Mixage personnel au serveur + + + + When connected to a server, the controls here allow you to set your local mix without affecting what others hear from you. The title shows the server name and, when known, whether it is actively recording. + Lorsque vous êtes connecté à un serveur, les contrôles vous permettent de régler votre mixage local sans affecter ce que les autres entendent de vous. Le titre indique le nom du serveur et, lorsqu'il est connu, s'il est en train d'enregistrer. + + + Server Serveur - + T R Y I N G T O C O N N E C T T E N T A T I V E D E C O N N E X I O N - - Personal Mix at the Server: - Mixage personnel du serveur : + + RECORDING ACTIVE + ENREGISTREMENT ACTIF + + + + Personal Mix at: + Mixage personnel à : CChannelFader - + Channel Level Niveau de canal - - 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. - Affiche le niveau audio pré-fader de ce canal. Tous les clients connectés au serveur se verront attribuer un niveau audio, la même valeur pour chaque client. - - - + Input level of the current audio channel at the server Niveau d'entrée du canal audio actuel sur le serveur - + Mixer Fader - Charriot du mixeur - - - - 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. - Règle le niveau audio de ce canal. Tous les clients connectés au serveur se verront attribuer un chariot audio à chaque client, ce qui permettra d'ajuster le mixage local. + Chariot du mixeur - + Local mix level setting of the current audio channel at the server Réglage du niveau de mixage local du canal audio actuel sur le serveur - + Status Indicator Indicateur d'état - + Shows a status indication about the client which is assigned to this channel. Supported indicators are: Affiche une indication sur l'état du client qui est affecté à ce canal. Les indicateurs pris en charge sont : - - Speaker with cancellation stroke: Indicates that the other client has muted you. - Haut-parleur avec barre d'annulation : indique que l'autre client vous a mis en sourdine. - - - + Status indicator label Étiquette d'indicateur d'état - + Panning Panoramique - - Sets the panning position from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. - Règle la position panoramique du canal de gauche à droite. Fonctionne uniquement en mode stéréo ou de préférence en mode entrée mono/sortie stéréo. - - - + Local panning position of the current audio channel at the server Position panoramique locale du canal audio actuel sur le serveur - + With the Mute checkbox, the audio channel can be muted. En cochant la case Muet, le canal audio peut être mis en sourdine. - + Mute button Bouton de sourdine - - 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. - En cochant la case Solo, le canal audio peut être réglé sur solo, ce qui signifie que tous les autres canaux, à l'exception du canal actuel, sont mis en sourdine. Il est possible de mettre plus d'un canal en solo. - - - + Solo button Bouton de solo - + Fader Tag Étiquette de chariot + + + Grp + Grp + + + + Displays the pre-fader audio level of this channel. All clients connected to the server will be assigned an audio level, the same value for every client. + Affiche le niveau audio pré-fader de ce canal. Tous les clients connectés au serveur se verront attribuer un niveau audio, la même valeur pour chaque client. + + + + &No grouping + &Pas de regroupement + + + + + + + Assign to group + Affecter au groupe + + + + Adjusts the audio level of this channel. All clients connected to the server will be assigned an audio fader, displayed at each client, to adjust the local mix. + Ajuste le niveau audio de ce canal. Tous les clients connectés au serveur se verront attribuer un chariot audio, affiché sur chaque client, pour ajuster le mixage local. + + + + Speaker with cancellation stroke: Indicates that another client has muted you. + Haut-parleur avec barré d'annulation : indique qu'un autre client vous a mis en sourdine. + + + + Sets the pan from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. + Règle le panoramique de gauche à droite du canal. Fonctionne uniquement en mode stéréo ou de préférence en mode entrée mono/sortie stéréo. + - 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. - L'étiquette de chariot identifie le client connecté. Le nom du tag, la photo de votre instrument et un drapeau de votre pays peuvent être définis dans la fenêtre principale. + With the Solo checkbox, the audio channel can be set to solo which means that all other channels except the soloed channel are muted. It is possible to set more than one channel to solo. + En cochant la case Solo, le canal audio peut être réglé sur solo, ce qui signifie que tous les autres canaux, sauf le canal en solo, sont coupés. Il est possible de mettre plus d'un canal en solo. + + + + Group + Groupe + + + + With the Grp checkbox, a group of audio channels can be defined. All channel faders in a group are moved in proportional synchronization if any one of the group faders are moved. + Avec la case à cocher Grp, un groupe de canaux audio peut être défini. Tous les chariots de canaux d'un groupe sont déplacés en synchronisation proportionnelle si l'un des chariots du groupe est déplacé. + + + + Group button + Bouton de groupe + + + + The fader tag identifies the connected client. The tag name, a picture of your instrument and the flag of your country can be set in the main window. + L'étiquette de chariot identifie le client connecté. Le nom de l'étiquette, une photo de votre instrument et le drapeau de votre pays peuvent être définis dans la fenêtre principale. - + Mixer channel instrument picture Image d'instrument de canal de mixeur - + Mixer channel label (fader tag) Label de canal de mixeur (étiquette de chariot) - + Mixer channel country flag Drapeau de pays de canal de mixeur - + PAN PAN - + MUTE MUET - + SOLO SOLO - + + GRP + GRP + + + + M + M + + + + S + S + + + + G + G + + + Alias/Name Pseudo/nom - + Instrument Instrument - + Location Localisation - - - + + + Skill Level Niveau de compétence - + + Alias + Alias + + + Beginner Débutant - + Intermediate Intermédiaire - + Expert Expert - + Musician Profile Profil de musicien - - + + Mute Muet - + + Pan Pan - - + + Solo Solo @@ -422,67 +469,57 @@ New chat text edit box Dialogue d'édition de nouveau texte de tchate + + + Type a message here + Écrivez un message ici + + + + &Edit + Édit&er + + + + Cl&ear Chat History + Vid&er l'historique du tchate + + + + Do you want to open the link + Souhaitez-vous ouvrir le lien + + + + in an external browser? + dans un navigateur externe ? + CChatDlgBase - + Chat Tchate - - Cl&ear - N&ettoyer - - - - &Close - &Fermer + + &Send + En&voyer CClientDlg - + Input Level Meter Indicateur de niveau d'entrée - - The input level indicators show the input level of the two stereo channels of the current selected audio input. - Les indicateurs de niveau d'entrée indiquent le niveau d'entrée des deux canaux stéréo de l'entrée audio actuellement sélectionnée. - - - + Make sure not to clip the input signal to avoid distortions of the audio signal. Veillez à ne pas clipper le signal d'entrée afin d'éviter les distorsions du signal audio. - - - If the - Si le logiciel - - - - software, you should not hear your singing/instrument in the loudspeaker or your headphone when the - , vous ne cevriez pas entendre votre chant/instrument dans le haut-parleur ou votre casque lorsque le logiciel - - - - software is connected and you play your instrument/sing in the microphone, the LED level 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. - est connecté et que vous jouez de votre instrument ou chantez dans le microphone, le voyant de niveau devrait clignoter. Si ce n'est pas le cas, vous avez probablement sélectionné le mauvais canal d'entrée (par exemple, entrée ligne au lieu de l'entrée microphone) ou réglé le gain d'entrée trop bas dans le mixeur audio (Windows). - - - - For a proper usage of the - Pour un bon usage du logiciel - - - - software is not connected. This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). - n'est pas connecté. Vous pouvez y parvenir en mettant en sourdine votre canal audio d'entrée dans le mixeur de lecture (et âs dans le mixeur d'enregistrement !). - Input level meter @@ -499,225 +536,334 @@ Bouton connecter/déconnecter - - Push this button to connect to a server. A dialog where you can select a server will open. If you are connected, pressing this button will end the session. - Appuyez sur ce bouton pour vous connecter à un serveur. Une boîte de dialogue vous permettant de sélectionner un serveur s'ouvrira. Si vous êtes connecté, l'appui sur ce bouton mettra fin à la session. - - - + Connect and disconnect toggle button Bouton-bascule de connection/déconnexion - Clicking on this button changes the caption of the button from Connect to Disconnect, i.e., it implements a toggle functionality for connecting and disconnecting the - En cliquant sur ce bouton, la légende du bouton passe de Connecter à Déconnecter, c'est-à-dire qu'il met en œuvre une fonctionnalité de basculement pour connecter et déconnecter le logiciel - - - - - software. - . - - - Local Audio Input Fader Chariot d'entrée audio locale - + Local audio input fader (left/right) Chariot d'entrée audio locale (gauche/droite) - - Reverberation effect level setting - Paramètre de niveau d'effet de réverbération + + This shows the level of the two stereo channels for your audio input. + Ceci indique le niveau des deux canaux stéréo pour votre entrée audio. - - Left channel selection for reverberation - Sélection de canal gauche pour la réverbération + + If the application is connected to a server and you play your instrument/sing into the microphone, the VU 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. + Si l'application est connectée à un serveur et que vous jouez votre instrument/chantez dans le microphone, le VU-mètre devrait clignoter. Si ce n'est pas le cas, vous avez probablement sélectionné le mauvais canal d'entrée (par exemple 'entrée ligne' au lieu de l'entrée microphone) ou réglé le gain d'entrée trop bas dans le mélangeur audio (Windows). - - Right channel selection for reverberation - Sélection de canal droit pour la réverbération + + For proper usage of the application, you should not hear your singing/instrument through the loudspeaker or your headphone when the software is not connected.This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). + Pour une bonne utilisation de l'application, vous ne devez pas entendre votre chant/instrument par le haut-parleur ou votre casque lorsque le logiciel n'est pas connecté. Ceci peut être réalisé en coupant votre canal audio d'entrée dans le mixeur de lecture (pas dans le mixeur d'enregistrement !). - - If this LED indicator turns red, you will not have much fun using the - Si ce voyant devient rouge, vous n'aurez pas beaucoup de plaisir à utiliser le logiciel + + Controls the relative levels of the left and right local audio channels. For a mono signal it acts as a pan between the two channels.For example, if 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 + Contrôle les niveaux relatifs des canaux audio locaux gauche et droit. Pour un signal mono, il agit comme un pan entre les deux canaux. Par exemple, si un microphone est connecté au canal d'entrée droit et qu'un instrument est connecté au canal d'entrée gauche qui est beaucoup plus fort que le microphone, déplacez le curseur audio dans une direction où l'étiquette au-dessus du curseur indique + + + + Reverb effect + Effet Réverb + + + + Reverb can be applied to one local mono audio channel or to both channels in stereo mode. The mono channel selection and the reverb level can be modified. For example, if a microphone signal is fed in to the right audio channel of the sound card and a reverb effect needs to be applied, set the channel selector to right and move the fader upwards until the desired reverb level is reached. + La réverbération peut être appliquée à un canal audio mono local ou aux deux canaux en mode stéréo. La sélection du canal mono et le niveau de réverbération peuvent être modifiés. Par exemple, si un signal de microphone est envoyé sur le canal audio droit de la carte son et qu'un effet de réverbération doit être appliqué, réglez le sélecteur de canal à droite et déplacez le chariot vers le haut jusqu'à ce que le niveau de réverbération souhaité soit atteint. + + + + Reverb effect level setting + Réglage du niveau de l'effet de réverbération + + + + Reverb Channel Selection + Sélection du canal de réverbération + + + + With these radio buttons the audio input channel on which the reverb effect is applied can be chosen. Either the left or right input channel can be selected. + Ces boutons radio permettent de choisir le canal d'entrée audio sur lequel l'effet de réverbération est appliqué. Il est possible de sélectionner le canal d'entrée gauche ou droit. + + + + Left channel selection for reverb + Sélection du canal gauche pour la réverbération + + + + Right channel selection for reverb + Sélection du canal droit pour la réverbération + + + The + Le logiciel + + + + Green + Vert + + + + The delay is perfect for a jam session. + Le délai est parfait pour une séance de bœufs. + + + + Yellow + Jaune - + + Red + Rouge + + + Delay status LED indicator Indicateur LED d'état de délai - + + Opens a dialog where you can select a server to connect to. If you are connected, pressing this button will end the session. + Ouvre un dialogue dans lequel vous pouvez sélectionner un serveur auquel vous connecter. Si vous êtes connecté, le fait d'appuyer sur ce bouton mettra fin à la session. + + + + Shows the current audio delay status: + Indique l'état actuel du retard audio : + + + + A session is still possible but it may be harder to play. + Une session est toujours possible mais il sera probablement plus difficile de jouer. + + + + The delay is too large for jamming. + Le délai est trop important pour bœuffer. + + + + If this LED indicator turns red, you will not have much fun using the application. + Si ce voyant LED devient rouge, vous n'aurez pas beaucoup de plaisir à utiliser l'application. + + + + The buffers status LED shows the current audio/streaming status. If the light is red, the audio stream is interrupted. This is caused by one of the following problems: + La LED d'état des tampons indique l'état actuel de l'audio/streaming. Si le voyant est rouge, le flux audio est interrompu. Cela est dû à l'un des problèmes suivants : + + + + The sound card's buffer delay (buffer size) is too small (see Settings window). + Le délai du tampon de la carte son (taille du tampon) est trop petit (voir la fenêtre des paramètres). + + + + The upload or download stream rate is too high for your internet bandwidth. + Le taux de flux montant ou descendant est trop élevé pour votre bande passante Internet. + + + Buffers status LED indicator Indicateur LED d'état de tampon - - + + C&onnect Se c&onnecter - + + software upgrade available + mise à jour du logiciel disponible + + + + &File + &Fichier + + + &View &Vue - + &Connection Setup... Paramètres de &connexion... - + My &Profile... - Mon &profil + Mon &profil... - + C&hat... Tc&hate... - + &Settings... Paramètre&s... - + &Analyzer Console... Console d'&analyse - + + Use &Two Rows Mixer Panel + U&tiliser un panneau de mixage à deux rangées + + + + &Clear All Stored Solo and Mute Settings + Effa&cer tous les paramètres Solo et Muet enregistrés + + + + Ok + Ok + + + E&xit &Quitter - - None - Aucun + + &Edit + Édit&er - + Center Centre - + R D - - + + L G - 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 - Avec le chariot audio, les niveaux relatifs des canaux audio locaux gauche et droit peuvent être modifiés. Pour un signal mono, il agit comme un panoramique entre les deux canaux. Si, par exemple, un microphone est connecté au canal d'entrée droit et qu'un instrument est connecté au canal d'entrée gauche qui est beaucoup plus fort que le microphone, déplacez le fader audio dans une direction où l'étiquette au-dessus du chariot indique - - - , where , où - + is the current attenuation indicator. est l'indicateur d'atténuation actuel. - - Reverberation Level - Niveau de réverbération - - - - 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. - Un effet de réverbération peut être appliqué à un canal audio mono local ou aux deux canaux en mode stéréo. La sélection du canal mono et le niveau de réverbération peuvent être modifiés. Si, par exemple, le signal du microphone est envoyé dans le canal audio droit de la carte son et qu'un effet de réverbération doit être appliqué, réglez le sélecteur de canal à droite et déplacez le curseur vers le haut jusqu'à ce que le niveau de réverbération souhaité soit atteint. + + Delay Status LED + Voyant d'état de délai - - The reverberation effect requires significant CPU so 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 switched off and does not cause any additional CPU usage. - L'effet de réverbération nécessite un processeur important, de sorte qu'il ne doit être utilisé que sur des PC rapides. Si le chariot de niveau de réverbération est réglé au minimum (qui est le réglage par défaut), l'effet de réverbération est désactivé et n'entraîne aucune utilisation supplémentaire du processeur. + + Buffers Status LED + Voyant d'état de tampon - - Reverberation Channel Selection - Sélection de canal de réverbération + + The network jitter buffer is not large enough for the current network/audio interface jitter. + Le tampon de jitter réseau n'est pas assez grand pour le jitter actuel de l'interface réseau/audio. - - 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. - Ces boutons radio permettent de choisir le canal d'entrée audio sur lequel l'effet de réverbération est appliqué. Il est possible de sélectionner le canal d'entrée gauche ou droit. + + The CPU of the client or server is at 100%. + Le processeur du client ou du serveur est à 100%. - - Delay Status LED - Voyant d'état de délai + + &Load Mixer Channels Setup... + &Charger la configuration des canaux du mixeur... - - 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 large for jamming. - Le voyant d'état de délai indique l'état actuel du délai audio. Si le voyant est vert, le délai est parfait pour une session de bœuf. Si le voyant est jaune, une session est toujours possible mais elle peut être plus difficile à jouer. Si le voyant est rouge, le délai est trop important pour un bœuf. + + &Save Mixer Channels Setup... + &Sauvegarder la configuration des canaux du mixeur... - - Buffers Status LED - Voyant d'état de tampon + + N&o User Sorting + Pas de tri des canaux (&o) - - 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: - Le voyant d'état des tampons indique l'état actuel de l'audio/du streaming. Si le voyant est vert, il n'y a pas de dépassement de mémoire tampon ni de sous-dépassement et le flux audio n'est pas interrompu. Si le voyant est rouge, le flux audio est interrompu en raison de l'un des problèmes suivants : + + Sort Users by &Name + Trier les utilisateurs par &nom - - The network jitter buffer is not large enough for the current network/audio interface jitter. - Le tampon de jitter réseau n'est pas assez grand pour le jitter actuel de l'interface réseau/audio. + + Sort Users by &Instrument + Trier les utilisateurs par &instrument - - The sound card buffer delay (buffer size) is set to too small a value. - Le délai du tampon de la carte son (taille du tampon) est réglé sur une valeur trop faible. + + Sort Users by &Group + Trier les utilisateurs par &groupe - - The upload or download stream rate is too high for the current available internet bandwidth. - Le taux de flux montant ou descendant est trop élevé pour la bande passante Internet actuellement disponible. + + Sort Users by &City + Trier les utilisateurs par ville (&c) - - The CPU of the client or server is at 100%. - Le processeur du client ou du serveur est à 100%. + + Set All Faders to New Client &Level + Régler tous &les chariots sur le niveau d'un nouveau client - + Central Server Serveur central - + + + Select Channel Setup File + Sélectionnez le fichier de configuration des canaux + + + user utilisateur - + users utilisateurs - + + Your sound card is not working correctly. Please open the settings dialog and check the device selection and the driver settings. + Votre carte son ne fonctionne pas correctement. Veuillez ouvrir le dialogue des paramètres et vérifier la sélection du périphérique et les paramètres du pilote. + + + D&isconnect Dé&connecter @@ -725,585 +871,548 @@ CClientDlgBase - + Delay Délai - + Buffers Tampons - + Input Entrée - + L G - + R D - - Settings - Paramètres + + &Mute Myself + &Me silencer - - Chat - Tchate + + &Settings + Paramètre&s - - Mute Myself - Me silencer + + &Chat + T&chate - + C&onnect Se c&onnecter - + Pan Pan - + Center Centre - + Reverb Réverb - + Left Gauche - + Right Droite - - - CClientSettingsDlg - - Jitter Buffer Size - Taille du tampon de gigue + + MUTED (Other people won't hear you) + SILENCÉ (les autres personnes ne vous entendent pas) - - 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). - Le tampon de gigue compense les gigues de synchronisation du réseau et de l'interface audio. La taille de ce tampon de gigue a donc une influence sur la qualité du flux audio (combien de décrochages se produisent) et le délai global (plus le tampon est long, plus le délai est important). + + Update check + Vérification de mise à jour + + + CClientSettingsDlg - 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 below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun took place and the audio stream is interrupted. - La taille du tampon de gigue peut être choisie manuellement pour le client local et le serveur distant. Pour le tampon de gigue local, les désynchronisations dans le flux audio sont indiquées par le voyant situé en bas des chariots de taille du tampon de gigue. Si le voyant devient rouge, un dépassement de la taille de la mémoire tampon a eu lieu et le flux audio est interrompu. + Jitter Buffer Size + Taille du tampon de gigue - + The jitter buffer setting is therefore a trade-off between audio quality and overall delay. Le réglage du tampon de gigue est donc un compromis entre la qualité audio et le délai global. - - 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 Auto check is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). - Un réglage automatique de la taille du tampon de gigue est disponible. Si la case Auto est activée, les tampons de gigue du client local et du serveur distant sont réglés automatiquement en fonction des mesures de la gigue de synchronisation du réseau et de la carte son. Si la case Auto est activée, les chariots de la taille du tampon de gigue sont désactivés (ils ne peuvent pas être déplacés avec la souris). - - - - If 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 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 with a red light. - Si le réglage automatique du tampon de gigue est activé, les tampons réseau du client local et du serveur distant sont réglés à une valeur prudente pour minimiser la probabilité de décrochage audio. Pour ajuster le délai/latence audio, il est recommandé de désactiver la fonction de réglage automatique et de réduire manuellement la taille du tampon de gigue en utilisant les curseurs jusqu'à ce que votre limite personnelle acceptable du nombre d'interruptions soit atteinte. Le voyant visualisera les décrochages audio du tampon de gigue local par une lumière rouge. - - - + Local jitter buffer slider control Chariot de contrôle de la mémoire tampon de la gigue locale - + Server jitter buffer slider control Chariot de contrôle de la mémoire tampon de la gigue du serveur - + Auto jitter buffer switch Commutateur de tampon de gigue automatique - + Jitter buffer status LED indicator Indicateur LED de l'état du tampon de gigue - + Sound Card Device Périphérique d'interface audio - + The ASIO driver (sound card) can be selected using Le pilote ASIO (interface audio) peut être sélectionné en utilisant - + 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. sous le système d'exploitation Windows. Sous MacOS/Linux, aucune sélection de carte son n'est possible. Si le pilote ASIO sélectionné n'est pas valide, un message d'erreur s'affiche et le pilote valide précédent est sélectionné. - + If the driver is selected during an active connection, the connection is stopped, the driver is changed and the connection is started again automatically. Si le pilote est sélectionné pendant une connexion active, la connexion est interrompue, le pilote est modifié et la connexion est automatiquement relancée. - + Sound card device selector combo box Choix déroulant de sélecteur de périphérique d'interface audio - + If 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. Si le pilote ASIO4ALL est utilisé, veuillez noter que ce pilote introduit généralement environ 10 à 30 ms de latence audio supplémentaire. Il est donc recommandé d'utiliser une carte son avec un pilote ASIO natif. - + If you are using the kX ASIO driver, make sure to connect the ASIO inputs in the kX DSP settings panel. Si vous utilisez le pilote ASIO kX, assurez-vous de connecter les entrées ASIO dans le panneau de configuration DSP kX. - + Sound Card Channel Mapping Cartographie des canaux de la carte son - + If the selected sound card device offers more than one input or output channel, the Input Channel Mapping and Output Channel Mapping settings are visible. Si la carte son sélectionnée offre plus d'un canal d'entrée ou de sortie, les paramètres de mappage des canaux d'entrée et de sortie sont visibles. - + For each Pour chaque canal d'entrée/sortie (canal gauche et droite) de - , a different actual sound card channel can be selected. - , un canal différent de la carte son réelle peut être sélectionné. - - - + Left input channel selection combo box Choix déroulant de sélection de canal d'entrée gauche - + Right input channel selection combo box Choix déroulant de sélection de canal d'entrée droite - + Left output channel selection combo box Choix déroulant de sélection de canal de sortie gauche - + Right output channel selection combo box Choix déroulant de sélection de canal de sortie droite - + Enable Small Network Buffers Activer les petits tampons de réseau - + 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 Si activée, la prise en charge des très petits paquets audio de réseau est activée. Les très petits paquets réseau ne sont réellement utilisés que si le délai de la mémoire tampon de la carte son est inférieur à - + samples. The smaller the network buffers, the lower the audio latency. But at the same time the network load increases and the probability of audio dropouts also increases. échantillons. Plus la mémoire tampon du réseau est petite, plus la latence audio est faible. Mais en même temps, la charge du réseau augmente et la probabilité de décrochage audio augmente également. - + Enable small network buffers check box Case-à-cocher pour activer les petits tampons de réseau - + Sound Card Buffer Delay Délai de temporisation de l'interface audio - - The buffer delay setting is a fundamental setting of the - Le paramètre de délai de temporisation est un paramètre fondamental du logiciel - - - - software. This setting has influence on many connection properties. - . Ce paramètre influence de nombreuses propriétés de connexion. - - - + Three buffer sizes are supported Trois tailles de tampon sont prises en charge - - 64 samples: This is the preferred setting since it provides the lowest latency but does not work with all sound cards. - 64 échantillons : c'est le réglage préféré car il donne la latence la plus faible mais ne fonctionne pas avec toutes les interfaces audio. + + 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 a red light in the status indicator (drop outs) and the higher the upload rate and the lower the overall delay. + Le délai actuel de la mémoire tampon a une influence sur l'état de la connexion, le taux de téléchargement actuel et le délai global. Plus la taille de la mémoire tampon est faible, plus la probabilité d'un voyant rouge dans l'indicateur d'état (désynchronisations) est élevée, plus le taux de téléchargement est élevé et plus le délai global est faible. - - 128 samples: This setting should work for most available sound cards. - 128 échantillons : ce réglage devrait fonctionner sur la plupart des interfaces audio disponibles. + + The buffer setting is therefore a trade-off between audio quality and overall delay. + Le réglage de la mémoire tampon est donc un compromis entre la qualité audio et le délai global. - - 256 samples: This setting should only be used if only a very slow computer or a slow internet connection is available. - 256 échantillons : ce paramètre ne doit être utilisé que si seul un ordinateur très lent ou une connexion internet lente est disponible. + + input/output channel (Left and Right channel) a different actual sound card channel can be selected. + un canal différent de la carte son réelle peut être sélectionné. - - Some sound card drivers do not allow the buffer delay to be changed from within the - Certains pilotes d'interface audio ne permettent pas de modifier le délai de la mémoire tampon à partir du logiciel + + 64 samples setting radio button + Bouton radio de paramétrage à 64 échantillons - - 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. - . Dans ce cas, le réglage du délai de mise en mémoire tampon est désactivé. Pour modifier le délai actuel de la mémoire tampon, ce paramètre doit être modifié dans le pilote de l'interface audio. Sous Windows, appuyez sur le bouton ASIO Setup pour ouvrir le panneau des paramètres du pilote. Sous Linux, utilisez l'outil de configuration Jack pour modifier la taille de la mémoire tampon. + + 128 samples setting radio button + Bouton radio de paramétrage à 128 échantillons - - If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The - Si aucune taille de tampon n'est sélectionnée et que tous les paramètres sont désactivés, une taille de tampon non prise en charge est utilisée par le pilote. Le logiciel + + 256 samples setting radio button + Bouton radio de paramétrage à 256 échantillons - - software will still work with this setting but with restricted performance. - continuera toujours de fonctionner avec ce réglage, mais avec des performances limitées. + + ASIO setup push button + Bouton-poussoir de paramétrage ASIO - - 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 a red light in the status indicator (drop outs) and the higher the upload rate and the lower the overall delay. - Le délai actuel de la mémoire tampon a une influence sur l'état de la connexion, le taux de téléchargement actuel et le délai global. Plus la taille de la mémoire tampon est faible, plus la probabilité d'un voyant rouge dans l'indicateur d'état (désynchronisations) est élevée, plus le taux de téléchargement est élevé et plus le délai global est faible. + + Audio Channels + Canaux audio - - The buffer setting is therefore a trade-off between audio quality and overall delay. - Le réglage de la mémoire tampon est donc un compromis entre la qualité audio et le délai global. + + Audio channels combo box + Choix déroulant de canaux audio - - If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the - Si les paramètres de délai de la mémoire tampon sont désactivés, il est interdit par le pilote audio de modifier ce paramètre à partir du logiciel + + Audio Quality + Qualité audio - . 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. - . Sous Windows, appuyez sur le bouton ASIO Setup pour ouvrir le panneau des paramètres du pilote. Sous Linux, utilisez l'outil de configuration Jack pour modifier la taille de la mémoire tampon. + + Audio quality combo box + Choix déroulant de qualité audio - - input/output channel (Left and Right channel) a different actual sound card channel can be selected. - un canal différent de la carte son réelle peut être sélectionné. + + New Client Level + Niveau de nouveau client - - software. 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. - . 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. + + New client level edit box + Dialogue d'édition de niveau de nouveau client - - 64 samples setting radio button - Bouton radio de paramétrage à 64 échantillons + + Custom Central Server Address + Adresse personnalisée du serveur central - - 128 samples setting radio button - Bouton radio de paramétrage à 128 échantillons + + Current Connection Status Parameter + Paramètre de l'état de la connexion actuelle - - 256 samples setting radio button - Bouton radio de paramétrage à 256 échantillons + + If this LED indicator turns red, you will not have much fun using the + Si ce voyant devient rouge, vous n'aurez pas beaucoup de plaisir à utiliser le logiciel - - ASIO setup push button - Bouton-poussoir de paramétrage ASIO + + software. + . - - Fancy Skin - Habillage fantaisie + + ASIO Setup + Paramètres ASIO - - If enabled, a fancy skin will be applied to the main window. - Si activée, un habillage fantaisie sera appliqué à la fenêtre principale. + + + Mono + Mono - - Fancy skin check box - Case-à-cocher pour l'habillage fantaisie + + mode will increase your stream's data rate. Make sure your upload rate does not exceed the available upload speed of your internet connection. + mode augmentera le débit de données de votre flux. Assurez-vous que votre débit montant ne dépasse pas la vitesse de téléchargement disponible de votre connexion internet. - - Display Channel Levels - Afficher les niveaux des canaux + + Mono-in/Stereo-out + Mono-entrée/stéréo-sortie - - If enabled, each client channel will display a pre-fader level bar. - Si activée, chaque canal de client affichera une barre de niveau pré-fader. + + + + Stereo + Stéréo - - Display channel levels check box - Case-à-cocher pour l'affichage des niveaux de canaux + + The jitter buffer compensates for network and sound card timing jitters. The size of the buffer therefore influences the quality of the audio stream (how many dropouts occur) and the overall delay (the longer the buffer, the higher the delay). + Le tampon de gigue compense les gigues de synchronisation du réseau et de la carte son. La taille de la mémoire tampon influence donc la qualité du flux audio (le nombre de désynchronisations) et le délai global (plus la mémoire tampon est longue, plus le délai est important). - - Audio Channels - Canaux audio + + You can set the jitter buffer size manually for the local client and the remote server. For the local jitter buffer, dropouts in the audio stream are indicated by the light below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun has taken place and the audio stream is interrupted. + Vous pouvez définir manuellement la taille du tampon de gigue pour le client local et le serveur distant. Pour la mémoire tampon de gigue locale, les désynchronisations dans le flux audio sont indiquées par le voyant situé sous les chariots de taille de la mémoire tampon de gigue. Si le voyant devient rouge, cela signifie qu'il y a eu un dépassement ou une sous-utilisation de la mémoire tampon et que le flux audio est interrompu. - - 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 mono-in/stereo-out mode the audio signal which is sent to the server is mono but the return signal is stereo. This is useful if the sound card has 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. - Sélectionnez le nombre de canaux audio à utiliser. Trois modes sont disponibles. Les modes mono et stéréo utilisent respectivement un et deux canaux audio. Dans le mode mono-in/stereo-out, le signal audio qui est envoyé au serveur est mono mais le signal de retour est stéréo. Ceci est utile dans le cas où l'interface audio place l'instrument sur un canal d'entrée et le microphone sur l'autre canal. Dans ce cas, les deux signaux d'entrée peuvent être mélangés dans un canal mono mais le mixage du serveur peut être entendu en stéréo. + + If the Auto setting 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 Auto is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). + Si le paramètre Auto est activé, les tampons de gigue du client local et du serveur distant sont automatiquement réglés en fonction des mesures de la gigue de synchronisation du réseau et de la carte son. Si le paramètre Auto est activé, les chariots de la taille des tampons de gigue sont désactivés (ils ne peuvent pas être déplacés avec la souris). - - 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. - L'activation du mode de streaming stéréo augmentera le débit de données du flux. Assurez-vous que le débit montant actuel ne dépasse pas la bande passante disponible de votre connexion internet. + + If the Auto setting 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 tweak the audio delay/latency it is recommended to disable the Auto setting and to lower the jitter buffer size manually by using the sliders until your personal acceptable amount of dropouts is reached. The LED indicator will display the audio dropouts of the local jitter buffer with a red light. + Si le paramètre Auto est activé, les tampons réseau du client local et du serveur distant sont réglés sur une valeur prudente pour minimiser la probabilité d'interruption de l'audio. Pour régler le délai/latence audio, il est recommandé de désactiver le paramètre Auto et de réduire manuellement la taille du tampon de gigue en utilisant les chariots jusqu'à ce que le nombre d'interruptions soit acceptable. L'indicateur LED affichera les désynchronisations audio du tampon de gigue local avec un voyant rouge. - - In 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. - Dans le cas du mode de streaming stéréo, aucune sélection de canal audio pour l'effet de réverbération ne sera disponible dans la fenêtre principale puisque l'effet est appliqué sur les deux canaux dans ce cas. + + The buffer delay setting is a fundamental setting of this software. This setting has an influence on many connection properties. + Le réglage du délai de la mémoire tampon est un paramètre fondamental de ce logiciel. Ce réglage a une influence sur de nombreuses propriétés de la connexion. - - Audio channels combo box - Choix déroulant de canaux audio + + 64 samples: The preferred setting. Provides the lowest latency but does not work with all sound cards. + 64 échantillons : le paramétrage préféré. Fournit la latence la plus faible mais ne fonctionne pas avec toutes les cartes son. - - Audio Quality - Qualité audio + + 128 samples: Should work for most available sound cards. + 128 échantillons : devrait fonctionner pour la plupart des cartes son disponibles. - - 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 upload rate does not exceed the available bandwidth of your internet connection. - Sélectionnez la qualité audio souhaitée. Une qualité audio faible, normale ou élevée peut être sélectionnée. Plus la qualité audio est élevée, plus le débit de données du flux audio est élevé. Assurez-vous que le débit montant actuel ne dépasse pas la bande passante disponible de votre connexion internet. + + 256 samples: Should only be used on very slow computers or with a slow internet connection. + 256 échantillons : ne devrait être utilisé que sur des ordinateurs très lents ou avec une connexion internet lente. - - Audio quality combo box - Choix déroulant de qualité audio + + Some sound card drivers do not allow the buffer delay to be changed from within the application. In this case the buffer delay setting is disabled and has to be changed using 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. + Certains pilotes de carte son ne permettent pas de modifier le délai de la mémoire tampon à partir de l'application. Dans ce cas, le réglage du délai de mise en mémoire tampon est désactivé et doit être modifié à l'aide du pilote de la carte son. Sous Windows, appuyez sur le bouton Paramètre ASIO pour ouvrir le panneau des paramètres du pilote. Sous Linux, utilisez l'outil de configuration Jack pour modifier la taille de la mémoire tampon. - - New Client Level - Niveau de nouveau client + + If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The application will still work with this setting but with restricted performance. + Si aucune taille de tampon n'est sélectionnée et que tous les paramètres sont désactivés, une taille de tampon non prise en charge est utilisée par le pilote. L'application fonctionnera toujours avec ce paramètre, mais avec des performances limitées. - - 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 of that client was already stored. - Le paramètre de niveau de nouveau client définit le niveau de chariot d'un client nouvellement connecté en pourcentage. C'est-à-dire que si un nouveau client se connecte au serveur actuel, il aura le niveau de chariot initial spécifié si aucun autre niveau de chariot d'une connexion précédente de ce client n'était déjà stocké. + + If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the software. 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. + Si les paramètres de délai de la mémoire tampon sont désactivés, il est interdit par le pilote audio de modifier ce paramètre depuis le logiciel. Sous Windows, appuyez sur le bouton Paramètres ASIO pour ouvrir le panneau des paramètres du pilote. Sous Linux, utilisez l'outil de configuration Jack pour modifier la taille de la mémoire tampon. - - New client level edit box - Dialogue d'édition de niveau de nouveau client + + Skin + thème graphique - - Custom Central Server Address - Adresse personnalisée du serveur central + + Select the skin to be used for the main window. + Sélectionnez le thème graphique à utiliser pour la fenêtre principale. - - The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. This address is only used if the custom server list is selected in the connection dialog. - L'adresse personnalisée du serveur central est l'adresse IP ou l'URL du serveur central sur lequel la liste des serveurs du dialogue de connexion est gérée. Cette adresse n'est utilisée que si la liste de serveurs personnalisée est sélectionnée dans le dialogue de connexion. + + Skin combo box + Choix déroulant de thème graphique - Central Server Address - Adresse du serveur central + + Selects the number of audio channels to be used for communication between client and server. There are three modes available: + Sélectionne le nombre de canaux audio à utiliser pour la communication entre le client et le serveur. Trois modes sont disponibles : - 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. - L'adresse du serveur central est l'adresse IP ou l'URL du serveur central sur lequel la liste des serveurs du dialogue de connexion est gérée. Avec le type d'adresse du serveur central, on peut soit sélectionner la région de localisation parmi les serveurs centraux par défaut, soit spécifier une adresse manuelle. + + and + et - Default central server type combo box - Choix déroulant de type de serveur central par défaut + + These modes use one and two audio channels respectively. + Ces modes utilisent respectivement un et deux canaux audio. - - Central server address line edit - Ligne d'édition pour l'adresse du serveur central + + Mono in/Stereo-out + Entrée mono/sortie stéréo - - Current Connection Status Parameter - Paramètre de l'état de la connexion actuelle + + The audio signal sent to the server is mono but the return signal is stereo. This is useful if the sound card has the instrument on one input channel and the microphone on the other. In that case the two input signals can be mixed to one mono channel but the server mix is heard in stereo. + Le signal audio envoyé au serveur est mono mais le signal de retour est stéréo. Ceci est utile si la carte son a l'instrument sur un canal d'entrée et le microphone sur l'autre. Dans ce cas, les deux signaux d'entrée peuvent être mélangés sur un canal mono mais le mixage du serveur est entendu en stéréo. - - The ping time is the time required for the audio stream to travel from the client to the server and back again. 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. - Le temps de ping est le temps nécessaire pour que le flux audio voyage du client au serveur et vice-versa. Ce délai est introduit par le réseau. Ce délai doit être de 20 ou 30 ms. Si ce délai est supérieur (par exemple 50-60 ms), la distance qui vous sépare du serveur est trop importante ou votre connexion internet n'est pas suffisante. + + Enabling + Activer - - The overall delay is calculated from the current ping time and the delay which is introduced by the current buffer settings. - Le délai global est calculé à partir du temps de ping actuel et du délai qui est introduit par les paramètres actuels de la mémoire tampon. + + In stereo streaming mode, no audio channel selection for the reverb effect will be available on the main window since the effect is applied to both channels in this case. + En mode de flux stéréo, aucune sélection de canal audio pour l'effet de réverbération ne sera disponible dans la fenêtre principale puisque l'effet est appliqué aux deux canaux dans ce cas. - - 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 your internet connection by, e.g., using speedtest.net). - Le débit montant dépend de la taille actuelle du paquet audio et du réglage de la compression audio. Assurez-vous que le débit montant n'est pas supérieur au débit disponible (vérifiez les capacités montant de votre connexion internet en utilisant, par exemple, speedtest.net). + + The higher the audio quality, the higher your audio stream's data rate. Make sure your upload rate does not exceed the available bandwidth of your internet connection. + Plus la qualité audio est élevée, plus le débit de données de votre flux audio est élevé. Assurez-vous que votre débit montant ne dépasse pas la bande passante disponible de votre connexion internet. - - If this LED indicator turns red, you will not have much fun using the - Si ce voyant devient rouge, vous n'aurez pas beaucoup de plaisir à utiliser le logiciel + + This setting defines the fader level of a newly connected client in percent. If a new client connects to the current server, they will get the specified initial fader level if no other fader level from a previous connection of that client was already stored. + Ce paramètre définit le niveau du chariot d'un client nouvellement connecté en pourcentage. Si un nouveau client se connecte au serveur actuel, il obtiendra le niveau de chariot initial spécifié si aucun autre niveau de chariot provenant d'une connexion précédente de ce client n'a déjà été enregistré. - - software. - . + + Leave this blank unless you need to enter the address of a central server other than the default. + Laissez ce champ vide, sauf si vous devez entrer l'adresse d'un serveur central autre que celui par défaut. - - ASIO Setup - Paramètres ASIO + + Central server address combo box + Boîte combo d'adresses du serveur central - - Mono - Mono + + The Ping Time is the time required for the audio stream to travel from the client to the server and back again. This delay is introduced by the network and should be about 20-30 ms. If this delay is higher than about 50 ms, your distance to the server is too large or your internet connection is not sufficient. + Le temps de ping est le temps nécessaire au flux audio pour aller du client au serveur et revenir. Ce délai est introduit par le réseau et doit être d'environ 20 à 30 ms. Si ce délai est supérieur à environ 50 ms, la distance qui vous sépare du serveur est trop importante ou votre connexion internet n'est pas suffisante. - - Mono-in/Stereo-out - Mono-entrée/stéréo-sortie + + Overall Delay is calculated from the current Ping Time and the delay introduced by the current buffer settings. + Le délai global est calculé à partir du temps de ping actuel et du délai introduit par les paramètres actuels de la mémoire tampon. - - Stereo - Stéréo + + Audio Upstream Rate depends on the current audio packet size and compression setting. Make sure that the upstream rate is not higher than your available internet upload speed (check this with a service such as speedtest.net). + Le débit montant audio dépend de la taille actuelle des paquets audio et du réglage de la compression. Assurez-vous que le débit montant n'est pas supérieur à votre vitesse de téléchargement Internet disponible (vérifiez cela avec un service tel que speedtest.net). - + Low Basse - + + Normal Normale - + High Haute - Manual - Manuel + + Fancy + Fantaisie + + + + Compact + Compact - + Custom Personnalisé - + All Genres Tous les genres - Genre Rock/Jazz - Genre rock/jazz - - - - Genre Classical/Folk/Choir + + Genre Classical/Folk/Choral Genre classique/folk/choeur - + Genre Rock Genre Rock - + Genre Jazz Genre Jazz - + Default Défaut - Default (North America) - Défaut (Amérique du Nord) - - - + preferred préféré - - + + Size: Taille : - + Buffer Delay Délai de temporisation - + Buffer Delay: Délai de temporisation : - - Predefined Address - Adresse prédéfinie - - - - The selected audio device could not be used because of the following error: - Le périphérique audio sélectionné n'a pas pu être utilisé en raison de l'erreur suivante : - - - - The previous driver will be selected. - Le pilote précédent sera sélectionné. - - - - Ok - Ok - CClientSettingsDlgBase @@ -1385,84 +1494,80 @@ Auto - + Local Local - + Server Serveur - - + + Size Taille - + Misc Divers - + Audio Channels Canaux audio - + Audio Quality Qualité audio - + New Client Level Niveau de nouveau client - - % - % + + Skin + Thème graphique - - Fancy Skin - Habillage fantaisie + + Language + Langue - - Display Channel Levels - Afficher les niveaux des canaux + + % + % - + Custom Central Server Address: Adresse personnalisée du serveur central : - Central Server Address: - Adresse du serveur central : - - - + Audio Stream Rate Débit du flux audio - - - + + + val val - + Ping Time Temps de réponse - + Overall Delay Délai global @@ -1470,100 +1575,95 @@ CConnectDlg - + Server List Liste de serveurs - - 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. - La liste de serveurs affiche une liste des serveurs disponibles qui sont inscrits sur le serveur central. Sélectionnez un serveur dans la liste et appuyez sur le bouton de connexion pour vous connecter à ce serveur. Vous pouvez également double-cliquer sur un serveur de la liste pour vous y connecter. Si un serveur est occupé, une liste des musiciens connectés est disponible en développant l'élément de la liste. Les serveurs permanents sont indiqués en caractères gras. - - - - 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. - Notez que ça peut prendre un certain temps pour récupérer la liste des serveurs depuis le serveur central. Si aucune adresse de serveur central valide n'est spécifiée dans les paramètres, aucune liste de serveurs ne sera disponible. - - - + Server list view Vue de la liste de serveurs - + Server Address Adresse du serveur - - The IP address or URL of the server running the - L'adresse IP ou l'URL du serveur qui exécute le logiciel serveur + + The Connection Setup window shows a list of available servers. Server operators can optionally list their servers by music genre. Use the List dropdown to select a genre, click on the server you want to join and press the Connect button to connect to it. Alternatively, double click on on the server name. Permanent servers (those that have been listed for longer than 48 hours) are shown in bold. + La fenêtre de configuration de la connexion affiche une liste des serveurs disponibles. Les opérateurs de serveurs peuvent, en option, lister leurs serveurs par genre musical. Utilisez le menu déroulant Liste pour sélectionner un genre, cliquez sur le serveur que vous souhaitez rejoindre et appuyez sur le bouton Connexion pour vous y connecter. Vous pouvez également double-cliquer sur le nom du serveur. Les serveurs permanents (ceux qui ont été listés pendant plus de 48 heures) sont indiqués en gras. - - 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: - doit être paramétré ici. Un numéro optionnel de port peut être ajouté après l'adresse IP ou l'URL en utilisant deux points en tant que séparateur, par exemple, exemple.org : + + If you know the IP address or URL of a server, you can connect to it using the Server name/Address field. An optional port number can be added after the IP address or URL using a colon as a separator, e.g, example.org: + Si vous connaissez l'adresse IP ou l'URL d'un serveur, vous pouvez vous y connecter en utilisant le champ Nom/Adresse du serveur. Un numéro de port optionnel peut être ajouté après l'adresse IP ou l'URL en utilisant deux points comme séparateur, par exemple, example.org : - - . A list of the most recent used server IP addresses or URLs is available for selection. - . Une liste des adresses IP ou URL de serveur les plus récentes est disponible pour la sélection. + + . The field will also show a list of the most recently used server addresses. + . Le champ affichera également une liste des adresses de serveurs les plus récemment utilisées. - + Server address edit box Dialogue d'édition d'addresse de serveur - + Holds the current server IP address or URL. It also stores old URLs in the combo box list. Contient l'adresse IP ou l'URL du serveur actuel. Il stocke également les anciennes URL dans la liste déroulante. - + Server List Selection Sélection de la liste des serveurs - + Selects the server list to be shown. Sélectionne la liste de serveurs à afficher. - + Server list selection combo box Liste déroulante de sélection de la liste des serveurs - + Filter Filtre - + The server list is filtered by the given text. Note that the filter is case insensitive. La liste des serveurs est filtrée par le texte donné. Notez que le filtre n'est pas sensible à la casse. - + Filter edit box Dialogue d'édition de filtre - + Show All Musicians Afficher tous les musiciens - + 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. Si vous cochez cette case, les musiciens de tous les serveurs sont affichés. Si vous décochez la case, tous les éléments de la vue en liste sont regroupés. - + Show all musicians check box Case-à-cocher pour afficher tous les musiciens + + + Type # for occupied servers + Tapez # pour les serveurs occupés + CConnectDlgBase @@ -1609,8 +1709,8 @@ - Server Name/Address - Nom du serveur / adresse + Server Address + Adresse du serveur @@ -1626,686 +1726,820 @@ CHelpMenu - + &Help &Aide - - + + Getting &Started... Premier pa&s... - + Software &Manual... &Manuel du logiciel... - + What's &This Qu'est-ce que c'est ? - + &About... À &propos - CLicenceDlg - - - I &agree to the above licence terms - J'&accepte les conditions de licence ci-dessus - - - - Accept - Accepter - - - - Decline - Décliner - - - - By connecting to this server and agreeing to this notice, you agree to the following: - En vous connectant à ce serveur et en acceptant le présent avis, vous acceptez ce qui suit : - - - - 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 - Vous acceptez que toutes les données, sons ou autres œuvres transmises à ce serveur soient détenus et créés par vous ou vos ayant-droits, et que vous rendiez ces données, sons ou autres œuvres disponibles via la licence Creative Commons suivante (pour plus d'informations sur cette licence, voir - + CLanguageComboBox - - You are free to: - Vous êtes libres de : + + Restart Required + Redémarrage nécessaire - - Share - Partager - - - - copy and redistribute the material in any medium or format - copier et redistribuer le matériel sur tout support ou format - - - - Adapt - Adapter - - - - remix, transform, and build upon the material - remixer, transformer et développer à partir du matériel - - - - The licensor cannot revoke these freedoms as long as you follow the license terms. - Le donneur de licence ne peut pas révoquer ces libertés tant que vous respectez les conditions de la licence. - - - - Under the following terms: - Dans les conditions suivantes : - - - - Attribution - Attribution + + Please restart the application for the language change to take effect. + Veuillez relancer l'application pour que le changement de langue prenne effet. + + + CLicenceDlg - - You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. - Vous devez donner un crédit approprié, fournir un lien vers la licence et indiquer si des modifications ont été apportées. Vous pouvez le faire de toute manière raisonnable, mais pas d'une manière qui suggère que le donneur de licence vous cautionne ou cautionne votre utilisation. + + This server requires you accept conditions before you can join. Please read these in the chat window. + Ce serveur exige que vous acceptiez des conditions avant de pouvoir le rejoindre. Veuillez les lire dans la fenêtre de tchate. - - NonCommercial - Non commercial + + I have read the conditions and &agree. + J'ai lu les conditions et les &accepte. - - You may not use the material for commercial purposes. - Vous ne pouvez pas utiliser le matériel à des fins commerciales. + + Accept + Accepter - - ShareAlike - Partager à l'identique + + Decline + Décliner + + + CMultiColorLED - - If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. - Si vous remixez, transformez ou développez à partir du matériel, vous devez distribuer vos contributions sous la même licence que l'original. + + Red + Rouge - - No additional restrictions - Aucune restriction supplémentaire + + Yellow + Jaune - - You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. - Vous ne pouvez pas appliquer des termes juridiques ou des mesures technologiques qui empêchent légalement d'autres personnes de faire ce que la licence autorise. + + Green + Vert CMusProfDlg - - server. This tag will also show up at each client which is connected to the same server as you. If the name is left empty, the IP address is shown instead. - . Cette balise apparaîtra également sur chaque client connecté au même serveur que vous. Si le nom est laissé vide, l'adresse IP est affichée à la place. - - - + Alias or name edit box Dialogue d'édition de pseudo ou de nom - + Instrument picture button Bouton d'image d'instrument - + Country flag button Bouton de drapeau de pays - + City edit box Dialogue d'édition de ville - + Skill level combo box Choix déroulant de niveau de compétence - - - + + + None Aucune - - + + Musician Profile Profil de musicien - + Alias/Name Pseudo/nom - + Instrument Instrument - + Country Pays - + City Ville - + Skill Compétence - + &Close &Fermer - + Beginner Débutant - + Intermediate Intermédiaire - + Expert Expert - - 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 in. The city you live in and the skill level playing your instrument may also be added. - Indiquez ici votre nom ou un pseudonyme afin que les autres musiciens avec lesquels vous voulez jouer sachent qui vous êtes. Vous pouvez également mettre une photo de l'instrument dont vous jouez et un drapeau du pays dans lequel vous vivez. La ville dans laquelle vous vivez et le niveau de compétence pour jouer de votre instrument peuvent également être ajoutés. + + Write your name or an alias here so the other musicians you want to play with know who you are. You may also add a picture of the instrument you play and a flag of the country you are located in. Your city and skill level playing your instrument may also be added. + Écrivez votre nom ou un pseudonyme ici pour que les autres musiciens avec lesquels vous voulez jouer sachent qui vous êtes. Vous pouvez également ajouter une photo de l'instrument dont vous jouez et un drapeau du pays dans lequel vous vous trouvez. Vous pouvez également ajouter votre ville et votre niveau de compétence pour jouer de votre instrument. - - What you set here will appear at your fader on the mixer board when you are connected to a - Ce que vous réglez ici apparaîtra au niveau de votre fader sur la table de mixage lorsque vous serez connecté à un serveur + + What you set here will appear at your fader on the mixer board when you are connected to a Jamulus server. This tag will also be shown at each client which is connected to the same server as you. + Ce que vous réglez ici apparaîtra au niveau de votre chariot sur la table de mixage lorsque vous serez connecté à un serveur Jamulus. Cette étiquette sera également affichée dans chaque client qui est connecté au même serveur que vous. - + Drum Set Batterie - + Djembe Djembé - + Electric Guitar Guitare électrique - + Acoustic Guitar Guitare accoustique - + Bass Guitar Guitare basse - + Keyboard Clavier - + Synthesizer Synthétiseur - + Grand Piano Piano à queue - + Accordion Accordéon - + Vocal Voix - + Microphone Microphone - + Harmonica Harmonica - + Trumpet Trompette - + Trombone Trombone - + French Horn Cor d'harmonie - + Tuba Tuba - + Saxophone Saxophone - + Clarinet Clarinette - + Flute Flute - + Violin Violon - + Cello Violoncelle - + Double Bass Contrebasse - + Recorder Enregistreur - + Streamer Diffuseur - + Listener Auditeur - + Guitar+Vocal Guitare+voix - + Keyboard+Vocal Clavier+voix - + Bodhran Bodhran - + Bassoon Basson - + Oboe Hautbois - + Harp Harpe - + Viola Alto - + Congas Congas - + Bongo Bongo - + Vocal Bass Voix basse - + Vocal Tenor Voix ténor - + Vocal Alto Voix alto - + Vocal Soprano Voix soprano - + Banjo Banjo - + Mandolin Mandoline - - - CServerDlg - - - Client List - Liste des clients - - - 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. - La liste des clients affiche tous les clients qui sont actuellement connectés à ce serveur. Certaines informations sur les clients, telles que les adresses IP et le nom, sont données pour chaque client connecté. + + Ukulele + Ukulélé - - Connected clients list view - Vue de la liste dess clients connectés + + Bass Ukulele + Ukulélé basse - - Start Minimized on Operating System Start - Démarrage minimisé au lancement du système d'exploitation + + Vocal Baritone + Voix baryton - - If the start minimized on operating system start check box is checked, the - Si la case à cocher "Démarrage minimisé au lancement du système d'exploitation" est cochée, le serveur + + Vocal Lead + Voix principale - - server will be started when the operating system starts up and is automatically minimized to a system task bar icon. - sera lancé au démarrage du système d'exploitation et est automatiquement minimisé dans une icône de la barre des tâches du système. + + Mountain Dulcimer + Dulcimer de montagne - - Show Creative Commons Licence Dialog - Dialogue d'affichage de la licence Creative Commons + + Scratching + Scratch - - If enabled, a Creative Commons BY-NC-SA 4.0 Licence dialog is shown each time a new user connects the server. - Si activé, une boîte de dialogue de licence Creative Commons BY-NC-SA 4.0 est affichée chaque fois qu'un nouvel utilisateur se connecte au serveur. + + Rapping + Rap - - Make My Server Public - Rendre mon serveur public + + No Name + Sans nom + + + CServerDlg - - If the Make My Server Public check box is checked, this server registers itself at the central server so that all - Si la case Rendre mon serveur public est cochée, ce serveur s'inscrit sur le serveur central afin que tous les utilisateurs de + + Client List + Liste des clients - - users can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. - puisse voir le serveur dans la liste des serveurs du dialogue de connexion et s'y connecter. L'inscription du serveur est renouvelée périodiquement pour s'assurer que tous les serveurs de la liste des serveurs du dialogue de connexion sont effectivement disponibles. + + 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. + La liste des clients affiche tous les clients qui sont actuellement connectés à ce serveur. Certaines informations sur les clients, telles que les adresses IP et le nom, sont données pour chaque client connecté. - - Register Server Status - État du serveur inscrit + + Connected clients list view + Vue de la liste dess clients connectés - If the Make My Server Public check box is checked, this will show whether registration with the central server is successful. - Si la case Rendre mon serveur public est cochée, cela indiquera le succès de l'enregistrement auprès du serveur central. + + Start Minimized on Operating System Start + Démarrage minimisé au lancement du système d'exploitation - Central Server Address - Adresse du serveur central + + Make My Server Public + Rendre mon serveur public - 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. - L'adresse du serveur central est l'adresse IP ou l'URL du serveur central auquel ce serveur est inscrit. Avec le type d'adresse du serveur central, on peut soit sélectionner la région locale parmi les serveurs centraux par défaut, soit spécifier une adresse manuelle. + + Register Server Status + État du serveur inscrit - + If the Make My Server Public check box is checked, this will show whether registration with the central server is successful. If the registration failed, please choose another server list. Si la case Rendre mon serveur public est cochée, cela indiquera si l'enregistrement auprès du serveur central est réussi. Si l'enregistrement a échoué, veuillez choisir une autre liste de serveurs. - Default central server type combo box - Choix déroulant de type de serveur central par défaut + + If the start minimized on operating system start check box is checked, the server will be started when the operating system starts up and is automatically minimized to a system task bar icon. + Si la case "Démarrage minimisé au démarrage du système d'exploitation" est cochée, le serveur sera démarré au démarrage du système d'exploitation et sera automatiquement réduit à une icône de la barre des tâches du système. + + + + If the Make My Server Public check box is checked, this server registers itself at the central server so that all users of the application can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. + Si la case Rendre mon serveur public est cochée, ce serveur s'enregistre sur le serveur central afin que tous les utilisateurs de l'application puissent voir le serveur dans la liste des serveurs de dialogue de connexion et s'y connecter. L'inscription du serveur est renouvelé périodiquement pour s'assurer que tous les serveurs de la liste des serveurs du dialogue de connexion sont effectivement disponibles. - + Custom Central Server Address Adresse personnalisée du serveur central - + The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. L'adresse personnalisée du serveur central est l'adresse IP ou l'URL du serveur central sur lequel la liste des serveurs du dialogue de connexion est gérée. - + Central server address line edit Ligne d'édition pour l'adresse du serveur central - + Server List Selection Sélection de la liste des serveurs - + Selects the server list (i.e. central server address) in which your server will be added. Sélectionne la liste de serveurs (c-à-d l'adresse du serveur central) dans laquelle votre serveur sera ajouté. - + Server list selection combo box Liste déroulante de sélection de la liste des serveurs - + Server Name Nom du serveur - - 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. - Le nom du serveur identifie votre serveur dans la liste des serveurs du dialogue de connexion chez les clients. Si aucun nom n'est donné, l'adresse IP est affichée à la place. + + The server name identifies your server in the connect dialog server list at the clients. + Le nom du serveur identifie votre serveur dans la liste des serveurs du dialogue de connexion chez les clients. - + Server name line edit Ligne d'édition pour le nom du serveur - + Location City Ville de localisation - + 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. La ville dans laquelle ce serveur est situé peut être définie ici. Si un nom de ville est saisi, il sera affiché dans la liste des serveurs du dialogue de connexion chez les clients. - + City where the server is located line edit Ligne d'édition pour la ville où est situé le serveur - + Location country Pays de localisation - + 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. Le pays dans lequel ce serveur est situé peut être défini ici. Si un pays est saisi, il sera affiché dans la liste des serveurs du dialogue de connexion chez les clients. - + Country where the server is located combo box Choix déroulant du pays où le serveur est situé + + + Display dialog to select recording directory button + Afficher le dialogue pour sélectionner le bouton du répertoire d'enregistrement + + + + + Main Recording Directory + Répertoire principal des enregistrements + + + + Click the button to open the dialog that allows the main recording directory to be selected.The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). + Cliquez sur le bouton pour ouvrir la boîte de dialogue permettant de sélectionner le répertoire d'enregistrement principal. La valeur choisie doit exister et être inscriptible (permettre la création de sous-répertoires par l'utilisateur sous lequel Jamulus fonctionne). + + + + Main recording directory text box (read-only) + Zone de texte du répertoire principal d'enregistrement (lecture seule) + + + + The current value of the main recording directory. The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). Click the button to open the dialog that allows the main recording directory to be selected. + La valeur actuelle du répertoire principal d'enregistrement. La valeur choisie doit exister et être inscriptible (permettre la création de sous-répertoires par l'utilisateur sous lequel Jamulus fonctionne). Cliquez sur le bouton pour ouvrir la boîte de dialogue permettant de sélectionner le répertoire d'enregistrement principal. + - + Clear the recording directory button + Effacer le bouton du répertoire d'enregistrement + + + + Clear Recording Directory + Effacer le répertoire des enregistrements + + + + Click the button to clear the currently selected recording directory. This will prevent recording until a new value is selected. + Cliquez sur le bouton pour effacer le répertoire d'enregistrement actuellement sélectionné. Cela empêchera l'enregistrement jusqu'à ce qu'une nouvelle valeur soit sélectionnée. + + + + Checkbox to turn on or off server recording + Case à cocher pour activer ou désactiver l'enregistrement du serveur + + + + Enable Recorder + Activer l'enregistreur + + + + Checked when the recorder is enabled, otherwise unchecked. The recorder will run when a session is in progress, if (set up correctly and) enabled. + Coché lorsque l'enregistreur est activé, sinon non coché. L'enregistreur fonctionnera lorsqu'une session est en cours, si (configuré correctement et) activé. + + + + Current session directory text box (read-only) + Zone de texte du répertoire de la session en cours (en lecture seule) + + + + Current Session Directory + Répertoire de la session en cours + + + + Enabled during recording and holds the current recording session directory. Disabled after recording or when the recorder is not enabled. + Activé pendant l'enregistrement et contient le répertoire de la session d'enregistrement en cours. Désactivé après l'enregistrement ou lorsque l'enregistreur n'est pas activé. + + + + Recorder status label + Ètiquette de statut d'enregistreur + + + + Recorder Status + Statut de l'enregistreur + + + + Displays the current status of the recorder. The following values are possible: + Affiche l'état actuel de l'enregistreur. Les valeurs suivantes sont possibles : + + + + No recording directory has been set or the value is not useable + Aucun répertoire d'enregistrement n'a été défini ou la valeur n'est pas utilisable + + + + Recording has been switched off + L'enregistrement a été désactivé + + + + by the UI checkbox + par la case à cocher de l'interface graphique + + + + , either by the UI checkbox or SIGUSR2 being received + , soit en cochant la case de l'interface graphique ou en recevant SIGUSR2 + + + + There is no one connected to the server to record + Il n'y a personne connecté au serveur pour enregistrer + + + + The performers are being recorded to the specified session directory + Les interprètes sont enregistrés dans le répertoire de session spécifié + + + + NOTE + NOTE + + + + If the recording directory is not useable, the problem will be displayed in place of the directory. + Si le répertoire d'enregistrement n'est pas utilisable, le problème sera affiché à la place du répertoire. + + + + Server welcome message edit box + Dialogue d'édition du message de bienvenue du serveur + + + + Server Welcome Message + Message de bienvenue du serveur + + + + A server welcome message text is displayed in the chat window if a musician enters the server. If no message is set, the server welcome is disabled. + Un message de bienvenue du serveur est affiché dans la fenêtre de tchate si un musicien entre sur le serveur. Si aucun message n'est défini, le message de bienvenue du serveur est désactivé. + + + + Type a message here. If no message is set, the server welcome is disabled. + Tapez un message ici. Si aucun message n'est défini, l'accueil du serveur est désactivé. + + + + software upgrade available + mise à jour du logiciel disponible + + + + ERROR + ERREUR + + + + Request new recording button + Demander un nouveau bouton d'enregistrement + + + + New Recording + Nouvel enregistrement + + + + During a recording session, the button can be used to start a new recording. + Pendant une session d'enregistrement, le bouton peut être utilisé pour démarrer un nouvel enregistrement. + + + + E&xit &Quitter - + &Hide Cac&hé - - - + + + server serveur - + &Open &Ouvrir - - server - serveur + + Select Main Recording Directory + Sélectionner le répertoire principal des enregistrements - - Predefined Address - Adresse prédéfinie + + Recording + Enregistrement - Manual - Manuel + + Not recording + Ne pas enregistrer - Default - Défaut + + Not initialised + Non initialisé - Default (North America) - Défaut (Amérique du nord) + + Not enabled + Non activé - + Server serveur - + &Window &Fenêtre - + Unregistered Non inscrit - + Bad address Mauvaise adresse - + Registration requested Inscription demandée - + Registration failed Échec de l'inscription - + Check server version Vérifier la version du serveur - + Registered Inscrit - + Central Server full Serveur central rempli - + + Your server version is too old + La version de votre serveur est trop vieille + + + + Requirements not fulfilled + Exigences non satisfaites + + + Unknown value Valeur inconnue @@ -2319,7 +2553,7 @@ - + Name Nom @@ -2329,14 +2563,29 @@ Taille du tampon de gigue - + + Server Setup + Paramètre du serveur + + + + Chat Window Welcome (HTML/CSS Supported) + Bienvenue dans la fenêtre de tchate (HTML/CSS pris en charge) + + + + Options + Options + + + Start Minimized on Windows Start Démarrage minimisé au lancement de Windows - - Show Creative Commons BY-NC-SA 4.0 Licence Dialog - Afficher le dialogue de la licence Creative Commons BY-NC-SA 4.0 + + Update check + Vérification de mise à jour @@ -2344,39 +2593,56 @@ Rendre mon serveur public (inscrire mon serveur dans la liste des serveurs) - + + Genre + Genre + + + + STATUS ÉTAT - + Custom Central Server Address: Adresse personnalisée du serveur central : - Central Server Address: - Adresse du serveur central : + + Recording Directory + Répertoire des enregistrements - + + Enable Jam Recorder + Activer l'enregistreur de bœuf + + + + New Recording + Nouvel enregistrement + + + + Language + Langue + + + My Server Info Informations de mon serveur - + Location: City Emplacement : ville - + Location: Country Emplacement : pays - - - TextLabelNameVersion - - CSound @@ -2417,98 +2683,114 @@ Impossible d'activer le client Jack. - + The Jack server was shut down. This software requires a Jack server to run. Try to restart the software to solve the issue. Le serveur Jack a été fermé. Ce logiciel nécessite un serveur Jack pour fonctionner. Essayez de redémarrer le logiciel pour résoudre le problème. - + CoreAudio input AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. L'appel d'entrée AudioHardwareGetProperty CoreAudio a échoué failed. Il semble qu'aucune carte son ne soit disponible dans le système. - + CoreAudio output AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. L'appel de sortie AudioHardwareGetProperty CoreAudio a échoué failed. Il semble qu'aucune carte son ne soit disponible dans le système. - + Current system audio input device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. Le taux d'échantillonnage de %1 Hz du périphérique d'entrée audio du système actuel n'est pas pris en charge. Veuillez ouvrir la configuration Audio-MIDI dans Applications->Utilitaires et essayer de définir un taux d'échantillonnage de %2 Hz. - + + + The current selected audio device is no longer present in the system. + Le périphérique audio actuellement sélectionné n'est plus présent dans le système. + + + + The audio input device is no longer available. + Le périphérique d'entrée audio n'est plus disponible. + + + + The audio output device is no longer available. + Le périphérique de sortie audio n'est plus disponible. + + + Current system audio output device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. Le taux d'échantillonnage de %1 Hz du périphérique de sortie audio du système actuel n'est pas pris en charge. Veuillez ouvrir la configuration Audio-MIDI dans Applications->Utilitaires et essayer de définir un taux d'échantillonnage de %2 Hz. - + The audio input stream format for this audio device is not compatible with this software. Le format du flux d'entrée audio pour ce périphérique audio n'est pas compatible avec ce logiciel. - + The audio output stream format for this audio device is not compatible with this software. Le format du flux de sortie audio pour ce périphérique audio n'est pas compatible avec ce logiciel. - + The buffer sizes of the current input and output audio device cannot be set to a common value. Please choose other input/output audio devices in your system settings. Les tailles de tampon du périphérique audio d'entrée et de sortie actuel ne peuvent pas être réglées à une valeur commune. Veuillez choisir d'autres périphériques audio d'entrée/sortie dans les paramètres de votre système. - + The audio driver could not be initialized. Le pilote audio n'a pas pu être initialisé. - + The audio device does not support the required sample rate. The required sample rate is: Le périphérique audio ne prend pas en charge la fréquence d'échantillonnage requise. La fréquence d'échantillonnage requise est : - + The audio device does not support setting the required sampling rate. This error can happen if you have an audio interface like the Roland UA-25EX where you set the sample rate with a hardware switch on the audio device. If this is the case, please change the sample rate to Le périphérique audio ne permet pas de régler la fréquence d'échantillonnage requise. Cette erreur peut se produire si vous avez une interface audio comme le Roland UA-25EX où vous réglez la fréquence d'échantillonnage avec un commutateur matériel sur le périphérique audio. Si c'est le cas, veuillez changer la fréquence d'échantillonnage à - + Hz on the device and restart the Hz sur le péripéhrique et redémarrer le logiciel - + software. . - + The audio device does not support the required number of channels. The required number of channels for input and output is: Le périphérique audio ne prend pas en charge le nombre de canaux requis. Le nombre de canaux requis pour l'entrée et la sortie est : - - + + Required audio sample format not available. Le format de l'échantillon audio requis n'est pas disponible. - + No ASIO audio device (driver) found. Aucun périphérique audio ASIO (pilote) trouvé. - + The Le logiciel - + software requires the low latency audio interface ASIO to work properly. This is not a 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 the ASIO4All driver. nécessite l'interface audio à faible latence ASIO pour fonctionner correctement. Il ne s'agit pas d'une interface audio Windows standard et un pilote audio spécial est donc nécessaire. Soit votre carte son dispose d'un pilote ASIO natif (ce qui est recommandé), soit vous pouvez utiliser d'autres pilotes comme le pilote ASIO4All. - + Error closing stream: $s Erreur de fermeture du flux : $s @@ -2516,55 +2798,68 @@ CSoundBase - - Invalid device selection. - Sélection de périphérique invalide. - - - - The audio driver properties have changed to a state which is incompatible with this software. The selected audio device could not be used because of the following error: - Les propriétés du pilote audio ont changé et sont devenues incompatibles avec ce logiciel. Le périphérique audio sélectionné n'a pas pu être utilisé en raison de l'erreur suivante : + + The selected audio device could not be used because of the following error: + Le périphérique audio sélectionné n'a pas pu être utilisé en raison de l'erreur suivante : - - Please restart the software. - Veuillez redémarrer le logiciel + + The previous driver will be selected. + Le pilote précédent sera sélectionné. - - Close - Fermer + + The previously selected audio device is no longer available or the audio driver properties have changed to a state which is incompatible with this software. We now try to find a valid audio device. This new audio device might cause audio feedback. So, before connecting to a server, please check the audio device setting. + Le périphérique audio précédemment sélectionné n'est plus disponible ou les propriétés du pilote audio sont passées à un état incompatible avec ce logiciel. Nous essayons à présent de trouver un périphérique audio valide. Ce nouveau périphérique audio peut provoquer un retour audio. Aussi, avant de vous connecter à un serveur, veuillez vérifier le réglage du périphérique audio. - + No usable Pas de périphérique audio (pilote) - + audio device (driver) found. utilisable trouvé - + In the following there is a list of all available drivers with the associated error message: Vous trouverez ci-dessous une liste de tous les pilotes disponibles avec le message d'erreur associé : - + Do you want to open the ASIO driver setups? Voulez-vous ouvrir les configurations des pilotes ASIO ? - + could not be started because of audio interface issues. n'a pas pu être lancé en raison de problèmes d'interface audio. + + QCoreApplication + + + , Version + , version + + + + Internet Jam Session Software + Logiciel de bœuf sur internet + + + + Released under the GNU General Public License (GPL) + Publié sous la licence publique générale GNU (GPL) + + global - + For more information use the What's This help (help menu, right mouse button or Shift+F1) Pour plus d'informations, utilisez l'aide Qu'est-ce que c'est (menu d'aide, bouton droit de la souris ou Maj+F1) diff --git a/src/res/translation/translation_it_IT.qm b/src/res/translation/translation_it_IT.qm index 433295f4d2..f94bb1653e 100644 Binary files a/src/res/translation/translation_it_IT.qm and b/src/res/translation/translation_it_IT.qm differ diff --git a/src/res/translation/translation_it_IT.ts b/src/res/translation/translation_it_IT.ts index 1442552d0c..020254316d 100644 --- a/src/res/translation/translation_it_IT.ts +++ b/src/res/translation/translation_it_IT.ts @@ -4,114 +4,139 @@ CAboutDlg - The - Il software + Il software - software enables musicians to perform real-time jam sessions over the internet. - permette ai musicisti di creare jam session in real time attraverso internet. + permette ai musicisti di creare jam session in real time attraverso internet. - There is a - Attraverso un Server + Attraverso un Server - server which collects the audio data from each - che acquisisce i dati audio da ogni + che acquisisce i dati audio da ogni - client, mixes the audio data and sends the mix back to each client. - client; mixa i dati audio e li rimanda ad ogni client. + client; mixa i dati audio e li rimanda ad ogni client. - uses the following libraries, resources or code snippets: - usa le seguenti librerie, risorse o pezzi di codice: + usa le seguenti librerie, risorse o parti di codice: - + Qt cross-platform application framework - + Audio reverberation code by Perry R. Cook and Gary P. Scavone Audio reverberation sviluppato da Perry R. Cook and Gary P. Scavone - + Some pixmaps are from the - Molte pixmaps provengono da + Alcune pixmaps provengono da - Country flag icons from Mark James - Icone delle bandiere a cura di Mark James + Icone delle bandiere a cura di Mark James - + + This app enables musicians to perform real-time jam sessions over the internet. + Dà la possibilità ai musicisti di realizzare sessioni live attraverso internet. + + + + There is a server which collects the audio data from each client, mixes the audio data and sends the mix back to each client. + Il server acquisisce i dati audio di ogni client, mixa i dati audio e li rimanda ad ogni client connesso. + + + + This app uses the following libraries, resources or code snippets: + Questa applicazione usa le seguenti librerie, risorse e parti di codice: + + + + Country flag icons by Mark James + Le icone delle bandiere sono state realizzate da Marl James + + + For details on the contributions check out the - Per maggiori informazioni su chi ha contributio, visitare + Per maggiori informazioni su chi ha contribuito, visitare - + Github Contributors list - Github lista dei collaboratori + Lista dei collaboratori su Github - + Spanish Spagnolo - + French Francese - + Portuguese Portoghese - + Dutch Olandese - + Italian Italiano - + German Tedesco - + + Polish + Polacco + + + + Swedish + Svedese + + + + Slovak + Slovacco + + + About Informazioni su - , Version - , Versione + , Versione - - Internet Jam Session Software - + Released under the GNU General Public License (GPL) + Rilasciato sotto licensa GNU General Public License (GPL) - Under the GNU General Public License (GPL) - Sotto la GNU General Public License (GPL) + Sotto la GNU General Public License (GPL) @@ -160,12 +185,12 @@ CAnalyzerConsole - + Analyzer Console Consolle Analizzatore - + Error Rate of Each Buffer Size Tasso di errore per ogni Buffer @@ -173,18 +198,33 @@ CAudioMixerBoard - + + Personal Mix at the Server + Mixer personale sul Server + + + + When connected to a server, the controls here allow you to set your local mix without affecting what others hear from you. The title shows the server name and, when known, whether it is actively recording. + Quando connessi i fader permettono di regolare i volumi in locale senza influenzare l'ascolto degli altri utenti. L'intestazione mostra il nome de server, se valorizzato, e le informazioni sullo stato della sessione di registrazione se attiva. + + + Server Server - + T R Y I N G T O C O N N E C T I N A T T E S A D I C O N N E S S I O N E - - Personal Mix at the Server: + + RECORDING ACTIVE + Sessione con Registrazione Attiva + + + + Personal Mix at: Mixer personale sul Server: @@ -192,188 +232,271 @@ CChannelFader - + + Pan Bilanciamento - - + + Mute Mute - - + + Solo Solo - + + &No grouping + &Non Assegnato + + + + + + + Assign to group + Assegna al Gruppo + + + + Grp + Grp + + + Channel Level Volume - 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. - Visualizza il livello audio pre-fader di questo canale. A tutti i client connessi al server verrà assegnato un livello audio, lo stesso valore per ciascun client. + Visualizza il livello audio pre-fader di questo canale. A tutti i client connessi al server verrà assegnato un livello audio, lo stesso valore per ciascun client. - + Input level of the current audio channel at the server Livello di input del canale audio corrente sul server - + Mixer Fader Mixer Fader - 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. - Regola il livello audio di questo canale. A tutti i client connessi al server verrà assegnato un fader audio su ciascun client, regolando il mix locale. + Regola il livello audio di questo canale. A tutti i client connessi al server verrà assegnato un fader audio su ciascun client, regolando il mix locale. - + Local mix level setting of the current audio channel at the server Impostazione del livello di volume locale del canale audio corrente sul server - + Status Indicator Indicatore di Stato - + Shows a status indication about the client which is assigned to this channel. Supported indicators are: Visualizza lo stato del client assegnato a questo canale. Gli Stati supportati sono: - Speaker with cancellation stroke: Indicates that the other client has muted you. - Altoparlante segnato: Indica che un altro client ha messo in mute il tuo canale. + Altoparlante segnato: Indica che un altro client ha messo in mute il tuo canale. - + Status indicator label Etichetta dell'indicatore di stato - + Panning Bilanciamento - Sets the panning position from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. - Imposta il Bilancimento da Sinistra a Destra del canale. Funzione abilitata in modalità stero oppure in modalità mono in/stereo out. + Imposta il Bilanciamento da Sinistra a Destra del canale. Funzione abilitata in modalità stereo oppure in modalità mono in/stereo out. - + Local panning position of the current audio channel at the server Bilancimento locale del canale audio corrente sul server - + With the Mute checkbox, the audio channel can be muted. Quando il Mute è selezionato, il canale audio è mutato. - + Mute button Pulsante Mute - 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. - Quando il Solo è attivo, il canale audio sarà in modalità solista escludendo gli altri canali che non saranno più udibili. E' possibile attivare il solo su più canali per sentirli contemporaneamente. + Quando il Solo è attivo, il canale audio sarà in modalità solista escludendo gli altri canali che non saranno più udibili. E' possibile attivare il solo su più canali per sentirli contemporaneamente. - + Solo button Pulsante Solo - + Fader Tag Tag Fader - 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. - Il tag fader identifica il client connesso. Il nome del tag, l'immagine del tuo strumento e una bandiera del tuo paese possono essere impostati nella finestra principale del profilo. + Il tag fader identifica il client connesso. Il nome del tag, l'immagine del tuo strumento e una bandiera del tuo paese possono essere impostati nella finestra principale del profilo. + + + + Displays the pre-fader audio level of this channel. All clients connected to the server will be assigned an audio level, the same value for every client. + Visualizza il livello "pre-fader" di questo canale. Tutti i client connessi al server avranno assegnato un livello audio, lo stesso valore per ogni client. + + + + Adjusts the audio level of this channel. All clients connected to the server will be assigned an audio fader, displayed at each client, to adjust the local mix. + Regola il livello audio di questo canale. A tutti i client connessi sarà assegnatu un fader per regolare il mix audio locale. - + + Speaker with cancellation stroke: Indicates that another client has muted you. + Altoparlate con il segnale rosso: indica che un altro client ha messo il tuo canale in mute. + + + + Sets the pan from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. + Regola il bilanciamento Sinistro - Destro del canale. Funziona solo se abilitata la funzione stereo oppure "mono-in/stereo-out". + + + + With the Solo checkbox, the audio channel can be set to solo which means that all other channels except the soloed channel are muted. It is possible to set more than one channel to solo. + Se selezionato il pulsate "Solo", il canale audio sarà settato nella modalità di "Solo" ovvero tutti i canali saranno mutati ad eccezione di quelli in modalità "Solo". + + + + Group + Raggruppa + + + + With the Grp checkbox, a group of audio channels can be defined. All channel faders in a group are moved in proportional synchronization if any one of the group faders are moved. + Con il comando Rag, può essere definito un gruppo di canali. Tutti i canali raggruppati possono essere modificati in modo proporzionale muovendo uno dei fader del gruppo. + + + + Group button + Raggruppa + + + + The fader tag identifies the connected client. The tag name, a picture of your instrument and the flag of your country can be set in the main window. + La targa sotto il Fader identifica il client connesso. Il nome, l'immagine dello strumento, e la bandiera della nazionalità possono essere settati tramite la finestra del profilo. + + + Mixer channel instrument picture Immagine dello strumento - + Mixer channel label (fader tag) Etichetta del Canale (fader tag) - + Mixer channel country flag Bandiera del Paese - + PAN - Bilanciamento + Bil. (L / R) - + MUTE MUTE - + SOLO SOLO - + + GRP + GRP + + + + M + M + + + + S + S + + + + G + G + + + Alias/Name Identificativo/Nome - + Instrument Strumento - + Location Località - - - + + + Skill Level Livello di Preparazione - + + Alias + Alias + + + Beginner Principiante - + Intermediate Livello Intermedio - + Expert Esperto - + Musician Profile Profilo del Musicista @@ -388,7 +511,7 @@ The chat window shows a history of all chat messages. - Lq finestra della chat mostra la storia di tutti i messaggi. + La finestra della chat mostra la storia di tutti i messaggi. @@ -410,66 +533,88 @@ New chat text edit box Nuova casella di testo della chat + + + Type a message here + Inserisi un messaggio qui + + + + &Edit + &Modifica + + + + Cl&ear Chat History + Cance&lla Storico Chat + + + + Do you want to open the link + Vuoi che il sia aperto + + + + in an external browser? + sul browser? + CChatDlgBase - + Chat Chat - + + &Send + &Invia + + Cl&ear - Can&ella + Can&ella - &Close - &Chiudi + &Chiudi CClientDlg - + Input Level Meter Livello Segnale d'ingresso - The input level indicators show the input level of the two stereo channels of the current selected audio input. - L'idicatore del segnale in ingresso mostra il livello dei due canali stereo scelti come input. + L'idicatore del segnale in ingresso mostra il livello dei due canali stereo scelti come input. - + Make sure not to clip the input signal to avoid distortions of the audio signal. - Controllare di non saturare il livello di input per evitare distorisioni nel segnale audio. + Controllare di non saturare il livello di input per evitare distorsioni nel segnale audio. - If the - Se + Se - software is connected and you play your instrument/sing in the microphone, the LED level 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. - è collegato, suonando lo strumento oppure cantando con il microfono, l'idicatore a LED dovrebbe illuninarsi. In caso contrario probabilmente avete selezionato il canale di ingresso errato (ad es. Line in anziché l'ingresso del microfono) oppure avete impostato un guadagno di input troppo basso nel mixer audio (Windows). + è collegato, suonando lo strumento oppure cantando con il microfono, l'idicatore a LED dovrebbe illuninarsi. In caso contrario probabilmente hai selezionato il canale di ingresso errato (ad es. Line in anziché l'ingresso del microfono) oppure hai impostato un guadagno di input troppo basso nel mixer audio (Windows). - For a proper usage of the - Per un uso corretto di + Per un uso corretto di - software, you should not hear your singing/instrument in the loudspeaker or your headphone when the - non dovreste sentire la voce o lo strumento nelle casse o nelle cuffie quando + non dovresti sentire la voce o lo strumento nelle casse o nelle cuffie quando - software is not connected. This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). - il programma non è connesso. Basta disattivare il canale audio in ingresso nel mixer di riproduzione (non nel mixer di registrazione!). + il programma non è connesso. Basta disattivare il canale audio in ingresso nel mixer di riproduzione (non nel mixer di registrazione!). @@ -487,225 +632,414 @@ Pulsante: Connetti-Disconnetti - Push this button to connect to a server. A dialog where you can select a server will open. If you are connected, pressing this button will end the session. - Cliccare il pulsante per connettersi ad un server. Si aprirà una finestra da dove poter scegliere a quale server connettersi. Se si è già connessi cliccando questo pulsante la connessione verrà interrotta. + Cliccare il pulsante per connettersi ad un server. Si aprirà una finestra da dove poter scegliere a quale server connettersi. Se si è già connessi cliccando questo pulsante la connessione verrà interrotta. - + Connect and disconnect toggle button Pulsante di connessione e disconnessione - Clicking on this button changes the caption of the button from Connect to Disconnect, i.e., it implements a toggle functionality for connecting and disconnecting the - Facendo clic su questo pulsante si modifica la dicitura del pulsante da Connetti a Disconnetti, ovvero implementa una funzionalità di attivazione / disattivazione per la connessione e disconnessione del + Facendo clic su questo pulsante si modifica la dicitura del pulsante da Connetti a Disconnetti, ovvero implementa una funzionalità di attivazione / disattivazione per la connessione e disconnessione del - - software. - programma. + programma. - + Local Audio Input Fader Fader per l'input audio locale - 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 - Con questo fader, è possibile modificare i livelli relativi dei canali audio sinistro e destro. Per un segnale mono si comporta come un mix tra i due canali. Se, ad esempio, un microfono è collegato al canale di ingresso destro e uno strumento è collegato al canale di ingresso sinistro che è molto più forte del microfono, spostare il fader audio nella direzione opposta a L (Sinistra) per bilanciare i volumi + Con questo fader, è possibile modificare i livelli relativi dei canali audio sinistro e destro. Per un segnale mono si comporta come un mix tra i due canali. Se, ad esempio, un microfono è collegato al canale di ingresso destro e uno strumento è collegato al canale di ingresso sinistro che è molto più forte del microfono, spostare il fader audio nella direzione opposta a L (Sinistra) per bilanciare i volumi - - + + L L - + , where , dove - + is the current attenuation indicator. si trova l'attuale indicatore di attenuazione. - + Local audio input fader (left/right) Fader di input locale (Sinistro/Destro) - Reverberation Level - Livello di Reverbero + Livello di Riverbero - 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. - Un effetto di riverbero può essere applicato al canale mono od ad antrambi i canali stereo. La selezione del canale mono e il livello di riverbero possono essere modificati. Se, ad esempio, il segnale del microfono viene immesso nel canale audio destro della scheda audio e deve essere applicato un effetto di riverbero, impostare il selettore di canale a destra e spostare il fader verso l'alto fino a raggiungere il livello di riverbero desiderato. + Un effetto di riverbero può essere applicato al canale mono o ad entrambi i canali stereo. La selezione del canale mono e il livello di riverbero possono essere modificati. Se, ad esempio, il segnale del microfono viene immesso nel canale audio destro della scheda audio e deve essere applicato un effetto di riverbero, impostare il selettore di canale a destra e spostare il fader verso l'alto fino a raggiungere il livello di riverbero desiderato. - The reverberation effect requires significant CPU so 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 switched off and does not cause any additional CPU usage. - L'effetto di reverbero richiede un uso significativo della CPU, quindi è raccomandabile usarlo su computer veloci. Se il livello del reverbero è messo al minimo (come di default), l'effetto è disattivato e non causa rallentamenti sulla CPU. + L'effetto di riverbero richiede un uso significativo della CPU, quindi è raccomandabile usarlo su computer veloci. Se il livello del riverbero è messo al minimo (come di default), l'effetto è disattivato e non causa rallentamenti sulla CPU. - Reverberation effect level setting - Settaggio del livello di Reverbero + Settaggio del livello di Riverbero - Reverberation Channel Selection - Selettore di canale per il Reverbero + Selettore di canale per il Riverbero - 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. - Con questi pulsanti di opzione è possibile scegliere il canale d'ingresso audio su cui viene applicato l'effetto di riverbero. È possibile selezionare il canale di input sinistro o destro. + Con questi pulsanti di opzione è possibile scegliere il canale d'ingresso audio su cui viene applicato l'effetto di riverbero. È possibile selezionare il canale di input sinistro o destro. - Left channel selection for reverberation - Canale Sinistro per il Reverbero + Canale Sinistro per il Riverbero - Right channel selection for reverberation - Canale Destro per il Reverbero + Canale Destro per il Riverbero - + Delay Status LED LED di Stato del Delay - 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 large for jamming. - Il LED di stato del delay indica graficamente il valore assunto in quel momento. Se verde il delay è nei valori ottimali, se giallo indica che durante la sessione si possono verificare situazioni in cui può diventare difficile suonare, se rosso il delay è troppo alto per una sessione. + Il LED di stato del delay indica graficamente il valore assunto in quel momento. Se verde il delay è nei valori ottimali, se giallo indica che durante la sessione si possono verificare situazioni in cui può diventare difficile suonare, se rosso il delay è troppo alto per una sessione. - If this LED indicator turns red, you will not have much fun using the - Se il LED diventa rosso avrete difficoltà nel suonare con + Se il LED diventa rosso avrete difficoltà nel suonare con - + Delay status LED indicator LED di stato del Delay - + Buffers Status LED LED di Stato del Buffer - 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: - Il LED di stato del buffer indica la qualità dello straming. Se verde non sono presenti anomalie nel buffer e lo stream audio non subirà interruzioni. Se rosso lo stream audio subirà interruzioni per causa di uno dei seguenti motivi: + Il LED di stato del buffer indica la qualità dello straming. Se verde non sono presenti anomalie nel buffer e lo stream audio non subirà interruzioni. Se rosso lo stream audio subirà interruzioni per causa di uno dei seguenti motivi: - + The network jitter buffer is not large enough for the current network/audio interface jitter. Il Jitter Buffer non è grande abbastanza per la tipologia di rete/interfaccia audio usate. - The sound card buffer delay (buffer size) is set to too small a value. - Il buffer delay della scheda audio è impostato su un valore troppo basso. + Il buffer delay della scheda audio è impostato su un valore troppo basso. - The upload or download stream rate is too high for the current available internet bandwidth. - La quantià di dati in upload o in download è eccessiva rispetto alla banda internet disponibile. + La quantià di dati in upload o in download è eccessiva rispetto alla banda internet disponibile. + + + + This shows the level of the two stereo channels for your audio input. + Visualizza il livello di input dei due canali stereo. + + + + If the application is connected to a server and you play your instrument/sing into the microphone, the VU 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. + Se il programma è connesso ad un server e voi state suonando o cantando, il VU-Meter sarà in funzione. Se ciò non accade probabilemnte avete settato un ingresso errato oppure il livello di input è troppo basso. + + + + For proper usage of the application, you should not hear your singing/instrument through the loudspeaker or your headphone when the software is not connected.This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). + Per un corretto utilizzo dell'applicazione, non è possibile ascoltare il canto o lo strumento attraverso l'altoparlante o le cuffie quando il programma non è collegato. Basta disattivare l'audio del canale di ingresso nel mixer di riproduzione (non nel mixer di registrazione!). + + + Clicking on this button changes the caption of the button from Connect to Disconnect, i.e., it implements a toggle functionality for connecting and disconnecting the application. + Cliccando su questo pulsante il stato passa da Connesso a Disconnesso, implementa infatti la funzionalità di connessione-disconnessione del programma. + + + + Controls the relative levels of the left and right local audio channels. For a mono signal it acts as a pan between the two channels.For example, if 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 + Controlla i livelli relativi dei canali audio locali sinistro e destro. Per un segnale mono funge da pan tra i due canali. Ad esempio, se un microfono è collegato al canale di ingresso destro e uno strumento è collegato al canale di ingresso sinistro che è molto più forte del microfono, spostare il cursore audio in una direzione in cui viene mostrata l'etichetta sopra il fader + + + + Reverb effect + Effetto Reverbero + + + + Reverb can be applied to one local mono audio channel or to both channels in stereo mode. The mono channel selection and the reverb level can be modified. For example, if a microphone signal is fed in to the right audio channel of the sound card and a reverb effect needs to be applied, set the channel selector to right and move the fader upwards until the desired reverb level is reached. + Il Reverbero può essere applicato sia in modalità mono che stereo. La selezione del canale mono e il livello di riverbero possono essere modificati. Ad esempio, se un segnale del microfono viene immesso nel canale audio destro della scheda audio e deve essere applicato un effetto di riverbero, impostare il selettore di canale su destra e spostare il fader verso l'alto fino a raggiungere il livello di riverbero desiderato. + + + + Reverb effect level setting + Livello dell'effetto di Reverbero + + + + Reverb Channel Selection + Selezione Canale Reverbero + + + + With these radio buttons the audio input channel on which the reverb effect is applied can be chosen. Either the left or right input channel can be selected. + Con questi pulsanti di opzione è possibile scegliere il canale di ingresso audio su cui viene applicato l'effetto riverbero. È possibile selezionare il canale di input sinistro o destro. + + + + Left channel selection for reverb + Canale Sinistro per il Reverbero + + + + Right channel selection for reverb + Canale Destro per il Reverbero + + + + Green + Verde + + + + The delay is perfect for a jam session. + Il delay è perfetto per una live session. + + + + Yellow + Giallo + + + + Red + Rosso + + + + Opens a dialog where you can select a server to connect to. If you are connected, pressing this button will end the session. + Apre una finestra di dialogo in cui è possibile selezionare un server a cui connettersi. Se si è connessi, premere questo pulsante per terminare la sessione. + + + + Shows the current audio delay status: + Visualizza lo stato corrente del delay: + + + + A session is still possible but it may be harder to play. + Una sessione è ancora possibile ma potrebbe essere più difficile suonare. + + + + The delay is too large for jamming. + Il delay è eccessivo per una live session. + + + + If this LED indicator turns red, you will not have much fun using the application. + Se il LED diventa rosso non si avrà una buona esperinza di utilizzo dell'applicazione. + + + + The buffers status LED shows the current audio/streaming status. If the light is red, the audio stream is interrupted. This is caused by one of the following problems: + Il LED di stato del buffer mostra lo stato audio dello streaming corrente. Se la luce è rossa, il flusso audio viene interrotto. Ciò è causato da uno dei seguenti problemi: + + + + The sound card's buffer delay (buffer size) is too small (see Settings window). + Il ritardo della scheda audio(ovvero il buffer size) è troppo basso (vedere i Settaggi della Scheda). + + + + The upload or download stream rate is too high for your internet bandwidth. + La banda passante per lo stream (upload e download) è troppo rispetto alla qualità della connessione internet. - + The CPU of the client or server is at 100%. La CPU del client è satura al 100%. - + Buffers status LED indicator Led di stato del Buffer - - + + C&onnect C&onnetti - + + software upgrade available + Nuova versione disponibile + + + + &File + &File + + + &View &Vista - + &Connection Setup... Setup &Connessione... - + My &Profile... &Profilo Personale... - + C&hat... C&hat... - + &Settings... &Settaggi... - + &Analyzer Console... &Analizzatore... - + + N&o User Sorting + N&on Riordinare i Canali + + + + Sort Users by &City + Ordina i Canali per &Città di provenienza + + + + Use &Two Rows Mixer Panel + Abilita &Vista su due Righe + + + + &Clear All Stored Solo and Mute Settings + &Riprisitna tutti gli stati salvati di SOLO e MUTE + + + + Ok + Ok + + + &Clear All Stored Solo Settings + &Ripristina i canali settati in "Solo" + + + + Set All Faders to New Client &Level + Setta &Livelli del Mixer al valore impostato per i Nuovi Utenti + + + E&xit &Uscita - + + &Load Mixer Channels Setup... + &Carica Setup Mixer... + + + + &Save Mixer Channels Setup... + &Salva Setup Mixer... + + + + &Edit + &Modifica + + + + Sort Users by &Name + Ordina canali per &Nome + + + + Sort Users by &Instrument + Ordina canali per &Strumento + + + + Sort Users by &Group + Ordina Canali per Nome &Utente + + + &Sort Users by Name + &Canali in ordine Alfabetico + + None - Nullo + Nullo - + Center Centro - + R R - + Central Server Server Centrale - + + + Select Channel Setup File + Selezione File di Setup dei Canali + + + user utente - + users utenti - + + Your sound card is not working correctly. Please open the settings dialog and check the device selection and the driver settings. + + + + D&isconnect D&isconnetti @@ -713,367 +1047,353 @@ CClientDlgBase - + Delay Delay - + Buffers Buffer - + Input Ingresso - + L L - + R R - - Settings - Settaggi + + &Mute Myself + &Disattiva Input - - Chat - Chat + + &Settings + &Settaggi - - Mute Myself - Disattiva Input + + &Chat + &Chat - + C&onnect C&onnetti - + Pan Bilanciamento - + Center Centro - + Reverb - Reverbero + Riverbero - + Left Left (Sinistra) - + Right Right (Destra) + + + MUTED (Other people won't hear you) + Muto (gli altri non possono sentirti) + + + + Update check + Controlla Aggiornamenti + CClientSettingsDlg - + Jitter Buffer Size Dimensione Jitter Bufer - 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). - Il Jitter Buffer compensa i ritardi della rete o della scheda audio. La sua dimensione influisce sulla qualità dello stream audio (troppe interruzioni) e sull'OverAll delay (più grande è il buffer, più alto è il delay). + Il Jitter Buffer compensa i ritardi della rete o della scheda audio. La sua dimensione influisce sulla qualità dello stream audio (troppe interruzioni) e sull'OverAll delay (più grande è il buffer, più alto è il delay). - 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 below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun took place and the audio stream is interrupted. - La dimensione del Jitter Buffer può essere settata manualmente sia per il client che per il server. Le problematiche del buffer locale sono segnalate dal LED posto sotto i Fader del Jitter buffer. Se il LED è rosso il buffer è corrotto e lo streamaudio subisce interruzioni. + La dimensione del Jitter Buffer può essere settata manualmente sia per il client che per il server. Le problematiche del buffer locale sono segnalate dal LED posto sotto i Fader del Jitter buffer. Se il LED è rosso il buffer è corrotto e lo streamaudio subisce interruzioni. - + The jitter buffer setting is therefore a trade-off between audio quality and overall delay. I settaggi del jitter buffer regolano il compromesso tra qualità audio e ritardo generale. - 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 Auto check is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). - E' disponibile la funzione di regolazine Automatica del Jitter Buffer. Se Abilitata il Jitter Buffer Locale e Remoto saranno settati in base a delle misure effettuate sulla rete e sul ritardo della scheda audio. In questo caso i fader di regolazione saranno disabilitati(non potranno essere mossi con il muose). + E' disponibile la funzione di regolazine Automatica del Jitter Buffer. Se Abilitata il Jitter Buffer Locale e Remoto saranno settati in base a delle misure effettuate sulla rete e sul ritardo della scheda audio. In questo caso i fader di regolazione saranno disabilitati(non potranno essere mossi con il muose). - If 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 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 with a red light. - Nel caso in cui sia abilitata l'impostazione automatica del jitter buffer, i buffer di rete del client locale e del server remoto sono impostati su un valore di tipo conservativo per ridurre al minimo la probabilità di dropout audio. Per modificare il ritardo / latenza audio, si consiglia di disabilitare la funzionalità di impostazione automatica e di ridurre manualmente la dimensione del JitterBuffer utilizzando i fader fino a raggiungere il limite personale per una qualità audio accettabile. L'indicatore LED visualizzerà i dropout audio del Jitter Buffer locale diventando rosso. + Nel caso in cui sia abilitata l'impostazione automatica del jitter buffer, i buffer di rete del client locale e del server remoto sono impostati su un valore di tipo conservativo per ridurre al minimo la probabilità di dropout audio. Per modificare il ritardo / latenza audio, si consiglia di disabilitare la funzionalità di impostazione automatica e di ridurre manualmente la dimensione del JitterBuffer utilizzando i fader fino a raggiungere il limite personale per una qualità audio accettabile. L'indicatore LED visualizzerà i dropout audio del Jitter Buffer locale diventando rosso. - + Local jitter buffer slider control Controlli per la gestione del Jitter Buffer - + Server jitter buffer slider control Fader del Jitter Buffer del Server - + Auto jitter buffer switch Switch Jitter Buffer Automatico - + Jitter buffer status LED indicator LED di stato del Jitter Buffer - + Sound Card Device Scheda Audio - + The ASIO driver (sound card) can be selected using I driver ASIO (scheda audio) possono essere selezionati usando - + 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. su sistemi operativi Windows. Su MacOS/Linux, non è possibile cambiare driver audio. Se il driver ASIO selezionato non è valido un errore viene visualizzato ripristinando il driver precedentemente attivo e funzionante. - + If the driver is selected during an active connection, the connection is stopped, the driver is changed and the connection is started again automatically. Se il driver viene cambiato mentre si è connessi ad un server, la connessione verrà fermata, il driver sarà sostituito e successivamente la connessione verrà ripristinata automaticamente. - + Sound card device selector combo box Box per la selezione della scheda audio - + If 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. Nel caso in cui vengano usati i driver ASIO4ALL bisogna sapere che questi di solito introducono 10-30 ms di ritardo aggiuntivo. Si consiglia di usare driver ASIO nativi per la scheda audio in uso. - + If you are using the kX ASIO driver, make sure to connect the ASIO inputs in the kX DSP settings panel. Se si usano i driver kX ASIO, accertarsi di connettere l'input ASIO nel pannello dei settaggi kX DSP. - + Sound Card Channel Mapping Mappa dei Canali della Scheda Audio - + If the selected sound card device offers more than one input or output channel, the Input Channel Mapping and Output Channel Mapping settings are visible. Se la scheda audio dispone di diversi Input o Output, verrà visualizzata la mappa dei canali di input e di output. - + For each Per ciascun - + input/output channel (Left and Right channel) a different actual sound card channel can be selected. input/output (Canale Sinistro e Destro) può essere selezionata una scheda audio diversa. - + Left input channel selection combo box Box per la selezione dell'Ingresso Sinistro (Left) - + Right input channel selection combo box Box per la selezione dell'Ingresso Destro (Right) - + Left output channel selection combo box Box per la selezione dell'uscita Sinistra (Left) - + Right output channel selection combo box Box per la selezione dell'uscita Destra (Right) - + Enable Small Network Buffers Abilita Riduzione Buffer di Rete - + 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 Se abilitato, viene attivata la possibilità di usare piccoli pacchetti di rete. L'uso di pacchetti di rete di dimensione ridotta è attivo se la scheda audio supporta un buffer delay inferiore a - + samples. The smaller the network buffers, the lower the audio latency. But at the same time the network load increases and the probability of audio dropouts also increases. samples. Più piccoli sono i pacchetti di rete minore sarà la latenza, ma allo stesso tempo aumenta il carico di rete umentando la possibilità di dropout audio. - + Enable small network buffers check box Check Box per abilitare la riduzione dei pacchetti di rete - + Sound Card Buffer Delay Buffer Delay della scheda audio - The buffer delay setting is a fundamental setting of the - Settare correttamente il Buffer Delay è un operazione fondamentale su + Settare correttamente il Buffer Delay è un operazione fondamentale su - software. This setting has influence on many connection properties. - Questo settaggio ha influenza su molte proprietà di connessione. + Questo settaggio ha influenza su molte proprietà di connessione. - + Three buffer sizes are supported Le dimensioni dei Buffer supportati sono - 64 samples: This is the preferred setting since it provides the lowest latency but does not work with all sound cards. - 64 Campioni: Per le basse latenze ma non funziona su tutte le schede audio. + 64 Campioni: Per le basse latenze ma non funziona su tutte le schede audio. - 128 samples: This setting should work for most available sound cards. - 128 Campioni: Settaggio che trova compatibilità con la maggiorparte delle schede audio. + 128 Campioni: Settaggio che trova compatibilità con la maggiorparte delle schede audio. - 256 samples: This setting should only be used if only a very slow computer or a slow internet connection is available. - 256 Campiono: Settaggio usato su computer connessi a reti lente o su PC obsoleti. + 256 Campiono: Settaggio usato su computer connessi a reti lente o su PC obsoleti. - Some sound card drivers do not allow the buffer delay to be changed from within the - Molte Schede audio non permettono di modificare il buffer delay direttamente tramite l'interfaccia di + Molte Schede audio non permettono di modificare il buffer delay direttamente tramite l'interfaccia di - 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. - In questo caso il Buffer Delay va settato direttamente tramite il software di gestione del driver della scheda audio. Su Windows cliccare sul bottone ASIO Setup per aprire il pannello dei driver. Su linux usare il pannello di configurazione di Jack per settare la dimensione del buffer. + In questo caso il Buffer Delay va settato direttamente tramite il software di gestione del driver della scheda audio. Su Windows cliccare sul bottone ASIO Setup per aprire il pannello dei driver. Su linux usare il pannello di configurazione di Jack per settare la dimensione del buffer. - If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The - Se non è selezionata alcuna scelta e le possibilità di scelta sono disabilitate, significa che è in uso una dimensione del buffer non supportata dal driver. Il programma + Se non è selezionata alcuna scelta e le possibilità di scelta sono disabilitate, significa che è in uso una dimensione del buffer non supportata dal driver. Il programma - software will still work with this setting but with restricted performance. - funzionerà ma con performance ridotte. + funzionerà ma con performance ridotte. - + 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 a red light in the status indicator (drop outs) and the higher the upload rate and the lower the overall delay. - Il Buffer Delay influenza lo stato della connessione, la velocità di upload e l'OverAll Delay. Usare una dimensione troppo bassa del buffer comporta, maggiore probabilità che l'indicatore di stato diventi rosso (drop outs) consumo di banda in upload e una diminuzione dell'OverAll Delay. + Il Buffer Delay influenza lo stato della connessione, la velocità di upload e l'Overall Delay. Usare una dimensione troppo bassa del buffer comporta, maggiore probabilità che l'indicatore di stato diventi rosso (drop outs) consumo di banda in upload e una diminuzione dell'Overall Delay. - + The buffer setting is therefore a trade-off between audio quality and overall delay. L'impostazione del buffer è quindi un compromesso tra qualità audio e ritardo generale. - If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the - Se le impostazioni del buffer delay sono disabilitate, il driver audio non può modificare questa impostazione tramite + Se le impostazioni del buffer delay sono disabilitate, il driver audio non può modificare questa impostazione tramite - software. 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. - Su Windows premere il bottone ASIo setup per aprire il pannello di settaggio del driver. Su Linux usare il tool di configurazione di Jack per modificare la dimensione del buffer. + Su Windows premere il bottone ASIo setup per aprire il pannello di settaggio del driver. Su Linux usare il tool di configurazione di Jack per modificare la dimensione del buffer. - + 64 samples setting radio button Pulsante per abilitare 64 Campioni - + 128 samples setting radio button Pulsante per abilitare 128 Campioni - + 256 samples setting radio button Pulsante per abilitare 256 Campioni - + ASIO setup push button Pulsante del pannello di setup ASIO - Fancy Skin - Tema Fantaia + Tema Fantaia - If enabled, a fancy skin will be applied to the main window. - Se selezionato questo tema verrà applicato alla finestra principale. + Se selezionato questo tema verrà applicato alla finestra principale. - Fancy skin check box - Check Box Tema Fantasia + Check Box Tema Fantasia - Display Channel Levels - Mostra livelli canali audio + Mostra livelli canali audio - If enabled, each client channel will display a pre-fader level bar. - Se abilitato su ogni client apparirà un metet a LED prima del Fader. + Se abilitato su ogni client apparirà un metet a LED prima del Fader. - Display channel levels check box - Check Box per abilitare la visualizzazione dei livelli dei canali audio + Check Box per abilitare la visualizzazione dei livelli dei canali audio - + Audio Channels Canali Audio - 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 mono-in/stereo-out mode the audio signal which is sent to the server is mono but the return signal is stereo. This is useful if the sound card has 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. - Seleziona il numero dei canali audio che saranno usati. Si possono usare tre modalità. In modalità mono e in modalità stereo vengono usati rispettivamente uno o due canali. Nella modalità mono-in/stereo-out il segnale inviato al server è mono ma il segnale di ritorno è stereo. Questà modalità è utile se si collega lo strumento su di un canale e il microfono nell'altro. In questo modo i due segnali verranno mixati su di un canale mono ma sarà possibile ascoltare il mix ricevuto dal server in stereo. + Seleziona il numero dei canali audio che saranno usati. Si possono usare tre modalità. In modalità mono e in modalità stereo vengono usati rispettivamente uno o due canali. Nella modalità mono-in/stereo-out il segnale inviato al server è mono ma il segnale di ritorno è stereo. Questà modalità è utile se si collega lo strumento su di un canale e il microfono nell'altro. In questo modo i due segnali verranno mixati su di un canale mono ma sarà possibile ascoltare il mix ricevuto dal server in stereo. - 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. - Abilitando lo streaming stereo verrà incrementato l'uso dei dati in upload. Accertarsi di avere velocità in upload sufficiente per lo stream. + Abilitando lo streaming stereo verrà incrementato l'uso dei dati in upload. Accertarsi di avere velocità in upload sufficiente per lo stream. - In 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. - Nel caso in cui si una lo streaming stereo, non sarà possibile selezionare su quale canale far intervenire il reverbero inquanto sarà applicato ad entrambi i canali Left e Right. + Nel caso in cui si una lo streaming stereo, non sarà possibile selezionare su quale canale far intervenire il riverbero inquanto sarà applicato ad entrambi i canali Left e Right. @@ -1086,39 +1406,36 @@ Qualità Audio - 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 upload rate does not exceed the available bandwidth of your internet connection. - Selezionare la qualità audio desiderata. Si può scegliere tra Low (Bassa), normal (standard), high (Alta). Maggiore è la qualità settata più alto sarà il valore di streaming audio. Accertarsi di avere sufficiente banda in upload. + Selezionare la qualità audio desiderata. Si può scegliere tra Low (Bassa), normal (standard), high (Alta). Maggiore è la qualità settata più alto sarà il valore di streaming audio. Accertarsi di avere sufficiente banda in upload. - + Audio quality combo box Combo Box Qualità Audio - + New Client Level Livello Volume Nuovo Client - 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 of that client was already stored. - Settare il livello per il nuovo client definisce il livello, in percentuale, di ingresso per un nuovo utente che si connette. Un nuovo client che si connette alla sessione assume un vulome uguale a quello settato se non ci sono livelli memorizzati per questo client in precedenti connessioni con lo stesso. + Settare il livello per il nuovo client definisce il livello, in percentuale, di ingresso per un nuovo utente che si connette. Un nuovo client che si connette alla sessione assume un volume uguale a quello settato se non ci sono livelli memorizzati per questo client in precedenti connessioni con lo stesso. - + New client level edit box - Box per modificre il livello di ingresso di un nuovo client + Box per modificare il livello di ingresso di un nuovo client - + Custom Central Server Address Indirizzo personalizzato del Server Centrale - The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. This address is only used if the custom server list is selected in the connection dialog. - L'indirizzo personalizzato del server centrale è un indirizzo IP o URL di un server centrale in cui viene gestito l'elenco dei server della finestra di dialogo della connessione. Questo indirizzo viene utilizzato solo se l'elenco dei server personalizzati è selezionato nella finestra di dialogo della connessione. + L'indirizzo personalizzato del server centrale è un indirizzo IP o URL di un server centrale in cui viene gestito l'elenco dei server della finestra di dialogo della connessione. Questo indirizzo viene utilizzato solo se l'elenco dei server personalizzati è selezionato nella finestra di dialogo della connessione. Central Server Address @@ -1133,93 +1450,248 @@ Box per l'indirizzo del Server Centrale - - Central server address line edit - Modifica indirizzo Server Centrale + Central server address line edit + Modifica indirizzo Server Centrale + + + + Current Connection Status Parameter + Parametri attuali di connessione + + + The ping time is the time required for the audio stream to travel from the client to the server and back again. 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. + Il Ping è il tempo richiesto dallo stream audio per essere trasmesso dal client al server e tornare indietro. Questo ritardo è introdotto dalla rete. Questo ritardo dovrebbe non superare i 20-30 ms. Se tale ritardo supera i 50-60 ms significa che la distanza dal server è eccessiva oppure che la connessione non è adeguata. + + + The overall delay is calculated from the current ping time and the delay which is introduced by the current buffer settings. + L'Overall è un valore calcolato in base al ping corrente e al ritardo introdotto dai settaggi del buffer. + + + 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 your internet connection by, e.g., using speedtest.net). + La velocità di trasferimento dati in upload dipende dalla dimensione dei pacchetti audio e dai settaggi di compressione dell'audio. Assicurarsi di non usare valori di upstream non adeguati alla propria connessione (è possibile verificare tali valori mediante un test sulla propria connessione, usando per esempio il sito speedtest.net). + + + + If this LED indicator turns red, you will not have much fun using the + Se questo indicatore a LED diventa rosso non si godrà di un esperienza ottimale del programma + + + + software. + . + + + + ASIO Setup + ASIO Setup + + + + + Mono + Mono + + + + mode will increase your stream's data rate. Make sure your upload rate does not exceed the available upload speed of your internet connection. + modalità che aumenterà la velocità dei dati del tuo stream. Assicurati che la tua velocità di upload non superi la velocità di upload disponibile per la tua connessione Internet. + + + + Mono-in/Stereo-out + Mono-in/Stereo-out + + + + + + Stereo + Stereo + + + + The jitter buffer compensates for network and sound card timing jitters. The size of the buffer therefore influences the quality of the audio stream (how many dropouts occur) and the overall delay (the longer the buffer, the higher the delay). + Il Jitter Buffer compensa i ritardi della rete e della scheda audio. La dimensione del buffer influenza quindi la qualità del flusso audio (quando si verificano i dropout) e il ritardo complessivo (più è alto il buffer, maggiore è il ritardo). + + + + You can set the jitter buffer size manually for the local client and the remote server. For the local jitter buffer, dropouts in the audio stream are indicated by the light below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun has taken place and the audio stream is interrupted. + È possibile impostare manualmente la dimensione delJitter Buffer per il client locale e il server remoto. Per il Jitter Buffer locale, i dropout nel flusso audio sono indicati dalla luce sotto i fader del Jitter Buffer. Se la luce diventa rossa, si è verificato un sovraccarico / underrun del buffer e il flusso audio viene interrotto. + + + + If the Auto setting 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 Auto is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). + Se la modalità "Auto" è abilitata il Jitter Buffer si regolerà automaticamente sulla base di misure sulla rete e sulle latenze della scheda audio. Quando la modalità "Auto" è abilitata i fader saranno disabilitati. + + + + If the Auto setting 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 tweak the audio delay/latency it is recommended to disable the Auto setting and to lower the jitter buffer size manually by using the sliders until your personal acceptable amount of dropouts is reached. The LED indicator will display the audio dropouts of the local jitter buffer with a red light. + Se l'impostazione Auto è abilitata, i buffer di rete del client locale e del server remoto vengono impostati su un valore conservativo per ridurre al minimo la probabilità di interruzione dell'audio. Per modificare il ritardo / latenza audio, si consiglia di disabilitare l'impostazione Auto e di ridurre manualmente la dimensione del buffer utilizzando i fader fino a raggiungere una qualità audio accettabile. L'indicatore LED mostrerà i dropout audio del Jitter Buffer locale con una luce rossa. + + + + The buffer delay setting is a fundamental setting of this software. This setting has an influence on many connection properties. + Il Buffer Delay è un settaggio fondamentale per questo programma. Questo settaggio influenza molte propriètà di connessione. + + + + 64 samples: The preferred setting. Provides the lowest latency but does not work with all sound cards. + 64 Campiono: Settaggio preferito. Permette di ottenere latenze bassissime ma non tutto le schede audio supportano questo valore. + + + + 128 samples: Should work for most available sound cards. + 128 Campioni: Valore accettato dalla maggior parde delle schede audio. + + + + 256 samples: Should only be used on very slow computers or with a slow internet connection. + 256 Campioni: Usato su computer vecchi o su connessioni lente. + + + + Some sound card drivers do not allow the buffer delay to be changed from within the application. In this case the buffer delay setting is disabled and has to be changed using 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. + Alcune driver non permettono il settaggio del buffer delay quando il programma è avviato. In questo caso la scelta del buffer delay è disabilitata è puo essere modificata avviando il software del driver della scheda audio. Su windows cliccare su "ASIO Setup" per aprire i settings del driver ASIO. Su Linux usare la configurazione di Jack per modificare la dimensione del Buffer. + + + + If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The application will still work with this setting but with restricted performance. + Si nessuna delle opzioni di Buffer è selezionata vuol dire che una dimensione non supportata è in uso da parte del driver. Il programma continuerà a funzionare con performance limitate. + + + + If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the software. 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. + Se le impostazioni di ritardo del buffer sono disabilitate, il driver audio non può modificare questa impostazione dal programma. Su Windows, premi il pulsante ASIO Setup per aprire il pannello delle impostazioni del driver. Su Linux, utilizzare lo strumento di configurazione Jack per modificare la dimensione del buffer. + + + + Skin + Vista + + + + Select the skin to be used for the main window. + Selezione la vista da applicare alla finestra principale. + + + + Skin combo box + Box di selezione Vista + + + + Selects the number of audio channels to be used for communication between client and server. There are three modes available: + Seleziona il numero di canali audio da utilizzare per la comunicazione tra client e server. Sono disponibili tre modalità: + + + + and + e + + + + These modes use one and two audio channels respectively. + Questa modalità usa rispettivamente uno o due canali audio. + + + + Mono in/Stereo-out + Mono in/Stereo-out - - Current Connection Status Parameter - Parametri attuali di connessione + + The audio signal sent to the server is mono but the return signal is stereo. This is useful if the sound card has the instrument on one input channel and the microphone on the other. In that case the two input signals can be mixed to one mono channel but the server mix is heard in stereo. + Il segnale audio inviato al server è mono ma il segnale di ritorno è stereo. Ciò è utile se la scheda audio ha lo strumento su un canale di ingresso e il microfono sull'altro. In tal caso, i due segnali di ingresso possono essere miscelati su un canale mono ma il mix del server viene ascoltato in stereo. - - The ping time is the time required for the audio stream to travel from the client to the server and back again. 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. - Il Ping è il tempo richiesto dallo stream audio per essere trasmesso dal client al server e tronare indietro. Questo ritardo è introdotto dalla rete. Questo ritardo dovrebbe non superare i 20-30 ms. Se tale ritardo supera i 50-60 ms significa che la distanza dal server è eccessiva oppure che la connessione non è adeguata. + + Enabling + Abilitando - - The overall delay is calculated from the current ping time and the delay which is introduced by the current buffer settings. - L'OverAll è un valore calcolato dal ping corrente e il ritardo introdotto dai settaggi sul buffer. + + In stereo streaming mode, no audio channel selection for the reverb effect will be available on the main window since the effect is applied to both channels in this case. + Nella modalità stereo, nessuna selezione di canali audio per l'effetto riverbero sarà disponibile nella finestra principale poiché in questo caso l'effetto viene applicato ad entrambi i canali. - - 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 your internet connection by, e.g., using speedtest.net). - La dimensione dei dati in upload dipende dalla dimensione dei pacchetti audio e dai settaggi di compressione dell'audio. Assicurarsi di non usare valori di upstream non adeguati alla propria connessione (è possibile verificare tali valori mediante un test sulla propria connessione, usando per esempio il sito speedtest.net). + + The higher the audio quality, the higher your audio stream's data rate. Make sure your upload rate does not exceed the available bandwidth of your internet connection. + Maggiore è la qualità audio, maggiore è la quantità dei dati del flusso audio. Assicurati che la tua velocità di upload non superi la larghezza di banda disponibile della tua connessione Internet. - - If this LED indicator turns red, you will not have much fun using the - Se questo indicatore a LED diventa rosso non si godra di un esperinza ottimale del programma + + This setting defines the fader level of a newly connected client in percent. If a new client connects to the current server, they will get the specified initial fader level if no other fader level from a previous connection of that client was already stored. + Questa impostazione definisce il livello di dissolvenza di un client appena connesso in percentuale. Se un nuovo client si connette al server corrente, otterrà il livello di fader iniziale specificato se nessun altro livello di fader da una precedente connessione di quel client era già memorizzato. - - software. - . + + Leave this blank unless you need to enter the address of a central server other than the default. + Lasciare vuoto questo campo a meno che non sia necessario immettere l'indirizzo di un server centrale diverso da quello predefinito. - - ASIO Setup - ASIO Setup + + Central server address combo box + Casella dell'indirizzo del server centrale - - Mono - Mono + + The Ping Time is the time required for the audio stream to travel from the client to the server and back again. This delay is introduced by the network and should be about 20-30 ms. If this delay is higher than about 50 ms, your distance to the server is too large or your internet connection is not sufficient. + Il ping è il tempo necessario affinché il flusso audio passi dal client al server e viceversa. Questo ritardo è introdotto dalla rete e dovrebbe essere di circa 20-30 ms. Se questo ritardo è superiore a circa 50 ms, la distanza dal server è eccessiva o la connessione a Internet non è sufficiente. - - Mono-in/Stereo-out - Mono-in/Stereo-out + + Overall Delay is calculated from the current Ping Time and the delay introduced by the current buffer settings. + Il ritardo complessivo viene calcolato dal tempo di ping corrente e dal ritardo introdotto dalle impostazioni del buffer correnti. - - Stereo - Stereo + + Audio Upstream Rate depends on the current audio packet size and compression setting. Make sure that the upstream rate is not higher than your available internet upload speed (check this with a service such as speedtest.net). + L'Upstream audio dipende dalle dimensioni del pacchetto audio e dalle impostazioni di compressione correnti. Assicurati che la velocità di upstream non sia superiore alla velocità della tua connessione (controlla questo con un servizio come speedtest.net). - + Low Low - + + Normal Normal - + High High - + + Fancy + Fantasia + + + + Compact + Compatto + + + preferred consigliato - - + + Size: Livello: - + Buffer Delay Buffer Delay - + Buffer Delay: Buffer Delay: @@ -1228,47 +1700,44 @@ Indirizzo Preferito - The selected audio device could not be used because of the following error: - La scheda audio selezioneta non può essere usata per i seguenti motivi: + La scheda audio selezionata non può essere usata per i seguenti motivi: - The previous driver will be selected. - Sarà ripristinato il driver precedentemente usato. + Sarà ripristinato il driver precedentemente usato. - Ok - Ok + Ok - + Custom Personalizzato - + All Genres Tutti i Generi - + Genre Rock Genere Rock - + Genre Jazz Genere Jazz - - Genre Classical/Folk/Choir + + Genre Classical/Folk/Choral Genere Classica/Folk/Corale - + Default Default @@ -1315,7 +1784,7 @@ Enable Small Network Buffers - Abilita riduzzione dimensione Buffer + Abilita riduzione dimensione Buffer @@ -1353,58 +1822,66 @@ Auto - + Local Locale - + Server Server - - + + Size Livello - + Misc Altri Parametri - + Audio Channels Canali Audio - + Audio Quality Qualità Audio - + New Client Level Livello Nuovo Client - + + Skin + Vista + + + + Language + Lingua + + + % % - Fancy Skin - Tema Fantasia + Tema Fantasia - Display Channel Levels - Visualizza Livelli Canali + Visualizza Livelli Canali - + Custom Central Server Address: Indirizzo Server Centrale alternativo: @@ -1413,125 +1890,140 @@ Indirizzo Server Centrale: - + Audio Stream Rate Velocità dello Streaming - - - + + + val val - + Ping Time Ping - + Overall Delay - OverAll Delay + Overall Delay CConnectDlg - + Server List Lista dei Server - 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. - La lista dei server mostra i server disponibili che si sono registrati sul server centrale. Selezionare un server dalla lista e preme connetti per entrane nella sessione. Altrimenti eseguire un doppio click sul nome del server in lista per connettersi. Se ad un server sono già connessi altri client sarà possibili vedere una lista di musicisti connessi a tale server. I server che sono registrati come permanenti sono visualizzati in grassetto. + La lista dei server mostra i server disponibili che si sono registrati sul server centrale. Selezionare un server dalla lista e preme connetti per entrane nella sessione. Altrimenti eseguire un doppio click sul nome del server in lista per connettersi. Se ad un server sono già connessi altri client sarà possibili vedere una lista di musicisti connessi a tale server. I server che sono registrati come permanenti sono visualizzati in grassetto. - 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. - Si noti che potrebbe essere necessario del tempo per recuperare l'elenco dei server dal server centrale. Se nelle impostazioni non è specificato alcun indirizzo di server centrale valido, non sarà disponibile alcun elenco di server. + Si noti che potrebbe essere necessario del tempo per recuperare l'elenco dei server dal server centrale. Se nelle impostazioni non è specificato alcun indirizzo di server centrale valido, non sarà disponibile alcun elenco di server. - + Server list view - Lista di Server + Lista dei Server - + Server Address Indirizzo del Server - The IP address or URL of the server running the - L'indirizzo IP o l'URL del server in cui è attivo + L'indirizzo IP o l'URL del server in cui è attivo - 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: - come server deve essere settato qui. Una porta di connessione opzionale può essere specificata aggiungendo i due punti ed il numero della porta dopo l'indirizzo. Es. example.org: + come server deve essere settato qui. Una porta di connessione opzionale può essere specificata aggiungendo i due punti ed il numero della porta dopo l'indirizzo. Es. example.org: - . A list of the most recent used server IP addresses or URLs is available for selection. - .Una lista di server recentementi usati è disponibile mediante il menù a tendina. + .Una lista di server recentementi usati è disponibile mediante il menù a tendina. - + + The Connection Setup window shows a list of available servers. Server operators can optionally list their servers by music genre. Use the List dropdown to select a genre, click on the server you want to join and press the Connect button to connect to it. Alternatively, double click on on the server name. Permanent servers (those that have been listed for longer than 48 hours) are shown in bold. + La finestra "Connessioni" mostra un elenco di server disponibili. Gli operatori di server possono facoltativamente elencare i loro server per genere musicale. Utilizzare l'elenco a discesa per selezionare un genere, fare clic sul server a cui si desidera accedere e premere il pulsante Connetti per connettersi ad esso. In alternativa, fai doppio clic sul nome del server. I server permanenti (quelli che sono stati elencati per più di 48 ore) sono mostrati in grassetto. + + + + If you know the IP address or URL of a server, you can connect to it using the Server name/Address field. An optional port number can be added after the IP address or URL using a colon as a separator, e.g, example.org: + Se si conosce l'indirizzo IP o l'URL di un server, è possibile connettersi ad esso utilizzando il campo Nome / indirizzo server. Un numero di porta opzionale può essere aggiunto dopo l'indirizzo IP o l'URL usando i due punti come separatore, ad esempio: esempio.org: + + + + . The field will also show a list of the most recently used server addresses. + . Il campo mostrerà anche un elenco degli indirizzi server utilizzati più di recente. + + + Server address edit box Box di editing dell'indirizzo del server - + Holds the current server IP address or URL. It also stores old URLs in the combo box list. Contiene l'indirizzo IP o l'URL del server corrente. Memorizza anche i vecchi URL visibili aprendo il menù a tendina. - + Server List Selection Lista dei Server selezionabili - + Selects the server list to be shown. Seleziona la lista dei server da visualizzare. - + Server list selection combo box Box di selezione Lista Server - + Filter Filtro - + The server list is filtered by the given text. Note that the filter is case insensitive. - L'elenco dei server è filtrato secondo il testo inserito. Si noti che il filtro non fà distinzione tra maiuscole e minuscole. + L'elenco dei server è filtrato secondo il testo inserito. Si noti che il filtro non fa distinzione tra maiuscole e minuscole. - + Filter edit box Box di modifica del Filtro - + Show All Musicians Visualizza tutti i Musicisti - + 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. - Se questa casella è selezionata saranno visualizzati tutti i musicisti connessi nei vari server. Se non selezionata la lista sarà compattata. + Se questa casella è selezionata, saranno visualizzati tutti i musicisti connessi nei vari server. Se non selezionata la lista sarà compattata. - + Show all musicians check box Box di Visualizzazione dei Musicisti + + + Type # for occupied servers + Inserisci # per i server con persone + CConnectDlgBase @@ -1577,8 +2069,12 @@ + Server Address + Indirizzo del Server + + Server Name/Address - Nome server/Indirizzo + Nome server/Indirizzo @@ -1594,495 +2090,559 @@ CHelpMenu - + &Help &Aiuto - - + + Getting &Started... &Introduzione... - + Software &Manual... &Manuale Software... - + What's &This &Cos'è Questo - + &About... I&nformazioni su... + + CLanguageComboBox + + + Restart Required + Riavvio Richiesto + + + + Please restart the application for the language change to take effect. + Perfavore Riavvia il programma oer rendere effettive le modifiche. + + CLicenceDlg - I &agree to the above licence terms - &Accetto i termini di licenza + &Accetto i termini di licenza - + + This server requires you accept conditions before you can join. Please read these in the chat window. + Il server richiede che tu accetti le condizioni prima di accedere. Leggi il regolamento nella finestra di chat. + + + + I have read the conditions and &agree. + Ho letto ed &accetto. + + + Accept Accetto - + Decline Declino - By connecting to this server and agreeing to this notice, you agree to the following: - Collegandosi a questo server e accettando questo avviso, si accetta quanto segue: + Collegandosi a questo server e accettando questo avviso, si accetta quanto segue: - 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 - Dichiari che tutti i dati, audio o altre opere trasmessi a questo server sono di tua proprietà e creati da te o dai tuoi licenziatari e che rendi questi dati, audio o altre opere disponibili a terzi mediante la seguente Licenza Creative Commons (per ulteriori informazioni su questa licenza, vedere + Dichiari che tutti i dati, audio o altre opere trasmessi a questo server sono di tua proprietà e creati da te o dai tuoi licenziatari e che rendi questi dati, audio o altre opere disponibili a terzi mediante la seguente Licenza Creative Commons (per ulteriori informazioni su questa licenza, vedere - You are free to: - Sei libero di: + Sei libero di: - Share - Condividere + Condividere - copy and redistribute the material in any medium or format - copiare e ridistribuire il materiale in qualsiasi supporto o formato + copiare e ridistribuire il materiale in qualsiasi supporto o formato - Adapt - Adattare + Adattare - remix, transform, and build upon the material - remixare, trasformare e modificare il materiale + remixare, trasformare e modificare il materiale - The licensor cannot revoke these freedoms as long as you follow the license terms. - Il licenziante non può revocare queste libertà fintanto che segui i termini della licenza. + Il licenziante non può revocare queste libertà fintanto che segui i termini della licenza. - Under the following terms: - Sotto i seguenti requisiti: + Sotto i seguenti requisiti: - Attribution - Attribuzione + Attribuzione - You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. - È necessario accreditare in modo appropriato, fornire un collegamento alla licenza e indicare se sono state apportate modifiche. Puoi farlo in modo ragionevole, ma non in modo tale da suggerire a te o al tuo utilizzo il supporto del licenziante. + È necessario accreditare in modo appropriato, fornire un collegamento alla licenza e indicare se sono state apportate modifiche. Puoi farlo in modo ragionevole, ma non in modo tale da suggerire a te o al tuo utilizzo il supporto del licenziante. - NonCommercial - Non Commerciale + Non Commerciale - You may not use the material for commercial purposes. - Non è possibile utilizzare il materiale a fini commerciali. + Non è possibile utilizzare il materiale a fini commerciali. - ShareAlike - Condividere allo stesso modo + Condividere allo stesso modo - If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. - Se remixate, trasformate o sviluppate il materiale, dovete distribuire i vostri contributi con la stessa licenza dell'originale. + Se remixate, trasformate o sviluppate il materiale, dovete distribuire i vostri contributi con la stessa licenza dell'originale. - No additional restrictions - Nessuna restrizione aggiuntiva + Nessuna restrizione aggiuntiva - You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. - Non è possibile applicare termini legali o misure tecnologiche che impediscono legalmente ad altre persone di fare qualsiasi cosa consentita dalla licenza. + Non è possibile applicare termini legali o misure tecnologiche che impediscono legalmente ad altre persone di fare qualsiasi cosa consentita dalla licenza. + + + + CMultiColorLED + + + Red + Rosso + + + + Yellow + Giallo + + + + Green + Verde CMusProfDlg - - + + Musician Profile Profilo del Musicista - + Alias/Name Nome/Alias - + Instrument Strumento - + Country Paese - + City Città - + Skill Livello - + &Close &Chiudi - - - + + + None None - + Beginner Principiante - + Intermediate Intermedio - + Expert Esperto - 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 in. The city you live in and the skill level playing your instrument may also be added. - Scrivi qui il tuo nome o alias in modo che gli altri musicisti con cui vuoi suonare ti riconoscano. Puoi anche aggiungere un'immagine dello strumento che suoni e la bandiera del paese in cui vivi. È inoltre possibile aggiungere la città in cui vivi e il tuo livello di abilità con lo strumento. + Scrivi qui il tuo nome o alias in modo che gli altri musicisti con cui vuoi suonare ti riconoscano. Puoi anche aggiungere un'immagine dello strumento che suoni e la bandiera del paese in cui vivi. È inoltre possibile aggiungere la città in cui vivi e il tuo livello di abilità con lo strumento. - What you set here will appear at your fader on the mixer board when you are connected to a - Quello che inserisci qui apparirà nel tuo fader del mixer quando ti connetti a un server + Quello che inserisci qui apparirà nel tuo fader del mixer quando ti connetti a un server - server. This tag will also show up at each client which is connected to the same server as you. If the name is left empty, the IP address is shown instead. - questo tag verrà mostrato anche a ciascun client connesso allo stesso server. Se viene lasciato vuoto, verrà visualizzato l'indirizzo IP. + questo tag verrà mostrato anche a ciascun client connesso allo stesso server. Se viene lasciato vuoto, verrà visualizzato l'indirizzo IP. + + + + Write your name or an alias here so the other musicians you want to play with know who you are. You may also add a picture of the instrument you play and a flag of the country you are located in. Your city and skill level playing your instrument may also be added. + Scrivi qui il tuo nome o un alias in modo che gli altri musicisti con cui vuoi suonare sappiano chi sei. Puoi anche aggiungere una foto dello strumento che suoni e una bandiera del paese in cui ti trovi. Puoi anche aggiungere la tua città e il tuo livello di abilità nel suonare il tuo strumento. + + + + What you set here will appear at your fader on the mixer board when you are connected to a Jamulus server. This tag will also be shown at each client which is connected to the same server as you. + Ciò che hai impostato apparirà sul tuo fader sulla scheda del mixer quando sei collegato a un server Jamulus. Questo tag verrà mostrato anche su ogni client collegato allo stesso server. - + Alias or name edit box Box di modifica Nome o Alias - + Instrument picture button Immagine dello strumento - + Country flag button Pulsante bandiera del paese - + City edit box Box di modifica Città - + Skill level combo box Livello di Abilità - + Drum Set Batteria - + Djembe Djembe - + Electric Guitar Chitarra elettrica - + Acoustic Guitar Chitarra Acustica - + Bass Guitar Basso Elettrico - + Keyboard Tastiera - + Synthesizer Sintetizzatore - + Grand Piano Grand Piano - + Accordion Fisarmonica - + Vocal Voce - + Microphone Microfono - + Harmonica Armonica - + Trumpet Tromba - + Trombone Trombone - + French Horn Corno Francese - + Tuba Tuba - + Saxophone Sassofono - + Clarinet Clarinet - + Flute Flauto - + Violin Violino - + Cello Cello - + Double Bass Contrabbasso - + Recorder Recorder - + Streamer Streamer - + Listener Ascoltatore - + Guitar+Vocal Chitarra+Voce - + Keyboard+Vocal Tastiera+Voce - + Bodhran Bodhran - + Bassoon Fagotto - + Oboe Oboe - + Harp Arpa - + Viola Viola - + Congas Congas - + Bongo Bongo - + Vocal Bass Voce Basso - + Vocal Tenor Voce Tenore - + Vocal Alto Voce Alto - + Vocal Soprano Voce Soprano - + Banjo Banjo - + Mandolin Mandolino + + + Ukulele + Uculele + + + + Bass Ukulele + Uculele Basso + + + + Vocal Baritone + Voce Baritono + + + + Vocal Lead + Vocal Lead + + + + Mountain Dulcimer + Dulcimer + + + + Scratching + Scratching + + + + Rapping + Rapping + + + + No Name + Senza Nome + CServerDlg - + Client List Lista dei Client - + 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. L'elenco dei client mostra tutti i client attualmente connessi a questo server. Alcune informazioni sui client come l'indirizzo IP e il nome vengono visualizzate per ciascun client collegato. - + Connected clients list view Lista dei Client Connessi - + Start Minimized on Operating System Start Avvia ridotto a icona all'avvio del sistema operativo - If the start minimized on operating system start check box is checked, the - Se è selezionato l'avvio in modalità minimizzata il server + Se è selezionato l'avvio in modalità minimizzata il server - server will be started when the operating system starts up and is automatically minimized to a system task bar icon. - si avvierà all'apertura del sistema operativo e si ridurrà automaticamente a icona sulla barra delle applicazioni. + si avvierà all'apertura del sistema operativo e si ridurrà automaticamente a icona sulla barra delle applicazioni. - Show Creative Commons Licence Dialog - Mostra finestra di dialogo Licenza Creative Commons + Mostra finestra di dialogo Licenza Creative Commons - If enabled, a Creative Commons BY-NC-SA 4.0 Licence dialog is shown each time a new user connects the server. - Se attivato, verrà visualizzata una finestra di dialogo con la licenza BY-NC-SA 4.0 di Creative Commons ogni volta che un nuovo client si connette al server. + Se attivato, verrà visualizzata una finestra di dialogo con la licenza BY-NC-SA 4.0 di Creative Commons ogni volta che un nuovo client si connette al server. - + Make My Server Public Rendi il mio server pubblico - If the Make My Server Public check box is checked, this server registers itself at the central server so that all - Se il Rendi il mio server Pubblico è attivato, questo server si registrerà con il server centrale in modo che tutti gli utenti di + Se il Rendi il mio server Pubblico è attivato, questo server si registrerà con il server centrale in modo che tutti gli utenti di - users can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. - possono vedere il server nell'elenco dei server nella finestra di connessione e possono connettersi ad esso. La registrazione del server viene periodicamente aggiornata per garantire che tutti i server nell'elenco siano effettivamente disponibili. + possono vedere il server nell'elenco dei server nella finestra di connessione e possono connettersi ad esso. La registrazione del server viene periodicamente aggiornata per garantire che tutti i server nell'elenco siano effettivamente disponibili. - + Register Server Status Stato di registrazione del server @@ -2103,165 +2663,387 @@ Box di selezione Server Centrale - + + If the start minimized on operating system start check box is checked, the server will be started when the operating system starts up and is automatically minimized to a system task bar icon. + Se la casella di controllo "Avvia ridotto a icona" all'avvio del sistema operativo è selezionata, il server verrà avviato all'avvio del sistema operativo e verrà automaticamente ridotto a icona a icona nella barra delle attività del sistema. + + + + If the Make My Server Public check box is checked, this server registers itself at the central server so that all users of the application can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. + Se la casella di controllo "Rendi pubblico il mio server" è selezionata, questo server si registra sul server centrale in modo che tutti gli utenti possano vedere il server nell'elenco dei server. La registrazione del server viene rinnovata periodicamente per assicurarsi che tutti i server nell'elenco siano effettivamente disponibili. + + + If the Make My Server Public check box is checked, this will show whether registration with the central server is successful. If the registration failed, please choose another server list. Se la casella di controllo Rendi il mio server pubblico è selezionata, mostrerà se la registrazione con il server centrale è andata a buon fine. Se la registrazione non è riuscita, selezionare un altro elenco di server. - + Custom Central Server Address Indirizzo Server Centrale alternativo - + The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. L'indirizzo del server centrale alternativo è l'indirizzo IP o l'URL del server centrale in cui viene gestito l'elenco dei server della finestra di dialogo della connessione. - + Central server address line edit Modifica Server Centrale - + Server List Selection Lista di selezione ei Server - + Selects the server list (i.e. central server address) in which your server will be added. Seleziona la lista dei server(es. indirizzo central server) nel quale il tuo server sarà aggiunto. - + Server list selection combo box Box di selezione dei server - + Server Name Nome del server - 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. - Il nome del server identifica il tuo server nell'elenco dei server sul client. Se non viene specificato un nome, viene invece visualizzato l'indirizzo IP. + Il nome del server identifica il tuo server nell'elenco dei server sul client. Se non viene specificato un nome, viene invece visualizzato l'indirizzo IP. - + + The server name identifies your server in the connect dialog server list at the clients. + Il nome del server identifica il tuo server nell'elenco dei server nella finestra di dialogo di connessione sui client. + + + Server name line edit Nome del server - + Location City Ubicazione Città - + 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. Qui è possibile specificare la città in cui si trova il server. Se viene inserita una città, verrà visualizzata nell'elenco dei server sul client. - + City where the server is located line edit Citta del Server - + Location country Ubicazione Paese - + 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. Qui è possibile specificare il paese in cui si trova il server. Se viene inserito un paese, verrà visualizzato nell'elenco dei server sul client. - + Country where the server is located combo box Box del Paese dove si trova il server + + + Display dialog to select recording directory button + Visualizza la finestra di dialogo per selezionare il pulsante della directory di registrazione + + + + + Main Recording Directory + Cartella Principale di Registrazione + + + + Click the button to open the dialog that allows the main recording directory to be selected.The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). + Fare clic sul pulsante per aprire la finestra di dialogo che consente di selezionare la directory di registrazione principale. Il Percorso scelto deve esistere ed non essere protetto da scrittura (autorizzare l'utente Jamulus alla creazione di cartelle e sottocartelle). + + + + Main recording directory text box (read-only) + Casella di testo della Cartella principale di registrazione (sola lettura) + + + + The current value of the main recording directory. The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). Click the button to open the dialog that allows the main recording directory to be selected. + Il valore corrente della directory di registrazione principale. Il percorso scelto deve esistere e non essere protetto da scrittura (autorizzare l'utente Jamulus alla creazine di cartelle e sottocartelle). Fare clic sul pulsante per aprire la finestra di dialogo che consente di selezionare la directory di registrazione principale. + - + Clear the recording directory button + Bottone per cancellare il contenuto della cartella di registrazione + + + + Clear Recording Directory + Cancella cartella di Registrazione + + + + Click the button to clear the currently selected recording directory. This will prevent recording until a new value is selected. + Premere il pulsante per eliminare la cartella di registrazione. Ciò impedirà la registrazione fino a quando non viene selezionato un nuovo percorso valido. + + + + Checkbox to turn on or off server recording + Spunta per abilitare o disabilitare la registrazione sul server + + + + Enable Recorder + Abilita Registrazione + + + + Checked when the recorder is enabled, otherwise unchecked. The recorder will run when a session is in progress, if (set up correctly and) enabled. + Se selezionato la registrazione è abilitata. La registrazione verrà eseguito quando è in corso una sessione, se (impostato correttamente e abilitato). + + + + Current session directory text box (read-only) + Casella di testo per la Cartella della sessione Corrente (Sola Lettura) + + + + Current Session Directory + Cartella della sessione Corrente + + + + Enabled during recording and holds the current recording session directory. Disabled after recording or when the recorder is not enabled. + Abilitato durante la registrazione e imposta la directory della sessione di registrazione. Disabilitato dopo la registrazione o quando il registratore non è abilitato. + + + + Recorder status label + Etichetta dello stato di Registrazione + + + + Recorder Status + Stato di Registrazione + + + + Displays the current status of the recorder. The following values are possible: + Visualizza lo stato della registrazione. Sono applicati i seguenti valori: + + + + No recording directory has been set or the value is not useable + Cartella per la registrazione non settata o non utilizzabile + + + + Recording has been switched off + La Registrazione è stata disattivata + + + + by the UI checkbox + dalla Checkbok sull'UI + + + + , either by the UI checkbox or SIGUSR2 being received + , dalla casella di controllo dell'interfaccia utente o dalla ricezione di SIGUSR2 + + + + There is no one connected to the server to record + Non c'è nessuno collegato al server per registrare + + + + The performers are being recorded to the specified session directory + Gli artisti vengono registrati nella directory della sessione specificata + + + + NOTE + NOTE + + + + If the recording directory is not useable, the problem will be displayed in place of the directory. + Se la directory di registrazione non è utilizzabile, il problema verrà visualizzato al posto della directory. + + + + Server welcome message edit box + Casella di modifica del messaggio di benvenuto del server + + + + Server Welcome Message + Messaggio di Benvenuto del Server + + + + A server welcome message text is displayed in the chat window if a musician enters the server. If no message is set, the server welcome is disabled. + Un messaggio di benvenuto del server viene visualizzato nella finestra di chat se un musicista entra nel server. Se non viene impostato alcun messaggio, il benvenuto del server è disabilitato. + + + + Type a message here. If no message is set, the server welcome is disabled. + Digita qui un messaggio. Se non viene impostato alcun messaggio, il benvenuto del server è disabilitato. + + + + software upgrade available + Disponibile Aggiornamento + + + + ERROR + ERRORE + + + Displays the current status of the recorder. + Visualizza lo stato del registratore. + + + + Request new recording button + Pulsante per una nuova registrazione + + + + New Recording + Nuova Registrazione + + + + During a recording session, the button can be used to start a new recording. + Durante una sessione di registrazione questo pulsante può essere usato per iniziare una nuova registrazione. + + + + E&xit &Esci - + &Hide &Nascondi - - - + + + server server - + &Open &Apri - server - server + server - + Server Server - + &Window &Finestra - + + Select Main Recording Directory + Seleziona la directory di registrazione principale + + Predefined Address - Indirizzo Predefinito + Indirizzo Predefinito + + + + Recording + Registrazione + + + + Not recording + Registrazione Ferma - + + Not initialised + Non inizializato + + + + Not enabled + Non Abilitata + + + Unregistered Non registrato - + Bad address Indirizzo Errato - + Registration requested Registrazione richiesta - + Registration failed Registrazione fallita - + Check server version Controlla Versione server - + Registered Registrato - + Central Server full Server Centrale Pieno - + + Your server version is too old + La tua versione del server è troppo vecchia + + + + Requirements not fulfilled + Requisiti non soddisfatti + + + Unknown value Valore sconosciuto @@ -2275,7 +3057,7 @@ - + Name Nome @@ -2285,14 +3067,33 @@ Dimensione Jitter Buffer - + + Server Setup + Settaggi Server + + + + Chat Window Welcome (HTML/CSS Supported) + Finesta Chat Messaggio di Benvenuto (Supporta HTML/CSS) + + + + Options + Opzioni + + + Start Minimized on Windows Start Avvio Minimizzato all'avvio di Windows - Show Creative Commons BY-NC-SA 4.0 Licence Dialog - Visualizza la finestra Creative Commons BY-NC-SA 4.0 Licence + Visualizza la finestra Creative Commons BY-NC-SA 4.0 Licence + + + + Update check + Controlla Aggiornamenti @@ -2300,44 +3101,81 @@ Rendi il server Pubblico (Regista il server nella lista dei server pubblici) - + + Genre + Genere + + + + STATUS STATO - + Custom Central Server Address: Indirizzo server centrale alternativo: + + + Recording Directory + Directory di Registrazione + + + + Enable Jam Recorder + Abilita Registrazione Jam + + + + New Recording + Nuova Registrazione + + + + Language + Lingua + Central Server Address: Indirizzo Server Centrale: - + My Server Info Informazioni sul Server - + Location: City Ubicazione: Città - + Location: Country Ubicazione: Paese - + Enable jam recorder + Abilita Registrazione Jam + + + New recording + Nuova Registrazione + + + Recordings folder + Cartella di Registrazione + + TextLabelNameVersion - TextLabelNameVersion + TextLabelNameVersion CSound - + Error closing stream: $s Errore chiusura dello stream: $s @@ -2378,93 +3216,109 @@ Jack client non può essere attivato. - + The Jack server was shut down. This software requires a Jack server to run. Try to restart the software to solve the issue. Il server Jack non è attivo. Questo software necessita del server Jack per funzionare. Prova a riavviare il software per risolvere questo problema. - + CoreAudio input AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. Ingresso CoreAudio Chiamata AudioHardwareGetProperty non riuscita. Sembra che il sistema non abbia una scheda audio disponibile. - + CoreAudio output AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. Uscita CoreAudio Chiamata AudioHardwareGetProperty non riuscita. Sembra che il sistema non abbia una scheda audio disponibile. - + Current system audio input device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. - La frequenza di campionamento corrente del dispositivo audio in ingresso di% 1 Hz non è supportata. Apri Impostazioni-Audio-MIDI in Applicazioni-> Utilità e prova a impostare una frequenza di campionamento di% 2 Hz. + La frequenza di campionamento corrente del dispositivo audio in ingresso di %1 Hz non è supportata. Apri Impostazioni-Audio-MIDI in Applicazioni-> Utilità e prova a impostare una frequenza di campionamento di %2 Hz. - + + + The current selected audio device is no longer present in the system. + + + + + The audio input device is no longer available. + + + + + The audio output device is no longer available. + + + + Current system audio output device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. - La frequenza di campionamento corrente del dispositivo audio in uscita% 1 Hz non è supportata. Apri Impostazioni-Audio-MIDI in Applicazioni-> Utilità e prova a impostare una frequenza di campionamento di% 2 Hz. + La frequenza di campionamento corrente del dispositivo audio in uscita %1 Hz non è supportata. Apri Impostazioni-Audio-MIDI in Applicazioni-> Utilità e prova a impostare una frequenza di campionamento di %2 Hz. - + The audio input stream format for this audio device is not compatible with this software. Il formato di trasmissione audio in ingresso per questo dispositivo audio non è supportato da questo software. - + The audio output stream format for this audio device is not compatible with this software. Il formato di trasmissione audio in uscita per questo dispositivo audio non è supportato da questo software. - + The buffer sizes of the current input and output audio device cannot be set to a common value. Please choose other input/output audio devices in your system settings. Le dimensioni del buffer dell'attuale dispositivo di input / output audio non possono essere impostate su un valore comune. Seleziona altri dispositivi di input / output audio nelle impostazioni di sistema. - + The audio driver could not be initialized. Il driver audio non può essere inizializzato. - + The audio device does not support the required sample rate. The required sample rate is: Il dispositivo audio non supporta la frequenza di campionamento richiesta. La frequenza di campionamento richiesta è: - + The audio device does not support setting the required sampling rate. This error can happen if you have an audio interface like the Roland UA-25EX where you set the sample rate with a hardware switch on the audio device. If this is the case, please change the sample rate to Il dispositivo audio non consente di impostare la frequenza di campionamento richiesta. Questo errore può verificarsi se si dispone di un dispositivo audio come il Roland UA-25EX in cui è configurato mediante un interruttore fisico sul dispositivo. In tal caso, modificare la frequenza di campionamento a - + Hz on the device and restart the Hz sul dispositivo e riavviare il programma - + software. . - + The audio device does not support the required number of channels. The required number of channels for input and output is: Il dispositivo audio non supporta il numero richiesto di canali. Il numero di canali richiesti è: - - + + Required audio sample format not available. Formato di campionamento audio richiesto non disponibile. - + No ASIO audio device (driver) found. Non è stato trovato un dispositivo ASIO (driver). - + The Il programma - + software requires the low latency audio interface ASIO to work properly. This is not a 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 the ASIO4All driver. richiede che l'interfaccia audio ASIO a bassa latenza funzioni correttamente. Non è un'interfaccia standard di Windows e quindi è necessario un driver audio speciale. La tua scheda audio potrebbe avere un driver ASIO nativo (consigliato) o potresti provare un driver alternativo come ASIO4All. @@ -2472,55 +3326,84 @@ CSoundBase - Invalid device selection. - Device Selezionato non valido. + Device Selezionato non valido. - The audio driver properties have changed to a state which is incompatible with this software. The selected audio device could not be used because of the following error: - I settaggi del driver audio sono stati cambiati con parametri incompatibili con questo programma. La scheda audio selezionata non può essere usata a causa dei seguenti errori: + I settaggi del driver audio sono stati cambiati con parametri incompatibili con questo programma. La scheda audio selezionata non può essere usata a causa dei seguenti errori: - Please restart the software. - Perfavore riavvia il programma. + Perfavore riavvia il programma. - Close - Chiudi + Chiudi + + + + The selected audio device could not be used because of the following error: + La scheda audio selezionata non può essere usata per i seguenti motivi: + + + + The previous driver will be selected. + Sarà ripristinato il driver precedentemente usato. - + + The previously selected audio device is no longer available or the audio driver properties have changed to a state which is incompatible with this software. We now try to find a valid audio device. This new audio device might cause audio feedback. So, before connecting to a server, please check the audio device setting. + + + + No usable Device Non utilizzabile - + audio device (driver) found. driver non trovati. - + In the following there is a list of all available drivers with the associated error message: Di seguito è riportato un elenco di tutti i driver disponibili con errore associato: - + Do you want to open the ASIO driver setups? Vuoi aprire il setup dei Driver Audio ASIO? - + could not be started because of audio interface issues. Impossibile avviare a causa di problemi con il dispositivo audio. + + QCoreApplication + + + , Version + Versione, Versione + + + + Internet Jam Session Software + Programma per Jam Session su Internet + + + + Released under the GNU General Public License (GPL) + Rilasciato sotto licensa GNU General Public License (GPL) + + global - + For more information use the What's This help (help menu, right mouse button or Shift+F1) Per maggiori informazioni usare il comando "Cos'è Questo" (Menù Aiuto, Tasto destro del mouse oppure Shift+F1) diff --git a/src/res/translation/translation_nl_NL.qm b/src/res/translation/translation_nl_NL.qm index f0425ac208..62e4e0f4d1 100644 Binary files a/src/res/translation/translation_nl_NL.qm and b/src/res/translation/translation_nl_NL.qm differ diff --git a/src/res/translation/translation_nl_NL.ts b/src/res/translation/translation_nl_NL.ts index c4810e3eba..fcd98a993e 100644 --- a/src/res/translation/translation_nl_NL.ts +++ b/src/res/translation/translation_nl_NL.ts @@ -4,114 +4,139 @@ CAboutDlg - The - De + De - software enables musicians to perform real-time jam sessions over the internet. - software stelt muzikanten in staat om real-time jamsessies uit te voeren via het internet. + software stelt muzikanten in staat om real-time jamsessies uit te voeren via het internet. - There is a - Er is een + Er is een - server which collects the audio data from each - server die audiodata van elke client verzamelt, + server die audiodata van elke client verzamelt, - client, mixes the audio data and sends the mix back to each client. - deze mixt, en de mix weer terugstuurt naar iedere client. + deze mixt, en de mix weer terugstuurt naar iedere client. - uses the following libraries, resources or code snippets: - gebruikt de volgende libraries, bronnen of code snippets: + gebruikt de volgende libraries, bronnen of code snippets: - + Qt cross-platform application framework Qt cross-platform applicatieframework - + Audio reverberation code by Perry R. Cook and Gary P. Scavone Audio reverberatiecode door Perry R. Cook en Gary P. Scavone - + Some pixmaps are from the Sommige pixmaps zijn van de - Country flag icons from Mark James + Landvlag-iconen van Mark James + + + + This app enables musicians to perform real-time jam sessions over the internet. + Deze applicatie stelt muzikanten in staat om real-time jamsessies uit te voeren via het internet. + + + + There is a server which collects the audio data from each client, mixes the audio data and sends the mix back to each client. + Een server verzamelt de audiodata van elke client, mixt deze en stuurt de mix weer terug naar iedere client. + + + + This app uses the following libraries, resources or code snippets: + Deze applicatie gebruikt de volgende libraries, bronnen of code snippets: + + + + Country flag icons by Mark James Landvlag-iconen van Mark James - + For details on the contributions check out the Voor details over de bijdragen, zie de - + Github Contributors list Github Bijdragerslijst - + Spanish Spaans - + French Frans - + Portuguese Portugees - + Dutch Nederlands - + Italian - + Italiaans - + German Duits - + + Polish + Pools + + + + Swedish + Zweeds + + + + Slovak + Slowaaks + + + About Over - , Version - , Versie + , Versie - Internet Jam Session Software - Internet Jamsessie Software + Internet Jamsessie Software - Under the GNU General Public License (GPL) - Onder de GNU General Public License (GPL) + Onder de GNU General Public License (GPL) @@ -144,12 +169,12 @@ &Contributors - Bijdragers + &Bijdragers &Translation - Vertaling + &Vertaling @@ -160,12 +185,12 @@ CAnalyzerConsole - + Analyzer Console Analyzer Console - + Error Rate of Each Buffer Size Foutpercentage van elke buffergrootte @@ -173,207 +198,297 @@ CAudioMixerBoard - + + Personal Mix at the Server + Eigen mix op de Server + + + + When connected to a server, the controls here allow you to set your local mix without affecting what others hear from you. The title shows the server name and, when known, whether it is actively recording. + Indien verbonden met de server kan hier de lokale mix ingesteld worden zonder dat hetgeen anderen van je horen wordt beïnvloed. De titel toont de servernaam en indien bekend of er audio wordt opgenomen. + + + Server Server - + T R Y I N G T O C O N N E C T A A N H E T V E R B I N D E N - - Personal Mix at the Server: - + + RECORDING ACTIVE + GELUIDSOPNAME ACTIEF + + + + Personal Mix at: + Eigen mix op: CChannelFader - + + Pan - Pan + Pan - - + + Mute Demp - - + + Solo Solo - + + &No grouping + &Geen groepering + + + + + + + Assign to group + Toewijzen aan groep + + + + Grp + Grp + + + Channel Level Kanaalniveau - 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. - Geeft het pre-fader-audioniveau van dit kanaal weer. Alle verbonden clients op de server krijgen een audioniveau toegewezen, dezelfde waarde voor elke client. + Geeft het pre-fader-audioniveau van dit kanaal weer. Alle verbonden clients op de server krijgen een audioniveau toegewezen, dezelfde waarde voor elke client. - + Input level of the current audio channel at the server Invoerniveau van het huidige audiokanaal op de server - + Mixer Fader Mixer Fader - 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. - Past het geluidsniveau van dit kanaal aan. Alle verbonden clients op de server krijgen een audiofader toegewezen bij elke client, waarbij de lokale mix wordt aangepast. + Past het geluidsniveau van dit kanaal aan. Alle verbonden clients op de server krijgen een audiofader toegewezen bij elke client, waarbij de lokale mix wordt aangepast. - + Local mix level setting of the current audio channel at the server Lokale instelling van het mixniveau van het huidige audiokanaal op de server - + Status Indicator - + Statusindicatie - + Shows a status indication about the client which is assigned to this channel. Supported indicators are: - - - - - Speaker with cancellation stroke: Indicates that the other client has muted you. - + Toont de status van de muzikant die aan dit kanaal is toegewezen. Ondersteunde indicaties: - + Status indicator label - + Statusindicatielabel - + Panning - + Panning - - Sets the panning position from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. - - - - + Local panning position of the current audio channel at the server - + Lokale panning-positie van het huidige audiokanaal op de server - + With the Mute checkbox, the audio channel can be muted. Met het selectievakje Demp kan het audiokanaal worden gedempt. - + Mute button Dempknop - 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. - Met het selectievakje Solo kan het audiokanaal worden ingesteld op solo, zodat alle overige kanalen worden gedempt. Het is mogelijk om meer dan één kanaal op solo in te stellen. + Met het selectievakje Solo kan het audiokanaal worden ingesteld op solo, zodat alle overige kanalen worden gedempt. Het is mogelijk om meer dan één kanaal op solo in te stellen. - + Solo button Soloknop - + Fader Tag Fader tag - 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. + De fadertag identificeert de verbonden client. De tagnaam, de afbeelding van uw instrument en een vlag van uw land kunnen in het hoofdvenster worden ingesteld. + + + + Displays the pre-fader audio level of this channel. All clients connected to the server will be assigned an audio level, the same value for every client. + Geeft het pre-fader-audioniveau van dit kanaal weer. Alle clients die verbonden zijn met de server krijgen een audioniveau toegewezen, dezelfde waarde voor elke client. + + + + Adjusts the audio level of this channel. All clients connected to the server will be assigned an audio fader, displayed at each client, to adjust the local mix. + + + + + Speaker with cancellation stroke: Indicates that another client has muted you. + Doorgestreepte luidspreker: Geeft aan dat een andere muzikant je gedempt heeft. + + + + Sets the pan from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. + Legt de panning-positie van links naar rechts vast. Werkt alleen in stereo of bij voorkeur mono in/stereo uit mode. + + + + With the Solo checkbox, the audio channel can be set to solo which means that all other channels except the soloed channel are muted. It is possible to set more than one channel to solo. + Met het selectievakje Solo kan het audiokanaal worden ingesteld op solo, zodat alle overige kanalen worden gedempt. Het is mogelijk om meer dan één kanaal op solo in te stellen. + + + + Group + Groep + + + + With the Grp checkbox, a group of audio channels can be defined. All channel faders in a group are moved in proportional synchronization if any one of the group faders are moved. + Met de Grp checkbox kan een groep van audiokanalen worden gedefinieerd. Alle kanaalfaders in een groep bewegen mee indien een van de faders wordt verschoven. + + + + Group button + Groepknop + + + + The fader tag identifies the connected client. The tag name, a picture of your instrument and the flag of your country can be set in the main window. De fadertag identificeert de verbonden client. De tagnaam, de afbeelding van uw instrument en een vlag van uw land kunnen in het hoofdvenster worden ingesteld. - + Mixer channel instrument picture Afbeelding van het mengkanaalinstrument - + Mixer channel label (fader tag) Label van het mengkanaal (faderlabel) - + Mixer channel country flag Landvlag van het kanaal - + PAN - + PAN - + MUTE DEMP - + SOLO SOLO - + + GRP + GRP + + + + M + M + + + + S + S + + + + G + G + + + Alias/Name Alias/Naam - + Instrument Instrument - + Location Locatie - - - + + + Skill Level Vaardigheidssniveau - + + Alias + Alias + + + Beginner Beginner - + Intermediate Gemiddeld - + Expert Gevorderd - + Musician Profile Muzikantenprofiel @@ -410,66 +525,88 @@ New chat text edit box Nieuw chat tekstbewerkingsvak + + + Type a message here + Typ hier een bericht + + + + &Edit + &Bewerk + + + + Cl&ear Chat History + &Wis Chatgeschiedenis + + + + Do you want to open the link + Wilt u de link openen + + + + in an external browser? + in een externe browser? + CChatDlgBase - + Chat Chat - + + &Send + &Verstuur + + Cl&ear - Wiss&en + Wiss&en - &Close - &Sluiten + &Sluiten CClientDlg - + Input Level Meter Ingangsniveaumeter - The input level indicators show the input level of the two stereo channels of the current selected audio input. - De indicatoren voor het ingangsniveau geven het ingangsniveau van de twee stereokanalen van de huidige geselecteerde audio-ingang weer. + De indicatoren voor het ingangsniveau geven het ingangsniveau van de twee stereokanalen van de huidige geselecteerde audio-ingang weer. - + Make sure not to clip the input signal to avoid distortions of the audio signal. Zorg ervoor dat u het ingangssignaal niet clipt om vervorming van het audiosignaal te voorkomen. - If the - Als de + Als de - software is connected and you play your instrument/sing in the microphone, the LED level 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. - software is verbonden en u speelt of zingt in de microfoon, dan zou de LED-niveaumeter moeten flikkeren. Als dit niet het geval is, heeft u waarschijnlijk het verkeerde ingangskanaal gekozen (bijv. line in i.p.v. de microfooningang) of heeft u de ingangsversterking te laag ingesteld in de (Windows) audiomixer. + software is verbonden en u speelt of zingt in de microfoon, dan zou de LED-niveaumeter moeten flikkeren. Als dit niet het geval is, heeft u waarschijnlijk het verkeerde ingangskanaal gekozen (bijv. line in i.p.v. de microfooningang) of heeft u de ingangsversterking te laag ingesteld in de (Windows) audiomixer. - For a proper usage of the - Voor juist gebruik van de + Voor juist gebruik van de - software, you should not hear your singing/instrument in the loudspeaker or your headphone when the - software, moet uw zang/instrument niet hoorbaar zijn door de luidspreker of uw koptelefoon wanneer de + software, moet uw zang/instrument niet hoorbaar zijn door de luidspreker of uw koptelefoon wanneer de - software is not connected. This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). - software niet is verbonden. Dit kan worden bereikt door het geluidskanaal in de afspeelmixer (niet de opnamemixer!) te dempen. + software niet is verbonden. Dit kan worden bereikt door het geluidskanaal in de afspeelmixer (niet de opnamemixer!) te dempen. @@ -487,225 +624,410 @@ Verbinden/Verbreken-knop - Push this button to connect to a server. A dialog where you can select a server will open. If you are connected, pressing this button will end the session. - Druk op deze knop om verbinding te maken met een server. In het daaropvolgende dialoogvenster kunt u een server kunt selecteren. Als u verbonden bent, wordt de sessie beëindigd door weer op deze knop te drukken. + Druk op deze knop om verbinding te maken met een server. In het daaropvolgende dialoogvenster kunt u een server kunt selecteren. Als u verbonden bent, wordt de sessie beëindigd door weer op deze knop te drukken. - + Connect and disconnect toggle button Knop voor het opzetten en verbreken van de verbinding - Clicking on this button changes the caption of the button from Connect to Disconnect, i.e., it implements a toggle functionality for connecting and disconnecting the - Door op deze knop te klikken verandert het onderschrift van de knop van Verbinden naar Verbreken, d.w.z. dat het een toggle-functie heeft voor verbinden/verbreken van de + Door op deze knop te klikken verandert het onderschrift van de knop van Verbinden naar Verbreken, d.w.z. dat het een toggle-functie heeft voor verbinden/verbreken van de - - software. - software. + software. - + Local Audio Input Fader Lokale audio-ingangsfader - 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 - Met de audiofader kunnen de relatieve niveaus van de linker en rechter lokale audiokanalen worden gewijzigd. Voor een monosignaal werkt het als een panning tussen de twee kanalen. Als bijvoorbeeld een microfoon is verbonden op het rechter ingangskanaal en een veel luider instrument is verbonden op het linker ingangskanaal, beweeg dan de audiofader in de richting: + Met de audiofader kunnen de relatieve niveaus van de linker en rechter lokale audiokanalen worden gewijzigd. Voor een monosignaal werkt het als een panning tussen de twee kanalen. Als bijvoorbeeld een microfoon is verbonden op het rechter ingangskanaal en een veel luider instrument is verbonden op het linker ingangskanaal, beweeg dan de audiofader in de richting: - - + + L L - + , where , waar - + is the current attenuation indicator. is de huidige dempingsindicator. - + Local audio input fader (left/right) Lokale audio-ingangsfader (links/rechts) - Reverberation Level - Niveau van de galm + Niveau van de galm - 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. - Een galmeffect kan worden toegepast op één lokaal mono-audiokanaal of op beide kanalen in de stereomodus. De monokanaalselectie en het galmniveau kunnen worden aangepast. Als bijvoorbeeld het microfoonsignaal in het juiste audiokanaal van de geluidskaart binnenkomt en er een galmeffect wordt toegepast, zet u de kanaalkeuzeschakelaar naar rechts en beweegt u de fader omhoog tot het gewenste galmniveau is bereikt. + Een galmeffect kan worden toegepast op één lokaal mono-audiokanaal of op beide kanalen in de stereomodus. De monokanaalselectie en het galmniveau kunnen worden aangepast. Als bijvoorbeeld het microfoonsignaal in het juiste audiokanaal van de geluidskaart binnenkomt en er een galmeffect wordt toegepast, zet u de kanaalkeuzeschakelaar naar rechts en beweegt u de fader omhoog tot het gewenste galmniveau is bereikt. - The reverberation effect requires significant CPU so 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 switched off and does not cause any additional CPU usage. - Het galmeffect vereist aanzienlijk wat CPU, zodat deze alleen op snelle PC's kan worden gebruikt. Als de fader voor het galmniveau op minimaal is ingesteld (wat de standaardinstelling is), wordt het galmeffect uitgeschakeld en veroorzaakt het geen extra CPU-gebruik. + Het galmeffect vereist aanzienlijk wat CPU, zodat deze alleen op snelle PC's kan worden gebruikt. Als de fader voor het galmniveau op minimaal is ingesteld (wat de standaardinstelling is), wordt het galmeffect uitgeschakeld en veroorzaakt het geen extra CPU-gebruik. - Reverberation effect level setting - Instelling van het niveau van het galmeffect + Instelling van het niveau van het galmeffect - Reverberation Channel Selection - Selectie van het galmkanaal + Selectie van het galmkanaal - 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. - Met deze radioknoppen kan het audio-invoerkanaal worden gekozen waarop het galmeffect wordt toegepast. Het linker of rechter ingangskanaal kan worden gekozen. + Met deze radioknoppen kan het audio-invoerkanaal worden gekozen waarop het galmeffect wordt toegepast. Het linker of rechter ingangskanaal kan worden gekozen. - Left channel selection for reverberation - Linker kanaalselectie voor galm + Linker kanaalselectie voor galm - Right channel selection for reverberation - Rechter kanaalselectie voor galm + Rechter kanaalselectie voor galm - + Delay Status LED Vertragingsstatus LED - 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 large for jamming. - De vertragingsstatus LED-indicator geeft de huidige geluidsvertragingsstatus aan. Als het lampje groen is, is de vertraging perfect voor een storingssessie. Als het lampje geel is, is een sessie nog steeds mogelijk, maar kan het moeilijker zijn om te spelen. Als het lichtje rood is, is de vertraging te groot voor een storing. + De vertragingsstatus LED-indicator geeft de huidige geluidsvertragingsstatus aan. Als het lampje groen is, is de vertraging perfect voor een storingssessie. Als het lampje geel is, is een sessie nog steeds mogelijk, maar kan het moeilijker zijn om te spelen. Als het lichtje rood is, is de vertraging te groot voor een storing. - If this LED indicator turns red, you will not have much fun using the - Als deze LED-indicator rood wordt, zult u niet veel plezier beleven aan het gebruik van de + Als deze LED-indicator rood wordt, zult u niet veel plezier beleven aan het gebruik van de - + Delay status LED indicator Vertragingsstatus LED-indicator - + Buffers Status LED Buffers Status LED - 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: - De indicator voor de status van de buffers geeft de huidige status van de audio/streaming aan. Als het lampje groen is, zijn er geen bufferoverschrijdingen/onderschrijdingen en wordt de audiostream niet onderbroken. Als het lampje rood is, wordt de audiostream onderbroken door een van de volgende problemen: + De indicator voor de status van de buffers geeft de huidige status van de audio/streaming aan. Als het lampje groen is, zijn er geen bufferoverschrijdingen/onderschrijdingen en wordt de audiostream niet onderbroken. Als het lampje rood is, wordt de audiostream onderbroken door een van de volgende problemen: - + The network jitter buffer is not large enough for the current network/audio interface jitter. De buffer voor de netwerkjitter is niet groot genoeg voor de huidige netwerk-/audio-interfacejitter. - The sound card buffer delay (buffer size) is set to too small a value. - De buffer vertraging van de geluidskaart (buffergrootte) is op een te kleine waarde ingesteld. + De buffer vertraging van de geluidskaart (buffergrootte) is op een te kleine waarde ingesteld. - The upload or download stream rate is too high for the current available internet bandwidth. + De upload- of downloadstroomsnelheid is te hoog voor de huidige beschikbare internetbandbreedte. + + + + This shows the level of the two stereo channels for your audio input. + Dit toont het nivo van de twee stereokanalen voor je audio-invoer. + + + + If the application is connected to a server and you play your instrument/sing into the microphone, the VU 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. + Indien de applicatie verbonden is met een server en in de microfoon wordt gespeeld of gezongen, dan zou de LED-niveaumeter moeten flikkeren. Als dit niet het geval is, dan heeft u waarschijnlijk het verkeerde ingangskanaal gekozen (bijv. line in i.p.v. de microfooningang) of heeft u de ingangsversterking te laag ingesteld in de (Windows) audiomixer. + + + + For proper usage of the application, you should not hear your singing/instrument through the loudspeaker or your headphone when the software is not connected.This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). + Voor goed gebruik moet u de zang of het instrument niet via de luidspreker of koptelefoon moeten horen als de software niet verbonden is. Dit kan worden bereikt door het dempen van je audiokanaal in de Playback mixer (niet de opnamemixer!). + + + Clicking on this button changes the caption of the button from Connect to Disconnect, i.e., it implements a toggle functionality for connecting and disconnecting the application. + Door op deze knop te klikken verandert het onderschrift van de knop van Verbinden naar Verbreken, d.w.z. dat het een toggle-functie heeft voor verbinden/verbreken. + + + + Controls the relative levels of the left and right local audio channels. For a mono signal it acts as a pan between the two channels.For example, if 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 + Met de audiofader kunnen de relatieve niveaus van de linker en rechter lokale audiokanalen worden gewijzigd. Voor een monosignaal werkt het als een panning tussen de twee kanalen. Als bijvoorbeeld een microfoon is verbonden op het rechter ingangskanaal en een veel luider instrument is verbonden op het linker ingangskanaal, beweeg dan de audiofader in de richting van het label + + + + Reverb effect + Galm-effect + + + + Reverb can be applied to one local mono audio channel or to both channels in stereo mode. The mono channel selection and the reverb level can be modified. For example, if a microphone signal is fed in to the right audio channel of the sound card and a reverb effect needs to be applied, set the channel selector to right and move the fader upwards until the desired reverb level is reached. + Een galmeffect kan worden toegepast op één lokaal mono-audiokanaal of op beide kanalen in de stereomodus. De monokanaalselectie en het galmniveau kunnen worden aangepast. Als bijvoorbeeld het microfoonsignaal in het juiste audiokanaal van de geluidskaart binnenkomt en er een galmeffect wordt toegepast, zet u de kanaalkeuzeschakelaar naar rechts en beweegt u de fader omhoog tot het gewenste galmniveau is bereikt. + + + + Reverb effect level setting + Instelling van het niveau van het galmeffect + + + + Reverb Channel Selection + Selectie van het galmkanaal + + + + With these radio buttons the audio input channel on which the reverb effect is applied can be chosen. Either the left or right input channel can be selected. + Met deze radioknoppen kan het audio-invoerkanaal worden gekozen waarop het galmeffect wordt toegepast. Het linker of rechter ingangskanaal kan worden gekozen. + + + + Left channel selection for reverb + Linker kanaalselectie voor galm + + + + Right channel selection for reverb + Rechter kanaalselectie voor galm + + + + Green + Groen + + + + The delay is perfect for a jam session. + De vertraging is prima voor een jamsessie. + + + + Yellow + Geel + + + + Red + Rood + + + + Opens a dialog where you can select a server to connect to. If you are connected, pressing this button will end the session. + Laat een dialoog zien waarin je een server kan selecteren om mee te verbinden. Indien reeds verbonden verbreekt deze knop de verbinding. + + + + Shows the current audio delay status: + Toont de huidige geluidsvertragingsstatus: + + + + A session is still possible but it may be harder to play. + Een sessie is nog mogelijk maar zal moeilijk gaan. + + + + The delay is too large for jamming. + De vertraging is te groot voor een jamsessie. + + + + If this LED indicator turns red, you will not have much fun using the application. + Als deze LED-indicator rood wordt, zult u niet veel plezier eraan beleven. + + + + The buffers status LED shows the current audio/streaming status. If the light is red, the audio stream is interrupted. This is caused by one of the following problems: + De bufferstatus-LED toont de huidige audio/streaming status. Indien rood dan wordt de audio-stream onderbroken. Dit kan veroorzaakt worden door de volgende problemen: + + + + The sound card's buffer delay (buffer size) is too small (see Settings window). + De buffer vertraging van de geluidskaart (buffergrootte) is op een te kleine waarde ingesteld. + + + + The upload or download stream rate is too high for your internet bandwidth. De upload- of downloadstroomsnelheid is te hoog voor de huidige beschikbare internetbandbreedte. - + The CPU of the client or server is at 100%. De CPU van de client of server staat op 100%. - + Buffers status LED indicator Status van de buffers LED-indicator - - + + C&onnect C&onnect - + + software upgrade available + software-update beschikbaar + + + + &File + &Bestand + + + &View &Bekijken - + &Connection Setup... &Verbindingsinstellingen... - + My &Profile... Mijn &Profiel... - + C&hat... C&hat... - + &Settings... &Settings... - + &Analyzer Console... &Analyzer Console... - + + N&o User Sorting + Kanalen Niet S&orteren + + + + Sort Users by &City + Sorteer muzikanten op &Stad + + + + Use &Two Rows Mixer Panel + Gebruik &Twee-rijen-mengpaneel + + + + &Clear All Stored Solo and Mute Settings + &Wis Alle Opgeslagen Solo- en Demp-instellingen + + + + Ok + Ok + + + &Clear All Stored Solo Settings + &Wis Alle Opgeslagen Solo-instellingen + + + + Set All Faders to New Client &Level + Zet Alle Faders op Nieuwe-Client-&Niveau + + + E&xit E&xit - + + &Load Mixer Channels Setup... + &Laad Mengkanaalinstellingen... + + + + &Save Mixer Channels Setup... + Mixerkanaalinstellingen &Opslaan... + + + + &Edit + &Bewerken + + + + Sort Users by &Name + Sorteer muzikanten op &Naam + + + + Sort Users by &Instrument + Sorteer muzikanten op &Instrument + + + + Sort Users by &Group + Sorteer muzikanten op &Groep + + None - Geen + Geen - + Center Centrum - + R R - + Central Server - + Centrale Server - + + + Select Channel Setup File + Selecteer bestand met Kanaalinstellingen + + + user gebruiker - + users gebruikers - + + Your sound card is not working correctly. Please open the settings dialog and check the device selection and the driver settings. + Uw geluidskaart werkt niet correct. Open het Instellingsvenster en controleer apparaatselectie en bestuurprogramma-instellingen. + + + D&isconnect &Afmelden @@ -713,367 +1035,357 @@ CClientDlgBase - + Delay Vertraging - + Buffers Buffers - + Input Invoer - + L L - + R R - - Settings - Instellingen + + &Mute Myself + Demp &Mijzelf - - Chat - Chat + + &Settings + In&stellingen - - Mute Myself - Demp Mijzelf + + &Chat + &Chat - + C&onnect &Verbinden - + Pan Pan - + Center Centrum - + Reverb Galm - + Left Links - + Right Rechts + + + MUTED (Other people won't hear you) + GEDEMPT (Anderen horen je niet) + + + + Update check + Update check + + + MUTED (You are not sending any audio to the server) + GEDEMPT (Je stuurt geen audio naar de server) + CClientSettingsDlg - + Jitter Buffer Size Jitter Buffermaat - 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). - De jitterbuffer compenseert voor netwerk- en geluidskaart-timingstoestanden. De grootte van deze jitterbuffer heeft dus invloed op de kwaliteit van de audiostream (hoeveel uitvallers er optreden) en de totale vertraging (hoe langer de buffer, hoe hoger de vertraging). + De jitterbuffer compenseert voor netwerk- en geluidskaart-timingstoestanden. De grootte van deze jitterbuffer heeft dus invloed op de kwaliteit van de audiostream (hoeveel uitvallers er optreden) en de totale vertraging (hoe langer de buffer, hoe hoger de vertraging). - 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 below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun took place and the audio stream is interrupted. - De jitter-buffergrootte kan handmatig worden gekozen voor de lokale client en de externe server. Voor de lokale jitterbuffer worden drop-outs in de audiostream aangegeven door het lampje op de onderkant van de faders voor de jitterbuffergrootte. Als het lampje op rood springt, heeft er een bufferoverschrijding/onderbenedenrijding plaatsgevonden en wordt de audiostream onderbroken. + De jitter-buffergrootte kan handmatig worden gekozen voor de lokale client en de externe server. Voor de lokale jitterbuffer worden drop-outs in de audiostream aangegeven door het lampje op de onderkant van de faders voor de jitterbuffergrootte. Als het lampje op rood springt, heeft er een bufferoverschrijding/onderbenedenrijding plaatsgevonden en wordt de audiostream onderbroken. - + The jitter buffer setting is therefore a trade-off between audio quality and overall delay. De jitterbufferinstelling is dus een afweging tussen geluidskwaliteit en totale vertraging. - 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 Auto check is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). - Een automatische instelling van de jitterbuffergrootte is beschikbaar. Als de controle Auto is ingeschakeld, worden de jitterbuffers van de lokale client en de externe server automatisch ingesteld op basis van metingen van de netwerk- en geluidskaarttimingsjitter. Als de automatische controle is ingeschakeld, zijn de faders voor de jitterbuffergrootte uitgeschakeld (ze kunnen niet met de muis worden verplaatst). + Een automatische instelling van de jitterbuffergrootte is beschikbaar. Als de controle Auto is ingeschakeld, worden de jitterbuffers van de lokale client en de externe server automatisch ingesteld op basis van metingen van de netwerk- en geluidskaarttimingsjitter. Als de automatische controle is ingeschakeld, zijn de faders voor de jitterbuffergrootte uitgeschakeld (ze kunnen niet met de muis worden verplaatst). - If 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 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 with a red light. - In het geval dat de automatische instelling van de jitterbuffer is ingeschakeld, worden de netwerkbuffers van de lokale client en de externe server op een conservatieve waarde gezet om de kans op audio-uitval te minimaliseren. Om de audio delay/latentie te tweaken is het aan te raden om de automatische instelling uit te schakelen en de grootte van de jitterbuffer handmatig te verlagen met behulp van de schuifregelaars totdat de persoonlijke aanvaardbare limiet van het aantal drop-outs is bereikt. De LED-indicator zal de audio dropouts van de lokale jitterbuffer visualiseren met een rood lampje. + In het geval dat de automatische instelling van de jitterbuffer is ingeschakeld, worden de netwerkbuffers van de lokale client en de externe server op een conservatieve waarde gezet om de kans op audio-uitval te minimaliseren. Om de audio delay/latentie te tweaken is het aan te raden om de automatische instelling uit te schakelen en de grootte van de jitterbuffer handmatig te verlagen met behulp van de schuifregelaars totdat de persoonlijke aanvaardbare limiet van het aantal drop-outs is bereikt. De LED-indicator zal de audio dropouts van de lokale jitterbuffer visualiseren met een rood lampje. - + Local jitter buffer slider control Lokale jitter-buffer-schuifregelaar - + Server jitter buffer slider control Server jitter-buffer-schuifregelaar - + Auto jitter buffer switch Automatische jitterbufferschakelaar - + Jitter buffer status LED indicator Jitter-buffer status LED-indicator - + Sound Card Device Geluidskaartapparaat - + The ASIO driver (sound card) can be selected using Het ASIO-stuurprogramma (geluidskaart) kan worden geselecteerd met behulp van - + 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. onder het Windows besturingssysteem. Onder MacOS/Linux is geen geluidskaartkeuze mogelijk. Als het geselecteerde ASIO-stuurprogramma niet geldig is, wordt een foutmelding weergegeven en wordt het vorige geldige stuurprogramma geselecteerd. - + If the driver is selected during an active connection, the connection is stopped, the driver is changed and the connection is started again automatically. Als het stuurprogramma tijdens een actieve verbinding wordt geselecteerd, wordt de verbinding gestopt, wordt het stuurprogramma gewijzigd en wordt de verbinding automatisch opnieuw gestart. - + Sound card device selector combo box Geluidskaart apparaat selector combo box - + If 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. In het geval dat de ASIO4ALL driver wordt gebruikt, dient u er rekening mee te houden dat deze driver meestal ongeveer 10-30 ms extra geluidsvertraging introduceert. Het gebruik van een geluidskaart met een native ASIO-driver wordt daarom aanbevolen. - + If you are using the kX ASIO driver, make sure to connect the ASIO inputs in the kX DSP settings panel. Als u het kX ASIO-stuurprogramma gebruikt, zorg er dan voor dat u de ASIO-ingangen in het kX DSP-instellingenpaneel aansluit. - + Sound Card Channel Mapping Geluidskaartkanalen in kaart brengen - + If the selected sound card device offers more than one input or output channel, the Input Channel Mapping and Output Channel Mapping settings are visible. In het geval dat het geselecteerde geluidskaartapparaat meer dan één ingangs- of uitgangskanaal biedt, zijn de instellingen voor het ingangs- en uitgangskanaal in kaart brengen zichtbaar. - + For each - Voor elk + Voor elke - + input/output channel (Left and Right channel) a different actual sound card channel can be selected. Invoer-/uitvoerkanaal (linker- en rechterkanaal) kan een ander daadwerkelijk kanaal van de geluidskaart worden geselecteerd. - + Left input channel selection combo box Linker ingangskanaal selectie combo box - + Right input channel selection combo box Juiste ingangskanaal selectie combo box - + Left output channel selection combo box Linkeruitgangskanaal selectie combobox - + Right output channel selection combo box Rechter uitgangskanaal selectie combo box - + Enable Small Network Buffers Kleine netwerkbuffers inschakelen - + 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 - Indien ingeschakeld, wordt de ondersteuning voor zeer kleine netwerkaudiopakketten geactiveerd. Zeer kleine netwerkpakketten worden alleen daadwerkelijk gebruikt als de buffervertraging van de geluidskaart kleiner is dan + Indien ingeschakeld, wordt de ondersteuning voor zeer kleine netwerkaudiopakketten geactiveerd. Zeer kleine netwerkpakketten worden alleen daadwerkelijk gebruikt als de buffervertraging van de geluidskaart kleiner is dan - + samples. The smaller the network buffers, the lower the audio latency. But at the same time the network load increases and the probability of audio dropouts also increases. monsters. Hoe kleiner de netwerkbuffers, hoe kleiner de audiolatentie. Maar tegelijkertijd neemt de netwerkbelasting toe en neemt ook de kans op audio-uitval toe. - + Enable small network buffers check box Schakel het selectievakje kleine netwerkbuffers in - + Sound Card Buffer Delay Geluidskaartbuffervertraging - The buffer delay setting is a fundamental setting of the - De instelling van de buffervertraging is een belangrijke instelling van de + De instelling van de buffervertraging is een belangrijke instelling van de - software. This setting has influence on many connection properties. - software. Deze instelling heeft invloed op veel verbindingseigenschappen. + software. Deze instelling heeft invloed op veel verbindingseigenschappen. - + Three buffer sizes are supported Drie buffermaten worden ondersteund - 64 samples: This is the preferred setting since it provides the lowest latency but does not work with all sound cards. - 64 monsters: Dit is de voorkeursinstelling omdat het de laagste latentie geeft, maar niet met alle geluidskaarten werkt. + 64 monsters: Dit is de voorkeursinstelling omdat het de laagste latentie geeft, maar niet met alle geluidskaarten werkt. - 128 samples: This setting should work for most available sound cards. - 128 monsters: Deze instelling zou moeten werken op de meeste beschikbare geluidskaarten. + 128 monsters: Deze instelling zou moeten werken op de meeste beschikbare geluidskaarten. - 256 samples: This setting should only be used if only a very slow computer or a slow internet connection is available. - 256 monsters: Deze instelling mag alleen worden gebruikt als er alleen een zeer langzame computer of een langzame internetverbinding beschikbaar is. + 256 monsters: Deze instelling mag alleen worden gebruikt als er alleen een zeer langzame computer of een langzame internetverbinding beschikbaar is. - Some sound card drivers do not allow the buffer delay to be changed from within the - Sommige geluidskaartbestuurders staan niet toe dat de buffervertraging wordt gewijzigd van binnenuit de + Sommige geluidskaartbestuurders staan niet toe dat de buffervertraging wordt gewijzigd van binnenuit de - 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. - software. In dit geval is de instelling van de buffervertraging uitgeschakeld. Om de eigenlijke buffervertraging te wijzigen, moet deze instelling in het stuurprogramma van de geluidskaart worden gewijzigd. Druk in Windows op de knop ASIO Setup om het instellingenpaneel van het stuurprogramma te openen. Op Linux gebruikt u de Jack-configuratietool om de grootte van de buffer te wijzigen. + software. In dit geval is de instelling van de buffervertraging uitgeschakeld. Om de eigenlijke buffervertraging te wijzigen, moet deze instelling in het stuurprogramma van de geluidskaart worden gewijzigd. Druk in Windows op de knop ASIO Setup om het instellingenpaneel van het stuurprogramma te openen. Op Linux gebruikt u de Jack-configuratietool om de grootte van de buffer te wijzigen. - If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The - Als er geen buffergrootte is geselecteerd en alle instellingen zijn uitgeschakeld, wordt een niet-ondersteunde buffergrootte gebruikt door het stuurprogramma. De + Als er geen buffergrootte is geselecteerd en alle instellingen zijn uitgeschakeld, wordt een niet-ondersteunde buffergrootte gebruikt door het stuurprogramma. De - software will still work with this setting but with restricted performance. - software zal nog steeds werken met deze instelling, maar met beperkte prestaties. + software zal nog steeds werken met deze instelling, maar met beperkte prestaties. - + 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 a red light in the status indicator (drop outs) and the higher the upload rate and the lower the overall delay. De werkelijke buffervertraging heeft invloed op de verbindingsstatus, de huidige uploadsnelheid en de totale vertraging. Hoe lager de buffergrootte, hoe hoger de kans op rood licht in de statusindicator (drop outs) en hoe hoger de uploadsnelheid en hoe lager de totale vertraging. - + The buffer setting is therefore a trade-off between audio quality and overall delay. De bufferinstelling is dus een afweging tussen de geluidskwaliteit en de totale vertraging. - If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the - Als de instellingen voor de buffervertraging zijn uitgeschakeld, is het door het audiostuurprogramma verboden om deze instelling te wijzigen vanuit de + Als de instellingen voor de buffervertraging zijn uitgeschakeld, is het door het audiostuurprogramma verboden om deze instelling te wijzigen vanuit de - software. 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. - software. Druk in Windows op de knop ASIO Setup om het instellingenpaneel van het stuurprogramma te openen. Op Linux gebruikt u de Jack-configuratietool om de grootte van de buffer te wijzigen. + software. Druk in Windows op de knop ASIO Setup om het instellingenpaneel van het stuurprogramma te openen. Op Linux gebruikt u de Jack-configuratietool om de grootte van de buffer te wijzigen. - + 64 samples setting radio button 64 monsters instellen radioknop - + 128 samples setting radio button 128 voorbeelden van het instellen van de radioknop - + 256 samples setting radio button 256 voorbeelden van het instellen van het radioknopje - + ASIO setup push button ASIO-instellingsdrukknop - Fancy Skin - Edele huid + Edele huid - If enabled, a fancy skin will be applied to the main window. - Indien ingeschakeld wordt er een fancy skin op het hoofdvenster aangebracht. + Indien ingeschakeld wordt er een fancy skin op het hoofdvenster aangebracht. - Fancy skin check box - Fancy skin check box + Fancy skin check box - Display Channel Levels - Weergave Kanaalniveaus + Weergave Kanaalniveaus - If enabled, each client channel will display a pre-fader level bar. - Indien ingeschakeld, zal elk clientkanaal een pre-fader niveau balk weergeven. + Indien ingeschakeld, zal elk clientkanaal een pre-fader niveau balk weergeven. - Display channel levels check box - Vinkje bij de weergave van de kanaalniveaus + Vinkje bij de weergave van de kanaalniveaus - + Audio Channels Audiokanalen - 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 mono-in/stereo-out mode the audio signal which is sent to the server is mono but the return signal is stereo. This is useful if the sound card has 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. - Selecteer het aantal te gebruiken audiokanalen. Er zijn drie modi beschikbaar. De mono- en stereomodus gebruiken respectievelijk één en twee audiokanalen. In de mono-in/stereo-uit modus is het audiosignaal dat naar de server wordt gestuurd mono, maar het retoursignaal is stereo. Dit is handig voor het geval dat de geluidskaart het instrument op het ene ingangskanaal zet en de microfoon op het andere kanaal. In dat geval kunnen de twee ingangssignalen worden gemixt naar één monokanaal, maar de servermix is in stereo te horen. + Selecteer het aantal te gebruiken audiokanalen. Er zijn drie modi beschikbaar. De mono- en stereomodus gebruiken respectievelijk één en twee audiokanalen. In de mono-in/stereo-uit modus is het audiosignaal dat naar de server wordt gestuurd mono, maar het retoursignaal is stereo. Dit is handig voor het geval dat de geluidskaart het instrument op het ene ingangskanaal zet en de microfoon op het andere kanaal. In dat geval kunnen de twee ingangssignalen worden gemixt naar één monokanaal, maar de servermix is in stereo te horen. - 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. - Het inschakelen van de stereo-streaming modus zal de stream-datasnelheid verhogen. Zorg ervoor dat de huidige uploadsnelheid niet hoger is dan de beschikbare bandbreedte van uw internetverbinding. + Het inschakelen van de stereo-streaming modus zal de stream-datasnelheid verhogen. Zorg ervoor dat de huidige uploadsnelheid niet hoger is dan de beschikbare bandbreedte van uw internetverbinding. - In 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. - In het geval van de stereo streaming-mode is er geen audiokanaalselectie voor het galmeffect beschikbaar op het hoofdvenster, aangezien het effect in dit geval op beide kanalen wordt toegepast. + In het geval van de stereo streaming-mode is er geen audiokanaalselectie voor het galmeffect beschikbaar op het hoofdvenster, aangezien het effect in dit geval op beide kanalen wordt toegepast. @@ -1086,39 +1398,32 @@ Audiokwaliteit - 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 upload rate does not exceed the available bandwidth of your internet connection. - Selecteer de gewenste audiokwaliteit. Er kan een lage, normale of hoge audiokwaliteit worden geselecteerd. Hoe hoger de audiokwaliteit, hoe meer audiodata moet worden verstuurd. Zorg ervoor dat de vereiste bandbreedte niet hoger is dan de beschikbare bandbreedte van uw internetverbinding. + Selecteer de gewenste audiokwaliteit. Er kan een lage, normale of hoge audiokwaliteit worden geselecteerd. Hoe hoger de audiokwaliteit, hoe meer audiodata moet worden verstuurd. Zorg ervoor dat de vereiste bandbreedte niet hoger is dan de beschikbare bandbreedte van uw internetverbinding. - + Audio quality combo box Audiokwaliteit combo-box - + New Client Level Nieuw clientniveau - 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 of that client was already stored. - De nieuwe instelling van het clientniveau definieert het faderniveau van een nieuwe verbonden client in procenten. D.w.z. als een nieuwe client verbinding maakt met de server, krijgt hij het opgegeven initiële faderniveau als er in de vorige verbinding niets is opgeslagen. + De nieuwe instelling van het clientniveau definieert het faderniveau van een nieuwe verbonden client in procenten. D.w.z. als een nieuwe client verbinding maakt met de server, krijgt hij het opgegeven initiële faderniveau als er in de vorige verbinding niets is opgeslagen. - + New client level edit box Nieuw bewerkingsvak op clientniveau - + Custom Central Server Address - - - - - The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. This address is only used if the custom server list is selected in the connection dialog. - + Eigen centrale serveradres Central Server Address @@ -1133,106 +1438,261 @@ Centraal serveradrestype combo box - Central server address line edit - Centraal serveradres bewerking van de lijn + Centraal serveradres bewerking van de lijn - + Current Connection Status Parameter Huidige verbindingsstatus-parameter - The ping time is the time required for the audio stream to travel from the client to the server and back again. 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. - De ping-tijd is de tijd die nodig is voor de audiostream om van de client naar de server en terug te reizen. Deze vertraging wordt geïntroduceerd door het netwerk. Deze vertraging moet zo laag zijn als 20-30 ms. Als deze vertraging hoger is (bijv. 50-60 ms), is uw afstand tot de server te groot of is uw internetverbinding niet toereikend. + De ping-tijd is de tijd die nodig is voor de audiostream om van de client naar de server en terug te reizen. Deze vertraging wordt geïntroduceerd door het netwerk. Deze vertraging moet zo laag zijn als 20-30 ms. Als deze vertraging hoger is (bijv. 50-60 ms), is uw afstand tot de server te groot of is uw internetverbinding niet toereikend. - The overall delay is calculated from the current ping time and the delay which is introduced by the current buffer settings. - De totale vertraging wordt berekend op basis van de huidige ping-tijd en de vertraging die door de huidige bufferinstellingen wordt veroorzaakt. + De totale vertraging wordt berekend op basis van de huidige ping-tijd en de vertraging die door de huidige bufferinstellingen wordt veroorzaakt. - 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 your internet connection by, e.g., using speedtest.net). - De upstreamsnelheid is afhankelijk van de huidige grootte van het audiopakket en de instelling van de audiocompressie. Zorg ervoor dat de upstreamsnelheid niet hoger is dan de beschikbare snelheid (controleer de upstreammogelijkheden van uw internetverbinding door bijvoorbeeld speedtest.net te gebruiken). + De upstreamsnelheid is afhankelijk van de huidige grootte van het audiopakket en de instelling van de audiocompressie. Zorg ervoor dat de upstreamsnelheid niet hoger is dan de beschikbare snelheid (controleer de upstreammogelijkheden van uw internetverbinding door bijvoorbeeld speedtest.net te gebruiken). - + If this LED indicator turns red, you will not have much fun using the Als deze LED-indicator rood wordt, zult u niet veel plezier beleven aan het gebruik van de - + software. software. - - ASIO Setup - ASIO-instelling + + ASIO Setup + ASIO-instelling + + + + + Mono + Mono + + + + mode will increase your stream's data rate. Make sure your upload rate does not exceed the available upload speed of your internet connection. + modus verhoogt de bandbreedte van de audiostream. Zorg ervoor dat deze niet hoger staat dan de beschikbare bandbreedte van uw internetverbinding. + + + + Mono-in/Stereo-out + Mono-in/Stereo-out + + + + + + Stereo + Stereo + + + + The jitter buffer compensates for network and sound card timing jitters. The size of the buffer therefore influences the quality of the audio stream (how many dropouts occur) and the overall delay (the longer the buffer, the higher the delay). + De jitterbuffer compenseert voor jitters als gevolg van netwerk- en geluidskaart timing. De grootte van de jitterbuffer beïnvloed de kwaliteit van de audiostream (aantal dropouts) en de totale vertraging (hoe groter de buffer, des te groter de vertraging). + + + + You can set the jitter buffer size manually for the local client and the remote server. For the local jitter buffer, dropouts in the audio stream are indicated by the light below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun has taken place and the audio stream is interrupted. + De jitter-buffergrootte kan handmatig worden gekozen voor de client en de server. Voor de lokale jitterbuffer worden drop-outs in de audiostream aangegeven door het lampje op de onderkant van de faders voor de jitterbuffergrootte. Als het lampje op rood springt, heeft er een bufferoverschrijding/onderbenedenrijding plaatsgevonden en wordt de audiostream onderbroken. + + + + If the Auto setting 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 Auto is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). + Als de Auto-instelling aanstaat worden de jitterbuffers lokaal en op de server automatisch aangepast op basis van metingen van het netwerk en de geluidskaart. Indien ingeschakeld kunnen de faders van de jitterbuffer niet handmatig worden bewogen. + + + + If the Auto setting 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 tweak the audio delay/latency it is recommended to disable the Auto setting and to lower the jitter buffer size manually by using the sliders until your personal acceptable amount of dropouts is reached. The LED indicator will display the audio dropouts of the local jitter buffer with a red light. + In het geval dat de automatische instelling van de jitterbuffer is ingeschakeld, worden de netwerkbuffers van de lokale client en de externe server op een conservatieve waarde gezet om de kans op audio-uitval te minimaliseren. Om de audio delay/latentie te tweaken is het aan te raden om de automatische instelling uit te schakelen en de grootte van de jitterbuffer handmatig te verlagen met behulp van de schuifregelaars totdat de persoonlijke aanvaardbare limiet van het aantal drop-outs is bereikt. De LED-indicator zal de audio dropouts van de lokale jitterbuffer visualiseren met een rood lampje. + + + + The buffer delay setting is a fundamental setting of this software. This setting has an influence on many connection properties. + De buffervertraging is een fundamentele instelling van dit programma. Deze instelling beïnvloed vele eigenschappen van de verbinding. + + + + 64 samples: The preferred setting. Provides the lowest latency but does not work with all sound cards. + 64 samples: Voorkeursinstelling. Geeft de kleinste vertraging maar werkt niet met alle geluidskaarten. + + + + 128 samples: Should work for most available sound cards. + 128 samples: Werkt voor de meeste geluidskaarten. + + + + 256 samples: Should only be used on very slow computers or with a slow internet connection. + 256 samples: Alleen te gebruiken bij langzame computers of met een langzame internetverbinding. + + + + Some sound card drivers do not allow the buffer delay to be changed from within the application. In this case the buffer delay setting is disabled and has to be changed using 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. + Sommige stuurprogramma's van geluidskaarten laten het niet toe de buffervertraging in het programma aan te passen. In dat geval dient het te worden aangepast bij het stuurprogramma zelf. Bij Windows, selecteer de ASIO Setup knop om dit in te stellen. Op Linux, gebruik de Jack configuration tool om de buffergrootte te veranderen. + + + + If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The application will still work with this setting but with restricted performance. + Indien geen buffergrootte is aangegeven en instellingen zijn uitgeschakeld, dan gebruikt het stuurprogramma een niet-ondersteunde buffergrootte. Het programma zal niet optimaal presteren. + + + + If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the software. 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. + Sommige stuurprogramma's van geluidskaarten laten het niet toe de buffervertraging in het programma aan te passen. In dat geval dient het te worden aangepast bij het stuurprogramma zelf. Bij Windows, selecteer de ASIO Setup knop om dit in te stellen. Op Linux, gebruik de Jack configuration tool om de buffergrootte te veranderen. + + + + Skin + Skin + + + + Select the skin to be used for the main window. + Selecteer de te gebruiken skin voor het hoofdvenster. + + + + Skin combo box + Skin combobox + + + + Selects the number of audio channels to be used for communication between client and server. There are three modes available: + Selecteer het aantal audiokanalen voor communicatie tussen client en server. Er zijn drie modi beschikbaar: + + + + and + en + + + + These modes use one and two audio channels respectively. + Deze modi gebruiken respectievelijk een en twee audiokanalen. + + + + Mono in/Stereo-out + Mono in/Stereo uit + + + + The audio signal sent to the server is mono but the return signal is stereo. This is useful if the sound card has the instrument on one input channel and the microphone on the other. In that case the two input signals can be mixed to one mono channel but the server mix is heard in stereo. + Het audiosignaal naar de server is mono maar wat terugkomt is stereo. Dit is handig als de gelsuidskaart het instrument op een invoerkanaal heeft en de microfoon op een ander. In dat geval kunnen de twee signalen gemixed worden naar een monokanaal terwijl de server mix in stereo blijft. + + + + Enabling + Aanzetten + + + + In stereo streaming mode, no audio channel selection for the reverb effect will be available on the main window since the effect is applied to both channels in this case. + In het geval van de stereo streaming-mode is er geen audiokanaalselectie voor het galmeffect beschikbaar op het hoofdvenster, aangezien het effect in dit geval op beide kanalen wordt toegepast. + + + + The higher the audio quality, the higher your audio stream's data rate. Make sure your upload rate does not exceed the available bandwidth of your internet connection. + Hoe hoger de audiokwaliteit, des te hoger de benodigde bandbreedte. Zorg ervoor dat deze niet hoger staat dan de beschikbare bandbreedte van uw internetverbinding. + + + + This setting defines the fader level of a newly connected client in percent. If a new client connects to the current server, they will get the specified initial fader level if no other fader level from a previous connection of that client was already stored. + Deze instelling stelt het faderniveau in van een nieuwe verbonden client in procenten. Als een nieuwe client verbinding maakt krijgt hij het opgegeven initiële faderniveau als dit in de vorige verbinding niet was opgeslagen. + + + + Leave this blank unless you need to enter the address of a central server other than the default. + Laat dit leeg tenzij u een centrale serveradres wilt invoeren dat anders is dan de standaard. + + + + Central server address combo box + Centrale serveradres combobox - - Mono - Mono + + The Ping Time is the time required for the audio stream to travel from the client to the server and back again. This delay is introduced by the network and should be about 20-30 ms. If this delay is higher than about 50 ms, your distance to the server is too large or your internet connection is not sufficient. + De ping-tijd is de tijd die nodig is voor de audiostream om van de client naar de server en terug te reizen. Deze vertraging wordt veroorzaakt door het netwerk en bedraagt ongeveer 20-30 ms. Als deze vertraging hoger is dan circa 50 ms, dan is uw afstand tot de server te groot of is uw internetverbinding niet toereikend. - - Mono-in/Stereo-out - Mono-in/Stereo-out + + Overall Delay is calculated from the current Ping Time and the delay introduced by the current buffer settings. + De totale vertraging wordt berekend op basis van de huidige ping-tijd en de vertraging door de huidige bufferinstellingen. - - Stereo - Stereo + + Audio Upstream Rate depends on the current audio packet size and compression setting. Make sure that the upstream rate is not higher than your available internet upload speed (check this with a service such as speedtest.net). + De upstreamsnelheid is afhankelijk van de huidige grootte van het audiopakket en de instelling van de audiocompressie. Zorg ervoor dat de upstreamsnelheid niet hoger is dan de beschikbare snelheid (controleer de upstreammogelijkheden van uw internetverbinding door bijvoorbeeld speedtest.net te gebruiken). - + Low Laag - + + Normal Normaal - + High Hoog + + + Fancy + Fancy + + + + Compact + Compact + Manual Handmatig - + Custom - + Aangepast - + All Genres - + Alle genres - + Genre Rock - + Genre Rock - + Genre Jazz - + Genre Jazz - - Genre Classical/Folk/Choir - + + Genre Classical/Folk/Choral + Genre Klassiek/Folk/Zang - + Default Standaard @@ -1241,40 +1701,37 @@ Standaard (Noord-Amerika) - + preferred gewenst - - + + Size: Size: - + Buffer Delay Buffervertraging - + Buffer Delay: Buffervertraging: - The selected audio device could not be used because of the following error: - Het geselecteerde audioapparaat kon niet worden gebruikt vanwege de volgende fout: + Het geselecteerde audioapparaat kon niet worden gebruikt vanwege de volgende fout: - The previous driver will be selected. - Het vorige stuurprogramma wordt geselecteerd. + Het vorige stuurprogramma wordt geselecteerd. - Ok - Ok + Ok @@ -1357,84 +1814,92 @@ Auto - + Local Lokaal - + Server Server - - + + Size Size - + Misc Overige - + Audio Channels Audiokanalen - + Audio Quality Audiokwaliteit - + New Client Level Nieuw client-niveau - + + Skin + Skin + + + + Language + Taal + + + % % - Fancy Skin - Fancy Skin + Fancy Skin - Display Channel Levels - Weergave Kanaalniveaus + Weergave Kanaalniveaus - + Custom Central Server Address: - + Eigen centrale serveradres: Central Server Address: Centraal Serveradres: - + Audio Stream Rate Audio Stream Rate - - - + + + val val - + Ping Time Ping-tijd - + Overall Delay Algehele vertraging @@ -1442,100 +1907,115 @@ CConnectDlg - + Server List Serverlijst - 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. - De serverlijst toont een lijst van beschikbare servers die op de centrale server zijn geregistreerd. Selecteer een server uit de lijst en druk op de verbindingsknop om verbinding te maken met deze server. U kunt ook dubbelklikken op een server uit de lijst om verbinding te maken met deze server. Als een server bezet is, is een lijst van de verbonden muzikanten beschikbaar door het lijstitem uit te breiden. Permanente servers worden vetgedrukt weergegeven. + De serverlijst toont een lijst van beschikbare servers die op de centrale server zijn geregistreerd. Selecteer een server uit de lijst en druk op de verbindingsknop om verbinding te maken met deze server. U kunt ook dubbelklikken op een server uit de lijst om verbinding te maken met deze server. Als een server bezet is, is een lijst van de verbonden muzikanten beschikbaar door het lijstitem uit te breiden. Permanente servers worden vetgedrukt weergegeven. - 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. - Merk op dat het enige tijd kan duren om de serverlijst op te halen van de centrale server. Als er geen geldig centraal serveradres is opgegeven in de instellingen, zal er geen serverlijst beschikbaar zijn. + Merk op dat het enige tijd kan duren om de serverlijst op te halen van de centrale server. Als er geen geldig centraal serveradres is opgegeven in de instellingen, zal er geen serverlijst beschikbaar zijn. - + Server list view Serverlijstweergave - + Server Address Serveradres - The IP address or URL of the server running the - Het IP-adres of de URL van de server waarop de + Het IP-adres of de URL van de server waarop de - 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: - serversoftware moet worden ingesteld. Een optioneel poortnummer kan worden toegevoegd na het IP-adres of de URL met een dubbele punt als scheidingsteken, bijvoorbeeld example.org: + serversoftware moet worden ingesteld. Een optioneel poortnummer kan worden toegevoegd na het IP-adres of de URL met een dubbele punt als scheidingsteken, bijvoorbeeld example.org: - . A list of the most recent used server IP addresses or URLs is available for selection. - . Een lijst met de meest recent gebruikte server-IP-adressen of URL's is beschikbaar voor selectie. + . Een lijst met de meest recent gebruikte server-IP-adressen of URL's is beschikbaar voor selectie. + + + + The Connection Setup window shows a list of available servers. Server operators can optionally list their servers by music genre. Use the List dropdown to select a genre, click on the server you want to join and press the Connect button to connect to it. Alternatively, double click on on the server name. Permanent servers (those that have been listed for longer than 48 hours) are shown in bold. + De serverlijst toont een lijst van beschikbare servers die op de centrale server zijn geregistreerd. Selecteer een server uit de lijst en druk op de verbindingsknop om verbinding te maken met deze server. U kunt ook dubbelklikken op een server uit de lijst om verbinding te maken met deze server. Als een server bezet is, is een lijst van de verbonden muzikanten beschikbaar door het lijstitem uit te breiden. Permanente servers worden vetgedrukt weergegeven. + + + + If you know the IP address or URL of a server, you can connect to it using the Server name/Address field. An optional port number can be added after the IP address or URL using a colon as a separator, e.g, example.org: + Het IP-adres of de URL van de server kan bij Servernaam/Adres worden ingevoerd. Een optioneel poortnummer kan worden toegevoegd na het IP-adres of de URL met een dubbele punt als scheidingsteken, b.v. example.org: + + + + . The field will also show a list of the most recently used server addresses. + . Het veld toont ook een lijst met de laatstgebruikte serveradressen. - + Server address edit box Serveradres bewerkingsvak - + Holds the current server IP address or URL. It also stores old URLs in the combo box list. Bevat het huidige server-IP-adres of de URL. Het slaat ook oude URL's op in de comboboxlijst. - + Server List Selection - + Serverlijstselectie - + Selects the server list to be shown. - + Selecteert de te tonen serverlijst. - + Server list selection combo box - + Serverlijstselectie combobox - + Filter Filter - + The server list is filtered by the given text. Note that the filter is case insensitive. De serverlijst wordt gefilterd met de gegeven tekst. Merk op dat het filter ongevoelig is voor hoofdletters. - + Filter edit box Filter bewerkingsvak - + Show All Musicians Toon alle muzikanten - + 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. Als u dit selectievakje aanvinkt, worden de muzikanten van alle servers getoond. Als u het selectievakje uitvinkt, worden alle items van de lijstweergave samengevouwen. - + Show all musicians check box Toon alle muzikanten checkbox + + + Type # for occupied servers + Typ # voor bezette servers + CConnectDlgBase @@ -1547,7 +2027,7 @@ List - + Lijst @@ -1581,512 +2061,580 @@ + Server Address + Serveradres + + Server Name/Address - Servernaam/Adres + Servernaam/Adres C&ancel - Annuleren + &Annuleren &Connect - Verbinden + &Verbinden CHelpMenu - + &Help &Hulp - - + + Getting &Started... - Aan de slag... + &Aan de slag... - + Software &Manual... - Softwarehandleiding... + Software&handleiding... - + What's &This - Wat Is Dit + Wat Is &Dit - + &About... &Over... + + CLanguageComboBox + + + Restart Required + Herstart noodzakelijk + + + + Please restart the application for the language change to take effect. + Start de applicatie opnieuw om de taalwijziging door te voeren. + + CLicenceDlg - I &agree to the above licence terms - Ik stem in met bovenstaande licentievoorwaarden + Ik &stem in met bovenstaande licentievoorwaarden - + + This server requires you accept conditions before you can join. Please read these in the chat window. + Deze server vereist dat u voorwaarden accepteert voor het verbinen. Lees deze in het chatvenster. + + + + I have read the conditions and &agree. + Ik heb de voorwaarden gelezen en &stem in. + + + Accept Accepteer - + Decline Niet akkoord - By connecting to this server and agreeing to this notice, you agree to the following: - Door verbinding te maken met deze server en akkoord te gaan met deze mededeling, gaat u akkoord met het volgende: + Door verbinding te maken met deze server en akkoord te gaan met deze mededeling, gaat u akkoord met het volgende: - 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 - U gaat ermee akkoord dat alle gegevens, geluiden of andere werken die naar deze server worden verzonden, eigendom zijn van en gemaakt zijn door u of uw licentiegevers, en dat u deze gegevens, geluiden of andere werken beschikbaar stelt via de volgende Creative Commons Licentie (voor meer informatie over deze licentie, zie + U gaat ermee akkoord dat alle gegevens, geluiden of andere werken die naar deze server worden verzonden, eigendom zijn van en gemaakt zijn door u of uw licentiegevers, en dat u deze gegevens, geluiden of andere werken beschikbaar stelt via de volgende Creative Commons Licentie (voor meer informatie over deze licentie, zie - You are free to: - Je staat vrij om: + Je staat vrij om: - Share - het materiaal + het materiaal - copy and redistribute the material in any medium or format - te delen, te kopiëren en te herdistribueren in elk medium of formaat + te delen, te kopiëren en te herdistribueren in elk medium of formaat - Adapt - Aanpassen + Aanpassen - remix, transform, and build upon the material - remixen, transformeren en bouwen op het materiaal + remixen, transformeren en bouwen op het materiaal - The licensor cannot revoke these freedoms as long as you follow the license terms. - De licentiegever kan deze vrijheden niet herroepen zolang u zich aan de licentievoorwaarden houdt. + De licentiegever kan deze vrijheden niet herroepen zolang u zich aan de licentievoorwaarden houdt. - Under the following terms: - Onder de volgende voorwaarden: + Onder de volgende voorwaarden: - Attribution - Naamsvermelding + Naamsvermelding - You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. - U moet de juiste erkenning geven, een link naar de licentie geven en aangeven of er wijzigingen zijn aangebracht. U mag dit op elke redelijke manier doen, maar niet op een manier die suggereert dat de licentiegever u of uw gebruik goedkeurt. + U moet de juiste erkenning geven, een link naar de licentie geven en aangeven of er wijzigingen zijn aangebracht. U mag dit op elke redelijke manier doen, maar niet op een manier die suggereert dat de licentiegever u of uw gebruik goedkeurt. - NonCommercial - Niet-commercieel + Niet-commercieel - You may not use the material for commercial purposes. - U mag het materiaal niet voor commerciële doeleinden gebruiken. + U mag het materiaal niet voor commerciële doeleinden gebruiken. - ShareAlike - hareAlike + hareAlike - If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. - Als u het materiaal remixt, transformeert of uitbouwt, moet u uw bijdragen distribueren onder dezelfde licentie als het origineel. + Als u het materiaal remixt, transformeert of uitbouwt, moet u uw bijdragen distribueren onder dezelfde licentie als het origineel. - No additional restrictions - Geen extra beperkingen + Geen extra beperkingen - You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. - U mag geen wettelijke voorwaarden of technologische maatregelen toepassen die anderen wettelijk beperken om iets te doen wat de licentie toestaat. + U mag geen wettelijke voorwaarden of technologische maatregelen toepassen die anderen wettelijk beperken om iets te doen wat de licentie toestaat. + + + + CMultiColorLED + + + Red + Rood + + + + Yellow + Geel + + + + Green + Groen CMusProfDlg - - + + Musician Profile Muzikantenprofiel - + Alias/Name Alias/Naam - + Instrument Instrument - + Country Land - + City Stad - + Skill Vaardigheid - + &Close &Sluiten - - - + + + None Geen - + Beginner Beginner - + Intermediate Gemiddeld - + Expert Gevorderd - 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 in. The city you live in and the skill level playing your instrument may also be added. - Stel hier je naam of een alias in zodat de andere muzikanten met wie je wilt spelen weten wie je bent. Daarnaast kun je een instrumentfoto van het instrument dat je bespeelt en een vlag van het land waar je woont, instellen. De stad waar je woont en het vaardigheidsniveau van het spelen van je instrument kunnen ook worden toegevoegd. + Stel hier je naam of een alias in zodat de andere muzikanten met wie je wilt spelen weten wie je bent. Daarnaast kun je een instrumentfoto van het instrument dat je bespeelt en een vlag van het land waar je woont, instellen. De stad waar je woont en het vaardigheidsniveau van het spelen van je instrument kunnen ook worden toegevoegd. - What you set here will appear at your fader on the mixer board when you are connected to a - Wat u hier instelt, verschijnt bij uw fader op het mengpaneel wanneer u verbonden bent met een + Wat u hier instelt, verschijnt bij uw fader op het mengpaneel wanneer u verbonden bent met een - server. This tag will also show up at each client which is connected to the same server as you. If the name is left empty, the IP address is shown instead. - server. Deze tag zal ook verschijnen op elke client die verbonden is met dezelfde server als u. Als de naam leeg is, wordt in plaats daarvan het IP-adres getoond. + server. Deze tag zal ook verschijnen op elke client die verbonden is met dezelfde server als u. Als de naam leeg is, wordt in plaats daarvan het IP-adres getoond. + + + + Write your name or an alias here so the other musicians you want to play with know who you are. You may also add a picture of the instrument you play and a flag of the country you are located in. Your city and skill level playing your instrument may also be added. + Stel hier je naam of een alias in zodat de andere muzikanten met wie je wilt spelen weten wie je bent. Daarnaast kun je een instrumentfoto van het instrument dat je bespeelt en een vlag van het land waar je woont, instellen. De stad waar je woont en je vaardigheidsniveau kunnen ook worden toegevoegd. + + + + What you set here will appear at your fader on the mixer board when you are connected to a Jamulus server. This tag will also be shown at each client which is connected to the same server as you. + Wat u hier instelt verschijnt bij uw fader op het mengpaneel indien verbonden met een Jamulus-server. Deze tag zal ook worden getoond aan andere muzikanten die met deze server verbonden zijn. - + Alias or name edit box Alias of naam bewerkingsvak - + Instrument picture button Afbeelding van het instrument - + Country flag button Landvlag knop - + City edit box Bewerkingsbox voor de stad - + Skill level combo box Combo-box voor vaardigheidsniveau - + Drum Set Drumstel - + Djembe Djembe - + Electric Guitar Elektrische Gitaar - + Acoustic Guitar Akoestische Gitaar - + Bass Guitar Basgitaar - + Keyboard Toetsenbord - + Synthesizer Synthesizer - + Grand Piano Piano - + Accordion Accordeon - + Vocal Vocaal - + Microphone Microfoon - + Harmonica Harmonica - + Trumpet Trompet - + Trombone Trombone - + French Horn Hoorn - + Tuba Tuba - + Saxophone Saxofoon - + Clarinet Klarinet - + Flute Fluit - + Violin Viool - + Cello Cello - + Double Bass Contrabas - + Recorder Opnemer - + Streamer Streamer - + Listener Luisteraar - + Guitar+Vocal Gitaar+Vocaal - + Keyboard+Vocal Toetsenbord+Vocaal - + Bodhran Bodhran - + Bassoon Fagot - + Oboe Hobo - + Harp Harp - + Viola Viola - + Congas Congas - + Bongo Bongo - + Vocal Bass - + Bas (stem) - + Vocal Tenor - + Tenor (stem) - + Vocal Alto - + Alt (stem) - + Vocal Soprano - + Sopraan (stem) - + Banjo Banjo - + Mandolin Mandoline + + + Ukulele + Ukelele + + + + Bass Ukulele + Ukelele-bas + + + + Vocal Baritone + Bariton (stem) + + + + Vocal Lead + Leadzanger + + + + Mountain Dulcimer + Dulcimer + + + + Scratching + Scratchen + + + + Rapping + Rappen + + + + No Name + Geen naam + CServerDlg - + Client List Clientlijst - + 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. De clientlijst toont alle clients die op dit moment verbonden zijn met deze server. Voor elke verbonden client wordt enige informatie over de clients gegeven, zoals het IP-adres en de naam. - + Connected clients list view Overzicht van de lijst met verbonden clients - + Start Minimized on Operating System Start Start geminimaliseerd bij de start van het besturingssysteem - If the start minimized on operating system start check box is checked, the - Als het selectievakje Starten geminimaliseerd op het besturingssysteem is aangevinkt, wordt de + Als het selectievakje Starten geminimaliseerd op het besturingssysteem is aangevinkt, wordt de - server will be started when the operating system starts up and is automatically minimized to a system task bar icon. - server gestart wanneer het besturingssysteem wordt opgestart en wordt automatisch geminimaliseerd tot een systeemtaakbalkpictogram. + server gestart wanneer het besturingssysteem wordt opgestart en wordt automatisch geminimaliseerd tot een systeemtaakbalkpictogram. - Show Creative Commons Licence Dialog - Toon de Creative Commons Licentie + Toon de Creative Commons Licentie - If enabled, a Creative Commons BY-NC-SA 4.0 Licence dialog is shown each time a new user connects the server. - Indien ingeschakeld, wordt een Creative Commons BY-NC-SA 4.0 Licentiedialoogvenster getoond telkens wanneer een nieuwe gebruiker de server verbindt. + Indien ingeschakeld, wordt een Creative Commons BY-NC-SA 4.0 Licentiedialoogvenster getoond telkens wanneer een nieuwe gebruiker de server verbindt. - + Make My Server Public Maak Mijn Server Openbaar - If the Make My Server Public check box is checked, this server registers itself at the central server so that all - Als het selectievakje Maak Mijn Server Openbaar is aangevinkt, registreert deze server zichzelf op de centrale server zodat alle + Als het selectievakje Maak Mijn Server Openbaar is aangevinkt, registreert deze server zichzelf op de centrale server zodat alle - users can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. - gebruikers kunnen de server zien in de lijst van de verbindingsserver in het dialoogvenster en er verbinding mee maken. De registratie van de server wordt periodiek vernieuwd om er zeker van te zijn dat alle servers in de connect dialog server lijst daadwerkelijk beschikbaar zijn. + gebruikers kunnen de server zien in de lijst van de verbindingsserver in het dialoogvenster en er verbinding mee maken. De registratie van de server wordt periodiek vernieuwd om er zeker van te zijn dat alle servers in de connect dialog server lijst daadwerkelijk beschikbaar zijn. - + Register Server Status Tabblad Serverstatus @@ -2103,121 +2651,333 @@ Het adres van de centrale server is het IP-adres of de URL van de centrale server waarop deze server is geregistreerd. Met het type centraal serveradres kan ofwel de lokale regio worden geselecteerd van de standaard centrale servers of kan een handmatig adres worden opgegeven. - + If the Make My Server Public check box is checked, this will show whether registration with the central server is successful. If the registration failed, please choose another server list. - + Als het selectievakje Maak Mijn Server Openbaar is aangevinkt, laat dit zien of registratie bij de centrale server is gelukt. Indien registratie mislukt, kies een andere serverlijst. Default central server type combo box Standaard centrale server type combo box - + + If the start minimized on operating system start check box is checked, the server will be started when the operating system starts up and is automatically minimized to a system task bar icon. + Als de Start geminimaliseerd bij systeemstart checkbox iss aangevinkt, zal de server gestart worden als het systeem opstart end word automatisch geminimaliseerd naar een icoon op de taakbalk. + + + + If the Make My Server Public check box is checked, this server registers itself at the central server so that all users of the application can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. + Indien de Maak mijn server openbaar checkbox is aangevinkt registreert deze server zichzelf bij de centrale server. Hierdoor kunnen alle gebruikers deze zien en er verbinding mee leggen. De registratie wordt periodiek vernieuwd om ervoor te zorgen dat alle servers in de serverlijst ook daadwerkelijk beschikbaar zijn. + + + Custom Central Server Address - + Eigen centrale serveradres - + The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. - + Het eigen centrale serveradres is het IP-adres of de URL van de centrale server die de serverlijst laat zien in de Verbindingsinstellingen. - + Central server address line edit Centrale server adresbewerking van de lijn - + Server List Selection - + Serverlijstselectie - + Selects the server list (i.e. central server address) in which your server will be added. - + Selecteert de serverlijst (i.e. het centrale serveradres) waarin je server zal worden toegevoegd. - + Server list selection combo box - + Serverlijstselectie combobox - + Server Name Servernaam - 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. - De naam van de server identificeert uw server in de lijst van de connect-dialoog-server bij de clients. Als er geen naam wordt gegeven, wordt in plaats daarvan het IP-adres getoond. + De naam van de server identificeert uw server in de lijst van de connect-dialoog-server bij de clients. Als er geen naam wordt gegeven, wordt in plaats daarvan het IP-adres getoond. + + + + The server name identifies your server in the connect dialog server list at the clients. + De servernaam identificeert je server in de serverlijst. Als er geen naam wordt opgegeven zal het IP-adres worden getoond. - + Server name line edit Servernaam lijnbewerking - + Location City Locatie Stad - + 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. De stad waar deze server zich bevindt kan hier worden ingesteld. Als er een plaatsnaam wordt ingevoerd, wordt deze getoond in de lijst van de connect-dialoog-server bij de clients. - + City where the server is located line edit Stad waar de server zich bevindt lijnbewerking - + Location country Land van locatie - + 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. Het land waarin deze server zich bevindt kan hier worden ingesteld. Als er een land is ingevoerd, wordt dit getoond in de lijst van de verbindingsserver bij de clients. - + Country where the server is located combo box Land waar de server zich bevindt combo box + + + Display dialog to select recording directory button + Toon venster voor het instellen van de directory voor geluidsopnames + + + + + Main Recording Directory + Hoofddirectory voor geluidsopnames + + + + Click the button to open the dialog that allows the main recording directory to be selected.The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). + Klik op deze knop om het venster te tonen waarin de directory voor geluidsopnames kan worden ingesteld. Deze directory moet reeds bestaan en door de gebruiker die Jamulus draait kunnen worden aangepast. + + + + Main recording directory text box (read-only) + Hoofddirectory voor geluidsopnames tekstvak (read-only) + + + + The current value of the main recording directory. The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). Click the button to open the dialog that allows the main recording directory to be selected. + De huidig ingestelde directory voor geluidsopnames. Deze directory moet reeds bestaan en door de gebruiker die Jamulus draait kunnen worden aangepast. + - + Clear the recording directory button + Wis aangegeven geluidsopnamedirectory + + + + Clear Recording Directory + Wis aangegeven geluidsopnamedirectory + + + + Click the button to clear the currently selected recording directory. This will prevent recording until a new value is selected. + Klik op deze knop om de aangegeven geluidsopnamedirectory te wissen. Opnames kunnen niet worden gemaakt zonder ingestelde geluidsopnamedirectory. + + + + Checkbox to turn on or off server recording + Checkbox om geluidsopname op de server in of uit te schakelen + + + + Enable Recorder + Geluidsopname inschakelen + + + + Checked when the recorder is enabled, otherwise unchecked. The recorder will run when a session is in progress, if (set up correctly and) enabled. + Aangevinkt indien de geluidsrecorder aanstaat. De geluidsrecorder zal dan sessie opnemen. + + + + Current session directory text box (read-only) + Huidige sessie directory text box (read-only) + + + + Current Session Directory + Huidige sessie directory + + + + Enabled during recording and holds the current recording session directory. Disabled after recording or when the recorder is not enabled. + Ingeschakeld tijdens geluidsopname en bevat de sessie-directory. Uitgeschakeld na geluidsopname of wanneer geluidsopname uit staat. + + + + Recorder status label + Geluidsopname statuslabel + + + + Recorder Status + Geluidsopname status + + + + Displays the current status of the recorder. The following values are possible: + Toon de huidige status van geluidsopname. De volgende keuzes zijn mogelijk: + + + + No recording directory has been set or the value is not useable + Geen geluidsopnamedirectory ingesteld of waarde is niet bruikbaar + + + + Recording has been switched off + Geluidsopname is uitgeschakeld + + + + by the UI checkbox + door het selectievak + + + + , either by the UI checkbox or SIGUSR2 being received + , of door het selectievak of doordat SIGUSR2 wordt ontvangen + + + + There is no one connected to the server to record + Er is niemand verbonden met de server voor geluidsopname + + + + The performers are being recorded to the specified session directory + Het geluid van de muzikanten wordt opgenomen in de aangegeven sessie-directory + + + + NOTE + NOOT + + + + If the recording directory is not useable, the problem will be displayed in place of the directory. + Als de geluidsopnamedirectory niet bruikbaar is wordt het probleem getoont in plaats van de locatie. + + + + Server welcome message edit box + Vak voor welkomsbericht server + + + + Server Welcome Message + Welkomsbericht server + + + + A server welcome message text is displayed in the chat window if a musician enters the server. If no message is set, the server welcome is disabled. + Een server welkomsbericht wordt getoond in het chatvenster als een muzikant binnenkomt. Indien niets ingesteld wordt geen welkomsbericht getoond. + + + + Type a message here. If no message is set, the server welcome is disabled. + Typ hier een bericht. Indien niets ingesteld wordt geen welkomsbericht getoond. + + + + software upgrade available + software-update beschikbaar + + + + ERROR + FOUT + + + Displays the current status of the recorder. + Toont de huidige status van de geluidsopname. + + + + Request new recording button + Knop om nieuwe geluidsopname aan te vragen + + + + New Recording + Nieuwe geluidsopname + + + + During a recording session, the button can be used to start a new recording. + Tijdens een opnamesessie kan de knio gebruikt worden om een nieuwe opname te starten. + + + + E&xit &Sluiten - + &Hide - Verbergen + &Verbergen - - - + + + server server - + &Open - &Open + &Open - server - server + server + + + + Select Main Recording Directory + Selecteer hoofdgeluidsopnamedirectory - Predefined Address - + Standaardadres + + + + Recording + Neemt op + + + + Not recording + Neemt niet op + + + + Not initialised + Niet geïnitialiseerd + + + + Not enabled + Niet ingeschakeld Manual @@ -2232,52 +2992,62 @@ Standaard (Noord-Amerika) - + Server Server - + &Window &Window - + Unregistered Niet geregistreerd - + Bad address Slecht adres - + Registration requested Aanmelding gevraagd - + Registration failed Registratie is mislukt - + Check server version Controleer de versie van de server - + Registered Geregistreerd - + Central Server full Centrale server vol - + + Your server version is too old + Je serverversie is te oud + + + + Requirements not fulfilled + Vereisten niet gehaald + + + Unknown value Onbekende waarde @@ -2291,69 +3061,125 @@ - + Name Naam Jitter Buffer Size - Jitter Buffermaat + Jitter Buffergrootte + + + + Server Setup + Server Setup + + + + Chat Window Welcome (HTML/CSS Supported) + Welkomsbericht Chatvenster (HTML/CSS mogelijk) + + + + Options + Opties - + Start Minimized on Windows Start - Start geminimaliseerd bij Windows-start + Start geminimaliseerd bij systeemstart - Show Creative Commons BY-NC-SA 4.0 Licence Dialog - Toon Creative Commons BY-NC-SA 4.0 Licentie Dialoog + Toon Creative Commons BY-NC-SA 4.0 Licentie Dialoog + + + + Update check + Update check Make My Server Public (Register My Server in the Server List) - Maak mijn server openbaar (Registreer mijn server in de lijst met servers) + Maak mijn server openbaar (Registreer deze in de lijst met servers) + + + + Genre + Genre - + + STATUS STATUS - + Custom Central Server Address: - + Eigen centrale serveradres: + + + + Recording Directory + Geluidsopnamedirectory + + + + Enable Jam Recorder + Activeer geluidsopname + + + + New Recording + Nieuwe geluidsopname + + + + Language + Taal Central Server Address: Adres Centrale Server: - + My Server Info Mijn serverinfo - + Location: City Locatie: Stad - + Location: Country Locatie: Land - + Enable jam recorder + Geluidsopname activeren + + + New recording + Nieuwe geluidsopname + + + Recordings folder + Geluidsopname folder + + TextLabelNameVersion - TextLabelNameVersion + TextLabelNameVersion CSound - + Error closing stream: $s Fout tijdens afsluiten: $s @@ -2394,93 +3220,109 @@ Kan de Jack-client niet activeren. - + The Jack server was shut down. This software requires a Jack server to run. Try to restart the software to solve the issue. De Jack-server werd afgesloten. Voor deze software is een Jack-server nodig om te kunnen draaien. Probeer de software te herstarten om het probleem op te lossen. - + CoreAudio input AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. CoreAudio-ingang AudioHardwareGetProperty-oproep mislukt. Het lijkt erop dat er geen geluidskaart beschikbaar is in het systeem. - + CoreAudio output AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. CoreAudio uitgang AudioHardwareGetProperty call mislukt. Het lijkt erop dat er geen geluidskaart beschikbaar is in het systeem. - + Current system audio input device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. Een sample rate van %1 Hz voor het audio-ingangsapparaat van het huidige systeem wordt niet ondersteund. Open de Audio-MIDI-Setup in Applications->Utilities en probeer een sample rate van %2 Hz in te stellen. - + + + The current selected audio device is no longer present in the system. + Het geselecteerde audioapparaat is niet langer in het systeem beschikbaar. + + + + The audio input device is no longer available. + Het audio-invoerapparaat is niet langer beschikbaar. + + + + The audio output device is no longer available. + Het audio-uitvoerapparaat is niet langer beschikbaar. + + + Current system audio output device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. De sample rate van %1 Hz van het huidige systeem audiouitgangsapparaat wordt niet ondersteund. Open de Audio-MIDI-Setup in Applications->Utilities en probeer een sample rate van %2 Hz in te stellen. - + The audio input stream format for this audio device is not compatible with this software. Het audio input stream-formaat voor dit audioapparaat is niet compatibel met deze software. - + The audio output stream format for this audio device is not compatible with this software. Het formaat van de audio-uitgangsstroom voor dit audioapparaat is niet compatibel met deze software. - + The buffer sizes of the current input and output audio device cannot be set to a common value. Please choose other input/output audio devices in your system settings. De buffergrootte van het huidige in- en uitgaande audioapparaat kan niet op een gemeenschappelijke waarde worden ingesteld. Kies andere in-/uitgangsaudioapparaten in uw systeeminstellingen. - + The audio driver could not be initialized. De audiodriver kon niet worden geïnitialiseerd. - + The audio device does not support the required sample rate. The required sample rate is: Het audioapparaat ondersteunt niet de vereiste samplefrequentie. De vereiste samplefrequentie wel: - + The audio device does not support setting the required sampling rate. This error can happen if you have an audio interface like the Roland UA-25EX where you set the sample rate with a hardware switch on the audio device. If this is the case, please change the sample rate to - Het audioapparaat biedt geen ondersteuning voor het instellen van de vereiste bemonsteringsfrequentie. Deze fout kan zich voordoen als u een audio-interface heeft zoals de Roland UA-25EX waarbij u de samplefrequentie instelt met een hardwareschakelaar op het audioapparaat. Als dit het geval is, verander dan de samplefrequentie in + Het audioapparaat biedt geen ondersteuning voor het instellen van de vereiste bemonsteringsfrequentie. Deze fout kan zich voordoen als u een audio-interface heeft zoals de Roland UA-25EX waarbij u de samplefrequentie instelt met een hardwareschakelaar op het audioapparaat. Als dit het geval is, verander dan de samplefrequentie in - + Hz on the device and restart the Hz op het apparaat en start de - + software. software. - + The audio device does not support the required number of channels. The required number of channels for input and output is: Het audioapparaat ondersteunt niet het vereiste aantal kanalen. Het vereiste aantal kanalen voor in- en uitvoer is: - - + + Required audio sample format not available. Vereist audiosampleformaat niet beschikbaar. - + No ASIO audio device (driver) found. Geen ASIO-audioapparaat (stuurprogramma) gevonden. - + The De - + software requires the low latency audio interface ASIO to work properly. This is not a 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 the ASIO4All driver. software vereist de lage-latency audio-interface ASIO om goed te kunnen werken. Dit is geen standaard Windows audio-interface en daarom is een speciale audio-stuurprogramma vereist. Ofwel heeft uw geluidskaart een native ASIO driver (die wordt aanbevolen), ofwel wilt u alternatieve drivers gebruiken zoals de ASIO4All driver. @@ -2488,55 +3330,84 @@ CSoundBase - Invalid device selection. - Ongeldige apparaatkeuze. + Ongeldige apparaatkeuze. - The audio driver properties have changed to a state which is incompatible with this software. The selected audio device could not be used because of the following error: - De eigenschappen van de audiodriver zijn veranderd in een toestand die niet compatibel is met deze software. Het geselecteerde audioapparaat kon niet worden gebruikt vanwege de volgende fout: + De eigenschappen van de audiodriver zijn veranderd in een toestand die niet compatibel is met deze software. Het geselecteerde audioapparaat kon niet worden gebruikt vanwege de volgende fout: - Please restart the software. - Start de software opnieuw op. + Start de software opnieuw op. - Close - Sluiten + Sluiten + + + + The selected audio device could not be used because of the following error: + Het geselecteerde audioapparaat kon niet worden gebruikt vanwege de volgende fout: + + + + The previous driver will be selected. + Het vorige stuurprogramma zal worden geselecteerd. + + + + The previously selected audio device is no longer available or the audio driver properties have changed to a state which is incompatible with this software. We now try to find a valid audio device. This new audio device might cause audio feedback. So, before connecting to a server, please check the audio device setting. + Het eerder geselecteerde audioapparaat is niet langer beschikbaar of de stuurprogramma-eigenschappen zijn aangepast en werken niet met deze software. We zoeken nu naar een bruikbaar audioapparaat. Dit nieuwe audioappraat zou audio feedback kunnen veroorzaken. Controleer daarom de audioapparaat-instellingen alvorens op een server in te loggen. - + No usable Niet bruikbaar - + audio device (driver) found. audioapparaat (stuurprogramma) gevonden. - + In the following there is a list of all available drivers with the associated error message: Hieronder vindt u een lijst van alle beschikbare drivers met de bijbehorende foutmelding: - + Do you want to open the ASIO driver setups? Wilt u de ASIO-stuurprogramma's openen? - + could not be started because of audio interface issues. kon niet worden gestart vanwege problemen met de audio-interface. + + QCoreApplication + + + , Version + , Versie + + + + Internet Jam Session Software + Internet Jamsessie Software + + + + Released under the GNU General Public License (GPL) + Gereleased onder de GNU General Public License (GPL) + + global - + For more information use the What's This help (help menu, right mouse button or Shift+F1) Voor meer informatie gebruik de Wat Is Dit hulp (helpmenu, rechtermuisklik of Shift+F1) diff --git a/src/res/translation/translation_pl_PL.qm b/src/res/translation/translation_pl_PL.qm new file mode 100755 index 0000000000..87a0371dfb Binary files /dev/null and b/src/res/translation/translation_pl_PL.qm differ diff --git a/src/res/translation/translation_pl_PL.ts b/src/res/translation/translation_pl_PL.ts new file mode 100644 index 0000000000..cd7a636903 --- /dev/null +++ b/src/res/translation/translation_pl_PL.ts @@ -0,0 +1,2902 @@ + + + + + CAboutDlg + + + Qt cross-platform application framework + Qt - międzyplatformowe szablony aplikacji + + + + Audio reverberation code by Perry R. Cook and Gary P. Scavone + Kod algorytmu pogłosu: Perry R. Cook i Gary P Scavone + + + + Some pixmaps are from the + Niektóre obrazki pochodzą z + + + + This app enables musicians to perform real-time jam sessions over the internet. + Aplikacja pozwala muzykom na sesje dźwiękowe w czasie rzeczywistym przez internet. + + + + There is a server which collects the audio data from each client, mixes the audio data and sends the mix back to each client. + To jest serwer, który zbiera dane audio każdego uczestnika i już zmiksowane odsyła do każdego. + + + + This app uses the following libraries, resources or code snippets: + Aplikacja korzysta z następująuch bibliotek, źródeł lub fragmentów kodu: + + + + Country flag icons by Mark James + Ikony flag poszczególnych krajów: Mark James + + + + For details on the contributions check out the + Aby znaleźć szczegółowe informacje o współtwórcach sprawdź + + + + Github Contributors list + Lista współautorów na Github-ie + + + + Spanish + hiszpański + + + + French + francuski + + + + Portuguese + portugalski + + + + Dutch + holenderski + + + + Italian + włoski + + + + German + niemiecki + + + + Polish + polski + + + + Swedish + szwedzki + + + + Slovak + słowacki + + + + About + O programie + + + + CAboutDlgBase + + + About + O programie + + + + TextLabelVersion + + + + + Copyright (C) 2005-2020 Volker Fischer and others + Copyright (C) 2005-2020 Volker Fischer i inni + + + + A&bout + O &programie + + + + &Libraries + &Biblioteki + + + + &Contributors + &Współtwórcy + + + + &Translation + &Tłumaczenia + + + + &OK + &OK + + + + CAnalyzerConsole + + + Analyzer Console + Konsola analizatora + + + + Error Rate of Each Buffer Size + Częstotliwość występowania błędu dla każdego rozmiaru bufora + + + + CAudioMixerBoard + + + Personal Mix at the Server + Własny miks na serwerze + + + + When connected to a server, the controls here allow you to set your local mix without affecting what others hear from you. The title shows the server name and, when known, whether it is actively recording. + Podczas połączenia z serwerem, znajdujące się tutaj opcje pozwalają ustawić własny miks bez wpływu na to jak inni ciebie słyszą. W tytule zawarta jest nazwa serwera oraz to, czy nagrywanie jest aktywne. + + + + Server + Serwer + + + + T R Y I N G T O C O N N E C T + P R Ó B U J Ę S I Ę P O Ł Ą C Z Y Ć + + + + RECORDING ACTIVE + NAGRYWANIE AKTYWNE + + + + Personal Mix at: + Własny miks na: + + + + CChannelFader + + + + + Pan + Panorama + + + + + + Mute + Wycisz + + + + + + Solo + Solo + + + + &No grouping + &Nie grupuj + + + + + + + Assign to group + Przypisz do grupy + + + + Channel Level + Poziom kanału + + + + Input level of the current audio channel at the server + Poziom wejściowy aktualnego kanału audio na serwerze + + + + Mixer Fader + Suwak miksera + + + + Local mix level setting of the current audio channel at the server + Lokalne ustawienie poziomu miksowania bieżącego kanału audio na serwerze + + + + Status Indicator + Wskaźnik stanu + + + + Shows a status indication about the client which is assigned to this channel. Supported indicators are: + Pokazuje wskazanie statusu uczestnika, który jest przypisany do tego kanału. Wskazana oznaczają: + + + + Status indicator label + Etykieta wskaźnika stanu + + + + Panning + Ustawienia panoramy + + + + Local panning position of the current audio channel at the server + Lokalna pozycja panoramy aktualnego kanału dźwiękowego na serwerze + + + + With the Mute checkbox, the audio channel can be muted. + Pole wyboru wycisza kanał audio. + + + + Mute button + Przycisk wyciszenia + + + + Solo button + Przycisk funkcji Solo + + + + Group + Grupa + + + + With the Grp checkbox, a group of audio channels can be defined. All channel faders in a group are moved in proportional synchronization if any one of the group faders are moved. + Pole wybory GR służy do definiowania grupy kanałów dźwiękowych. Kiedy jeden z suwaków w grupie jest przesuwany, wszystkie inne przesuwane są proporcjonalnie. + + + + Group button + Przycisk grupy + + + + Fader Tag + Etykieta suwaka + + + + Alias + Nazwa + + + + Displays the pre-fader audio level of this channel. All clients connected to the server will be assigned an audio level, the same value for every client. + Pokazuje początkowy poziom dźwięku suwaka tego kanału. Każdemu uczetnikowi podłączającemu się do tego serwera zostanie ustawiony ten poziom, ta sama wartość dla każdego. + + + + Adjusts the audio level of this channel. All clients connected to the server will be assigned an audio fader, displayed at each client, to adjust the local mix. + Regulacja poziomu dźwięku tego kanału. Wszystkim klientom połączonym z serwerem zostanie przypisany suwak, wyświetlany przy każdym kliencie, w celu dostosowania lokalnego miksowania. + + + + Speaker with cancellation stroke: Indicates that another client has muted you. + Przekreślony głośnik: Oznacza, że któryś użytkownik cię wyciszył. + + + + Sets the pan from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. + Ustawia panoramę do prawej strony kanału. Działa tylko w trybie stereo lub najlepiej mono in/stereo out. + + + + With the Solo checkbox, the audio channel can be set to solo which means that all other channels except the soloed channel are muted. It is possible to set more than one channel to solo. + Za pomocą tego pola wyboru, kanał audio może być ustawiony na solo, co oznacza, że wszystkie inne kanały są wyciszone. Możliwe jest ustawienie więcej niż jednego kanału w tym trybie. + + + + The fader tag identifies the connected client. The tag name, a picture of your instrument and the flag of your country can be set in the main window. + Oznaczenie suwaka idenyfikuje podłączonego uczestnika. Nazwa, obrazek i flaga kraju ustawia się w głównym oknie programu. + + + + Mixer channel instrument picture + Obrazek instrumentu dla kanału miksera + + + + Mixer channel label (fader tag) + Etykieta kanału mieksera (nazwa suwaka) + + + + Mixer channel country flag + Flaga kraju kanału miksera + + + + PAN + PANORAMA + + + + MUTE + WYCISZENIE + + + + SOLO + SOLO + + + + GRP + GR + + + + M + W + + + + S + S + + + + G + G + + + + Grp + Gr + + + + Alias/Name + Nick/Nazwa + + + + Instrument + Instrument + + + + Location + Lokalizacja + + + + + + Skill Level + Poziom umiejętności + + + + Beginner + Początkujący + + + + Intermediate + Średniozaawansowany + + + + Expert + Ekspert + + + + Musician Profile + Profil muzyka + + + + CChatDlg + + + Chat Window + Okno Czatu + + + + The chat window shows a history of all chat messages. + Okno rozmowy pokazuje historię wszystkich widomości czatu. + + + + Chat history + Historia czatu + + + + Input Message Text + Wprowadzanie wiadomości czatu + + + + 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. + Wpisz tekst widomości w polu edycji i wciśnij enter, aby wysłać wiadomość do serwera, który roześle ją do wszystkich podłączonych osób. Twoja wiadomość pojawi się w oknie czatu. + + + + New chat text edit box + Pole edycji nowego tekstu czatu + + + + Type a message here + Napisz tu wiadomość + + + + &Edit + &Edytuj + + + + Cl&ear Chat History + &Wyczyść historię rozmowy + + + + Do you want to open the link + Chcesz otworzyć ten link + + + + in an external browser? + w zewnętrznej przeglądarce? + + + + CChatDlgBase + + + Chat + Czat + + + + &Send + &Wyślij + + + + CClientDlg + + + Input Level Meter + Miernik Poziomu Wejścia + + + + Make sure not to clip the input signal to avoid distortions of the audio signal. + Aby uniknąć zniekształceń sygnału audio, należy upewnić się, że sygnał wejściowy nie jest obcinany. + + + + Input level meter + Miernik poziomu wejścia + + + + Simulates an analog LED level meter. + Symuluje analogową diodę miernika poziomu. + + + + Connect/Disconnect Button + Przycisk Połącz/Rozłącz + + + + Connect and disconnect toggle button + Przycisk przełącznika połącz i rozłącz + + + + Local Audio Input Fader + Fader Lokalnego Wejścia Audio + + + + + L + L + + + + , where + , gdzie + + + + is the current attenuation indicator. + jest wskaźnik aktualnego tłumienia. + + + + Local audio input fader (left/right) + Suwak lokalnego wejścia audio + + + + Delay Status LED + Dioda stanu opóźnienia + + + + Delay status LED indicator + Wskaźnik diody stanu opóźnienia + + + + Buffers Status LED + Dioda stanu buforów + + + + The network jitter buffer is not large enough for the current network/audio interface jitter. + Bufor dla odchyleń sieciowych nie jest wystarczający dla aktualnych odchyleń interfejsu sieciowego/dźwiękowego. + + + + This shows the level of the two stereo channels for your audio input. + Pokazuje poziom dwóch kanałów stereo dla wejścia audio. + + + + If the application is connected to a server and you play your instrument/sing into the microphone, the VU 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. + Jeśli aplikacja jest już podłączona do serwera i grasz na instrumencie/śpiewasz do mikrofonu, miernik VU powinien migać. Jeśli tak nie jest, prawdopodobnie wybrany został niewłaściwy kanał wejściowy (np. wejście liniowe zamiast wejścia mikrofonowego) lub ustawione zostało zbyt niskie wzmocnienie wejściowe w mikserze audio (Windows). + + + + For proper usage of the application, you should not hear your singing/instrument through the loudspeaker or your headphone when the software is not connected.This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). + W celu prawidłowego korzystania z aplikacji nie należy słyszeć śpiewu/instrumentu przez głośnik lub słuchawki, gdy oprogramowanie nie jest podłączone. Można to osiągnąć wyciszając wejściowy kanał audio w mikserze odtwarzającym (nie w mikserze nagrywającym!). + + + + Opens a dialog where you can select a server to connect to. If you are connected, pressing this button will end the session. + otwiera okno wyboru serwera. do którego można się podłączyć, a gdy połączono, naciśnięcie tego przycisku spowoduje zakończenie sesji. + + + + Controls the relative levels of the left and right local audio channels. For a mono signal it acts as a pan between the two channels.For example, if 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 + Kontroluje względne poziomy lewego i prawego lokalnego kanału audio. Na przykład, jeśli mikrofon jest podłączony do prawego kanału wejściowego, a instrument jest podłączony do lewego kanału wejściowego, który jest znacznie głośniejszy od mikrofonu, należy przesunąć suwak audio w kierunku, w którym pokazuje etykieta nad suwakiem + + + + Reverb effect + Efekt pogłosu + + + + Reverb can be applied to one local mono audio channel or to both channels in stereo mode. The mono channel selection and the reverb level can be modified. For example, if a microphone signal is fed in to the right audio channel of the sound card and a reverb effect needs to be applied, set the channel selector to right and move the fader upwards until the desired reverb level is reached. + Pogłos może być zastosowany do jednego lokalnego kanału audio mono lub do obu kanałów w trybie stereo. Wybór kanału monofonicznego i poziom pogłosu mogą być modyfikowane. Na przykład, jeśli sygnał z mikrofonu jest podawany do prawego kanału audio karty dźwiękowej i konieczne jest zastosowanie efektu pogłosu, należy ustawić przełącznik wyboru kanału w prawo i przesunąć suwak w górę, aż do osiągnięcia żądanego poziomu pogłosu. + + + + Reverb effect level setting + Ustawienie poziomu efektu pogłosu + + + + Reverb Channel Selection + Wybór kanału z pogłosem + + + + With these radio buttons the audio input channel on which the reverb effect is applied can be chosen. Either the left or right input channel can be selected. + Za pomocą tych przycisków wyboru można ustawić kanał wejściowy audio, na którym zostanie zastosowany efekt pogłosu. Można wybrać lewy lub prawy. + + + + Left channel selection for reverb + Wybór lewego kanału dla pogłosu + + + + Right channel selection for reverb + Wybór prawego kanału dla pogłosu + + + + Shows the current audio delay status: + Pokazuje obecny status opóźnienia audio: + + + + Green + Zielony + + + + The delay is perfect for a jam session. + Opóźnienie jest idealne dla sesji. + + + + Yellow + Żółty + + + + A session is still possible but it may be harder to play. + Sesja jest nadal możliwa, ale może być trudniej grać. + + + + Red + Czerwony + + + + The delay is too large for jamming. + Opóźnienie jest zbyt duże dla sesji. + + + + If this LED indicator turns red, you will not have much fun using the application. + Jeśli ta dioda zmieni kolor na czerwony, korzystanie z aplikacji może być utrudnione. + + + + The buffers status LED shows the current audio/streaming status. If the light is red, the audio stream is interrupted. This is caused by one of the following problems: + Dioda stanu buforów pokazuje aktualny status audio/transmisji. Jeśli dioda świeci na czerwono, strumień audio zostaje przerwany. Może to być spowodowane przez jeden z poniższych problemów: + + + + The sound card's buffer delay (buffer size) is too small (see Settings window). + Opóźnienie bufora karty dźwiękowej (rozmiar bufora) jest zbyt małe (patrz okno Ustawienia). + + + + The upload or download stream rate is too high for your internet bandwidth. + Prędkość przesyłania lub pobierania strumienia audio jest zbyt wysoka dla dostępnej prędkości internetu. + + + + The CPU of the client or server is at 100%. + Obciążenie CPU klienta lub serwera wynosi 100%. + + + + Buffers status LED indicator + Dioda wskazująca stan buforów + + + + + C&onnect + &Połącz + + + + software upgrade available + dostępna aktualizacja oprogramowania + + + + &File + &Plik + + + + &Load Mixer Channels Setup... + Wczytaj ustawienia kanałów &miksera... + + + + &Save Mixer Channels Setup... + Zapi&sz ustawienia kanałów miksera... + + + + &View + &Widok + + + + &Connection Setup... + &Konfiguracja połączenia... + + + + My &Profile... + Mój &profil... + + + + C&hat... + &Czat... + + + + &Settings... + &Ustawienia... + + + + &Analyzer Console... + &Konsola analizatora... + + + + N&o User Sorting + &Bez sortowania kanałów + + + + Sort Users by &Name + Sortuj kanały według &nazwy + + + + Sort Users by &Instrument + Sortuj kanały według &instrumentu + + + + Sort Users by &Group + Sortuj kanały według &grupy + + + + Sort Users by &City + Sortuj kanały według &miasta + + + + Ok + Ok + + + &Clear All Stored Solo Settings + Wy&czyść wszystkie zachowane ustawienia użytkowników + + + + Set All Faders to New Client &Level + Ustaw suwaki do określonego w ustawieniach &poziomu + + + + E&xit + &Wyjdź + + + + &Edit + &Edytuj + + + + Use &Two Rows Mixer Panel + Używaj &dwurzędowego panelu miksera + + + + &Clear All Stored Solo and Mute Settings + Wy&czyść wszystkie ustawienia solo/wycissz + + + + Center + Środek + + + + R + P + + + + Central Server + Serwer centralny + + + + + Select Channel Setup File + Wybierz plik ustawień kanału + + + + user + użytkownik + + + + users + użytkownicy + + + + Your sound card is not working correctly. Please open the settings dialog and check the device selection and the driver settings. + + + + + D&isconnect + &Rozłącz + + + + CClientDlgBase + + + Delay + Opóźnienie + + + + Buffers + Bufory + + + + Input + Wejście + + + + L + L + + + + R + P + + + + &Mute Myself + &Wycisz mnie + + + + &Settings + &Ustawienia + + + + &Chat + &Czat + + + + C&onnect + &Połącz + + + + Pan + Panorama + + + + Center + Środek + + + + Reverb + Pogłos + + + + Left + Lewy + + + + Right + Prawy + + + + MUTED (Other people won't hear you) + WYCISZONY (Inni nie będą cię słyszeć) + + + + Update check + Sprawdzanie uaktualnień + + + + CClientSettingsDlg + + + Jitter Buffer Size + Rozmiar bufora odchyleń + + + + The jitter buffer setting is therefore a trade-off between audio quality and overall delay. + Wartość bufora odchyleń jest więc kompromisem pomiędzy jakością dźwięku a całkowitym opóźnieniem. + + + + Local jitter buffer slider control + Suwak kontrolujący lokalny bufor odchyleń + + + + Server jitter buffer slider control + Suwak kontrolujący bufor odchyleń na serwerze + + + + Auto jitter buffer switch + Przełącznik automatycznego rozmiaru bufora odchyleń + + + + Jitter buffer status LED indicator + Dioda stanu bufora odchyleń + + + + Sound Card Device + Urządzenie dźwiękowe + + + + The ASIO driver (sound card) can be selected using + Sterownik ASIO (karta dźwiękowa) można wybrać za pomocą programu + + + + 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. + pod systemem operacyjnym Windows. W systemie MacOS/Linux nie ma możliwości wyboru karty dźwiękowej. Jeśli wybrany sterownik ASIO nie jest prawidłowy, wyświetlany jest komunikat o błędzie i wybierany jest poprzedni prawidłowy sterownik. + + + + If the driver is selected during an active connection, the connection is stopped, the driver is changed and the connection is started again automatically. + Jeżeli podczas aktywnego połączenia zostanie wybrany sterownik, to połączenie zostanie przerwane, sterownik zostanie zmieniony i połączenie zostanie ponownie automatycznie uruchomione. + + + + Sound card device selector combo box + Pole wyboru urządzenia karty dźwiękowej + + + + If you are using the kX ASIO driver, make sure to connect the ASIO inputs in the kX DSP settings panel. + Jeśli używasz sterownika kX ASIO, upewnij się, że podłączyłeś wejścia ASIO w panelu ustawień kX DSP. + + + + Sound Card Channel Mapping + Mapowanie kanałów kart dźwiękowych + + + + For each + Dla każdego + + + + input/output channel (Left and Right channel) a different actual sound card channel can be selected. + kanał wejściowy/wyjściowy (kanał prawy i lewy) - mogą być wybrane różne kanały karty dźwiękowej. + + + + Left input channel selection combo box + Pole wyboru lewego kanału wejściowego + + + + Right input channel selection combo box + Pole wyboru prawego kanału wejściowego + + + + Left output channel selection combo box + Pole wyboru lewego kanału wyjściowego + + + + Right output channel selection combo box + Pole wyboru prawego kanału wyjściowego + + + + Enable Small Network Buffers + Używaj małych buforów sieciowych + + + + 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 + Jeśli jest włączona jest obsługa bardzo małych sieciowych pakietów audio. Bardzo małe pakiety sieciowe są rzeczywiście wykorzystywane tylko wtedy, gdy opóźnienie bufora karty dźwiękowej jest mniejsze niż + + + + Enable small network buffers check box + Pole wyboru Włącz Małe Bufory Sieciowe + + + + Sound Card Buffer Delay + Opóźnienie Bufora Karty Dźwiękowej + + + + Three buffer sizes are supported + Obsługiwane są trzy wielkości buforów + + + + The buffer setting is therefore a trade-off between audio quality and overall delay. + Wartość bufora odchyleń jest więc kompromisem pomiędzy jakością dźwięku a całkowitym opóźnieniem. + + + + 128 samples setting radio button + przycisk wyboru 128-samplowego bufora + + + + 256 samples setting radio button + przycisk wyboru 256-samplowego bufora + + + + ASIO setup push button + Przycisk ustawień ASIO + + + + Fancy + Fantazyjna + + + + Compact + Kompaktowa + + + + Audio Channels + Kanały Audio + + + + mode will increase your stream's data rate. Make sure your upload rate does not exceed the available upload speed of your internet connection. + tryb ten zwiększy szybkość przesyłania danych w strumieniu. Upewnij się, że prędkość przesyłania danych nie przekracza dostępnej prędkości przesyłania połączenia internetowego. + + + + Audio channels combo box + Pole wyboru kanałów audio + + + + Audio Quality + Jakość Audio + + + + Audio quality combo box + Pole ustawień jakości audio + + + + New Client Level + Poziom nowego klienta + + + + New client level edit box + Pole edycji poziomu nowego klienta + + + + Current Connection Status Parameter + Wskaźnik aktualnego stanu połączenia + + + + If 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. + Jeśli używany jest sterownik ASIO4ALL, należy pamiętać, że zazwyczaj wprowadza on ok. 10-30 ms dodatkowego opóźnienia dźwięku. Dlatego zaleca się używanie karty dźwiękowej z natywnym sterownikiem ASIO. + + + + If the selected sound card device offers more than one input or output channel, the Input Channel Mapping and Output Channel Mapping settings are visible. + Jeśli wybrane urządzenie karty dźwiękowej oferuje więcej niż jeden kanał wejściowy lub wyjściowy, widoczne są ustawienia Mapowanie Kanału Wejścia i Mapowanie Kanału Wejścia. + + + + samples. The smaller the network buffers, the lower the audio latency. But at the same time the network load increases and the probability of audio dropouts also increases. + sample. Im mniejsze są bufory sieciowe, tym mniejsze jest opóźnienie dźwięku. Ale jednocześnie zwiększa się obciążenie sieci i prawdopodobieństwo przerw w transmisji dźwięku. + + + + 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 a red light in the status indicator (drop outs) and the higher the upload rate and the lower the overall delay. + Rzeczywiste opóźnienie bufora ma wpływ na status połączenia, bieżącą prędkość wysyłania i całkowite opóźnienie. Im mniejsza wielkość bufora, tym większe prawdopodobieństwo pojawienia się czerwonej kontrolki we wskaźniku statusu (drop-out) i tym większa szybkość wysyłania danych oraz tym mniejsze jest ogólne opóźnienie. + + + + 64 samples setting radio button + przycisk wyboru 64-samplowego bufora + + + + Custom Central Server Address + Własny adres centralnego serwera + + + + Audio Upstream Rate depends on the current audio packet size and compression setting. Make sure that the upstream rate is not higher than your available internet upload speed (check this with a service such as speedtest.net). + Prędkość wychodzącego strumienia audio zależy od ustawionej kompresji i rozmiaru ramki (mono/stereo). Upewnij się, że ta prędkość nie jest większa niż wyjściowa prędkość połączenia internetowego (można to sprawdzić np. przez speedtest.net). + + + + If this LED indicator turns red, you will not have much fun using the + Jeśli ta dioda zmieni kolor na czerwony, może być utrudnione używanie + + + + software. + . + + + + ASIO Setup + Ustawienia ASIO + + + + + Mono + Mono + + + + Mono-in/Stereo-out + Mono-in/Stereo-out + + + + + + Stereo + Stereo + + + + The jitter buffer compensates for network and sound card timing jitters. The size of the buffer therefore influences the quality of the audio stream (how many dropouts occur) and the overall delay (the longer the buffer, the higher the delay). + Bufor odchyleń kompensuje opóźnienia sieci i karty dźwiękowej. Rozmiar bufora ma zatem wpływ na jakość strumienia audio (ilość zakłóceń) i ogólne opóźnienie (im większy bufor, tym większe opóźnienie). + + + + You can set the jitter buffer size manually for the local client and the remote server. For the local jitter buffer, dropouts in the audio stream are indicated by the light below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun has taken place and the audio stream is interrupted. + Można ręcznie ustawić rozmiar bufora odchyleń dla klienta lokalnego i zdalnego serwera. W przypadku lokalnego bufora, przerwy w strumieniu audio są sygnalizowane przez diodę poniżej suwaków bufora odchylenia. Zmiana koloru na czerwony, oznacza przekroczenie/zaniżenie bufora i strumień audio zostanie przerwany. + + + + If the Auto setting 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 Auto is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). + Jeśli opcja Auto jest włączona, bufory odchylenia lokalnego klienta i zdalnego serwera są ustawiane automatycznie na podstawie pomiarów czasu trwania odchyleń sieci i karty dźwiękowej. Jeśli opcja Auto jest włączona, bufory są wyłączone (nie można ich przesuwać za pomocą myszy). + + + + If the Auto setting 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 tweak the audio delay/latency it is recommended to disable the Auto setting and to lower the jitter buffer size manually by using the sliders until your personal acceptable amount of dropouts is reached. The LED indicator will display the audio dropouts of the local jitter buffer with a red light. + Jeśli opcja Auto jest włączone, bufory sieciowe klienta lokalnego i zdalnego serwera są ustawione na wartość zachowawczą, aby zminimalizować prawdopodobieństwo przerwania odtwarzania dźwięku. W celu dostrojenia opóźnienia dźwięku zaleca się wyłączenie ustawienia Auto i ręczne zmniejszenie rozmiaru bufora odchyleń za pomocą suwaków do momentu osiągnięcia zadawalającego poziomu zakłóceń. Dioda będzie sygnalizować czerwoną lampką przerwanie odtwarzania dźwięku w lokalnym buforze lokalnym. + + + + The buffer delay setting is a fundamental setting of this software. This setting has an influence on many connection properties. + Ustawienie opóźnienia bufora jest podstawowym ustawieniem tego programu. To ustawienie ma wpływ na wiele właściwości połączenia. + + + + 64 samples: The preferred setting. Provides the lowest latency but does not work with all sound cards. + 64 sample: preferowane ustawienie. Oferuje najniższe opóźnienie ale nie jest obsługiwane przez wszystkie karty dźwiękowe. + + + + 128 samples: Should work for most available sound cards. + 128 sampli: powinno działać na większości z dostępnych kart dźwiękowych. + + + + 256 samples: Should only be used on very slow computers or with a slow internet connection. + 256 sampli: używać tylko na bardzo słabych komputerach lub przy wolnych połączeniach internetowych. + + + + Some sound card drivers do not allow the buffer delay to be changed from within the application. In this case the buffer delay setting is disabled and has to be changed using 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. + Niektóre sterowniki kart dźwiękowych nie pozwalają na zmianę opóźnienia bufora z poziomu aplikacji. W tym przypadku ustawienie opóźnienia bufora jest wyłączone i musi zostać zmienione za pomocą sterownika karty dźwiękowej. W systemie Windows, naciśnij przycisk ASIO Setup, aby otworzyć panel ustawień sterownika. W systemie Linux, użyj programu do konfiguracji serwera JACK i zmiany rozmiaru bufora. + + + + If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The application will still work with this setting but with restricted performance. + Jeżeli nie jest wybrany żaden rozmiar bufora i te ustawienia nie są dostępne, oznacza to, że sterownik używa niewspieranego rozmiaru bufora. Aplikacja będzie nadal działać z tym ustawieniem, ale jej wydajność będzie ograniczona. + + + + If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the software. 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. + Jeśli ustawienia opóźnienia bufora są wyłączone, sterownik audio nie może modyfikować tego ustawienia z poziomu programu. W systemie Windows naciśnij przycisk ASIO Setup, aby otworzyć panel ustawień sterownika. W systemie Linux, użyj narzędzia konfiguracyjnego Jack do zmiany rozmiaru bufora. + + + + Skin + Skórka + + + + Select the skin to be used for the main window. + Wybierz skórkę dla głównego okna aplikacji. + + + + Skin combo box + Lista wyboru skórki + + + + Selects the number of audio channels to be used for communication between client and server. There are three modes available: + Wybiera liczbę kanałów audio, które mają być używane do komunikacji między klientem a serwerem. Dostępne są trzy tryby: + + + + and + i + + + + These modes use one and two audio channels respectively. + Tryby te wykorzystują odpowiednio jeden i dwa kanały audio. + + + + Mono in/Stereo-out + wejście mono/wyjście stereo + + + + The audio signal sent to the server is mono but the return signal is stereo. This is useful if the sound card has the instrument on one input channel and the microphone on the other. In that case the two input signals can be mixed to one mono channel but the server mix is heard in stereo. + Sygnał audio wysyłany do serwera jest mono, ale sygnał zwrotny jest stereo. Jest to przydatne, jeśli karta dźwiękowa ma instrument na jednym kanale wejściowym i mikrofon na drugim. W tym przypadku dwa sygnały wejściowe mogą być miksowane do jednego kanału mono, ale miks serwera jest słyszalny w trybie stereo. + + + + Enabling + Włączanie + + + + In stereo streaming mode, no audio channel selection for the reverb effect will be available on the main window since the effect is applied to both channels in this case. + W trybie strumieniowania stereo w oknie głównym nie będzie dostępny wybór kanału audio dla efektu pogłosu, ponieważ w tym przypadku efekt jest stosowany do obu kanałów. + + + + The higher the audio quality, the higher your audio stream's data rate. Make sure your upload rate does not exceed the available bandwidth of your internet connection. + Im wyższa jakość dźwięku, tym większa szybkość transmisji danych strumienia audio. Upewnij się, że prędkość przesyłania danych nie przekracza dostępnej przepustowości połączenia internetowego. + + + + This setting defines the fader level of a newly connected client in percent. If a new client connects to the current server, they will get the specified initial fader level if no other fader level from a previous connection of that client was already stored. + To ustawienie definiuje poziom sygnału nowo podłączonego klienta w procentach. Jeśli nowy klient połączy się z aktualnym serwerem, otrzyma ten początkowy poziom sygnału, o ile żaden inny poziom z poprzedniego połączenia tego klienta nie był wcześniej zapisany. + + + + Leave this blank unless you need to enter the address of a central server other than the default. + Należy pozostawić to puste pole, chyba że konieczne jest wprowadzenie adresu centralnego serwera innego niż domyślny. + + + + Central server address combo box + Lista wyboru serwera centralnego + + + + The Ping Time is the time required for the audio stream to travel from the client to the server and back again. This delay is introduced by the network and should be about 20-30 ms. If this delay is higher than about 50 ms, your distance to the server is too large or your internet connection is not sufficient. + Czas Ping Time to czas potrzebny do przejścia strumienia audio z klienta do serwera i z powrotem. Opóźnienie to jest wprowadzane przez sieć i powinno wynosić około 20-30 ms. Jeśli opóźnienie to jest większe niż około 50 ms, odległość do serwera jest zbyt duża lub połączenie internetowe nie jest wystarczające. + + + + Overall Delay is calculated from the current Ping Time and the delay introduced by the current buffer settings. + Całkowite opóźnienie jest obliczane na podstawie bieżącego czasu ping i opóźnienia wprowadzonego przez bieżące ustawienia bufora. + + + + Low + Niska + + + + + Normal + Standardowa + + + + High + Wysoka + + + + Custom + Własny + + + + All Genres + Wszystkie style + + + + Genre Rock + Rock + + + + Genre Jazz + Jazz + + + + Genre Classical/Folk/Choral + Klasyka/Folk/Chór + + + + Default + Domyślny + + + + preferred + preferowany + + + + + Size: + Rozmiar: + + + + Buffer Delay + Opóźnienie bufora + + + + Buffer Delay: + Opóźnienie bufora: + + + The selected audio device could not be used because of the following error: + Wybrane urządzenie audio nie mogło być użyte z powodu następującego błędu: + + + The previous driver will be selected. + Został wybrany poprzedni sterownik. + + + Ok + Ok + + + + CClientSettingsDlgBase + + + Settings + Ustawienia + + + + Soundcard + Karta dźwiękowa + + + + Device + Urządzenie + + + + Input Channel Mapping + Mapowanie kanału wejścia + + + + + L + L + + + + + R + P + + + + Output Channel Mapping + Mapowanie kanału wyjścia + + + + Enable Small Network Buffers + Zezwalaj na małe bufory sieciowe + + + + Buffer Delay + Opóźnienie bufora + + + + (preferred) + (preferowane) + + + + (default) + (domyślne) + + + + (safe) + (bezpieczne) + + + + Driver Setup + Konfiguracja sterownika + + + + Jitter Buffer + Bufor odchyleń + + + + Auto + Automatyczny + + + + Local + Lokalny + + + + Server + Serwer + + + + + Size + Rozmiar + + + + Misc + Różne + + + + Audio Channels + Kanały audio + + + + Audio Quality + Jakość audio + + + + New Client Level + Poziom dołączającego się uczestnika + + + + Skin + Skórka + + + + Language + Język + + + + % + % + + + + Custom Central Server Address: + Własny adres serwera centralnego: + + + + Audio Stream Rate + Prędkość strumienia audio + + + + + + val + wartość + + + + Ping Time + Czas odpowiedzi + + + + Overall Delay + Opóźnienie całkowite + + + + CConnectDlg + + + Server List + Lissta serwerów + + + + Server list view + Podgląd listy serwerów + + + + Server Address + Adres serwera + + + + The Connection Setup window shows a list of available servers. Server operators can optionally list their servers by music genre. Use the List dropdown to select a genre, click on the server you want to join and press the Connect button to connect to it. Alternatively, double click on on the server name. Permanent servers (those that have been listed for longer than 48 hours) are shown in bold. + *nie jestem pewna + Okno ustawień połączenia pokazuje listę dostępnych serwerów. Operatorzy serwerów mogą opcjonalnie wyświetlać swoje serwery według gatunków muzycznych. Użyj listy wyboru, aby wybrać gatunek, wybierz serwer, do którego chcesz się połączyć i wciśnij przycisk Połącz. Ewentualnie, podwójne kliknij na nazwę serwera. Stałe serwery (te, które są na liście od ponad 48 godzin) są zaznaczone pogrubioną czcionką. + + + + If you know the IP address or URL of a server, you can connect to it using the Server name/Address field. An optional port number can be added after the IP address or URL using a colon as a separator, e.g, example.org: + Jeżeli znasz adres IP lub URL serwera, możesz się połączyć używając pola Nazwa/Adres Serwera. Opcjonalny numer portu może być dodany po adresie IP lub adresie URL za pomocą dwukropka jako separatora, np. example.org: + + + + . The field will also show a list of the most recently used server addresses. + . Pole pokazuje również listę niedawno używanych serwerów. + + + + Server address edit box + Pole edycji adresu serwera + + + + Holds the current server IP address or URL. It also stores old URLs in the combo box list. + *nie jestem pewna + Zatrzymuje aktualny adres IP lub adres URL serwera. Przechowuje również stare adresy URL na liście. + + + + Server List Selection + *nie jestem pewna + Wybór listy serwerów + + + + Selects the server list to be shown. + *nie jestem pewna + Wybierz listę serwerów, aby rozwinąć. + + + + Server list selection combo box + *nie jestem pewna (server list selection ma podobne tłumaczenie) + Pole wyboru listy serwerów + + + + Filter + Filtr + + + + The server list is filtered by the given text. Note that the filter is case insensitive. + Będą wyświetlane tylko serwery, których nazwy zawierają wprowadzony tekst. Uwaga: filtrowanie nie rozróżnia wielkości liter. + + + + Filter edit box + Pole edycji filtra + + + + Show All Musicians + Pokaż wsztystkich muzyków + + + + + 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. + Jeśli zaznaczysz to pole wyboru, zostaną pokazani muzycy wszystkich serwerów. Jeśli odznaczysz to pole wyboru, wszystkie elementy widoku listy zostaną zwinięte. + + + + Show all musicians check box + *nie jestem pewna + Pokaż wszystkim muzykom pole wyboru + + + + Type # for occupied servers + Wpisz # żeby widzieć zajęte serwery + + + + CConnectDlgBase + + + Connection Setup + Konfiguracja połączenia + + + + List + Lista + + + + Filter + Filtr + + + + Show All Musicians + Pokazuj wszystkich muzyków + + + + Server Name + Nazwa serwera + + + + Ping Time + Czas odpowiedzi + + + + Musicians + Muzycy + + + + Location + Lokalizacja + + + + Server Address + Adres serwera + + + + C&ancel + &Anuluj + + + + &Connect + &Połącz + + + + CHelpMenu + + + &Help + &Pomoc + + + + + Getting &Started... + &Zaczynajmy... + + + + Software &Manual... + &Instrukcja programu... + + + + What's &This + &Co to + + + + &About... + &O programie... + + + + CLanguageComboBox + + + Restart Required + Wymagany restart + + + + Please restart the application for the language change to take effect. + Aby wprowadzić zmianę języka, proszę uruchomić program ponownie. + + + + CLicenceDlg + + + This server requires you accept conditions before you can join. Please read these in the chat window. + Zanim dołączysz, ten serwer wymaga akceptacji warunków korzystania. Proszę je przeczytać w oknie czatu. + + + + I have read the conditions and &agree. + Zapoznałem się z zasadami i &akceptuję. + + + + Accept + Zgadzam się + + + + Decline + Odrzucam + + + + CMultiColorLED + + + Red + Czerwony + + + + Yellow + Żółty + + + + Green + Zielony + + + + CMusProfDlg + + + + Musician Profile + Profil muzyka + + + + Alias/Name + Nick/Imię + + + + Instrument + Instrument + + + + Country + Kraj + + + + City + Miasto + + + + Skill + Umiejętności + + + + &Close + &Zamknij + + + + + + None + Żaden + + + + Beginner + Początkujący + + + + Intermediate + Średniozaawansowany + + + + Expert + Ekspert + + + + Write your name or an alias here so the other musicians you want to play with know who you are. You may also add a picture of the instrument you play and a flag of the country you are located in. Your city and skill level playing your instrument may also be added. + Wpisz swoje imię lub przezwisko, żeby inni muzycy, z którymi chesz grać wiedzieli kto to. Także możesz dodać obrazek instrumentu na którym grasz i flagę kraju i miasto, z którego pochodzisz, a także poziom umiejętności. + + + + What you set here will appear at your fader on the mixer board when you are connected to a Jamulus server. This tag will also be shown at each client which is connected to the same server as you. + Podane tutaj informacje pojawią się przy twoim suwaku na mikserze w czasie połączenia z serwerem Jamulus-a. Taka etykieta pokaże się także u każdego uczestnika podłączonego do tego serwera co ty. + + + + Alias or name edit box + Pole edycji nazwy lub pseudonimu + + + + Instrument picture button + Przycisk wyboru instrumentu + + + + Country flag button + Przycisk wyboru flagi + + + + City edit box + Pole edycji miasta + + + + Skill level combo box + Lista wyboru poziomu umiejętności + + + + Drum Set + Zestaw perkusyjny + + + + Djembe + Djembe + + + + Electric Guitar + Gitara elektryczna + + + + Acoustic Guitar + Gitara akustyczna + + + + Bass Guitar + Gitara basowa + + + + Keyboard + Keyboard + + + + Synthesizer + Syntezator + + + + Grand Piano + Fortepian + + + + Accordion + Akordeon + + + + Vocal + Wokal + + + + Microphone + Mikrofon + + + + Harmonica + Harmonijka ustna + + + + Trumpet + Trąbka + + + + Trombone + Puzon + + + + French Horn + Waltornia + + + + Tuba + Tuba + + + + Saxophone + Saksofon + + + + Clarinet + Klarnet + + + + Flute + Flet + + + + Violin + Skrzypce + + + + Cello + Wiolonczela + + + + Double Bass + Kontrabas + + + + Recorder + Flet prosty + + + + Streamer + Streamer + + + + Listener + Słuchacz + + + + Guitar+Vocal + Gitara+Wokal + + + + Keyboard+Vocal + Keyboard+Wokal + + + + Bodhran + Bodhran + + + + Bassoon + Fagot + + + + Oboe + Obój + + + + Harp + Harfa + + + + Viola + Altówka + + + + Congas + Kongi + + + + Bongo + Bongo + + + + Vocal Bass + Bas + + + + Vocal Tenor + Tenor + + + + Vocal Alto + Alt + + + + Vocal Soprano + Sopran + + + + Banjo + Banjo + + + + Mandolin + Mandolina + + + + Ukulele + Ukulele + + + + Bass Ukulele + Ukulele basowe + + + + Vocal Baritone + Baryton + + + + Vocal Lead + Wokal prowadzący + + + + Mountain Dulcimer + Cymbały górskie + + + + Scratching + Scratch + + + + Rapping + Rapowanie + + + + No Name + Brak nazwy + + + + CServerDlg + + + Client List + Lista użytkowników + + + + 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. + Lista użytkowników wyświetla wszystkich użytkowników obecnie połączonych z tym serwerem. Informacje takie jak IP użytkownika oraz nazwa są wyświetlane dla każdego połączonego użytkownika. + + + + Connected clients list view + Widok listy podłączonych użytkowników + + + + Start Minimized on Operating System Start + Uruchom w tle przy włączeniu systemu operacyjnego + + + + Make My Server Public + Ustaw serwer jako publiczny + + + + Register Server Status + Zarejetruj status serwera + + + + If the Make My Server Public check box is checked, this will show whether registration with the central server is successful. If the registration failed, please choose another server list. + Jeśli pole wyboru Ustaw serwer jako publiczny jest zaznaczone, pokazuje, czy rejestracja na centralnym serwerze zakończyła się sukcesem. Jeśli rejestracja nie powiodła się, wybierz inny serwer z listy. + + + + Custom Central Server Address + Własny adres serwera centralnego + + + + The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. + Niestandardowy adres centralnego serwera to adres IP lub adres URL centralnego serwera, na którym zarządzana jest lista serwerów w oknie dialogowym połączenia. + + + + Central server address line edit + Edycja linii adresowej centralnego serwera + + + + Server List Selection + Wybór listy serwerów + + + + Selects the server list (i.e. central server address) in which your server will be added. + Wybiera adres serwera (np. adres serwera centralnego), na listę którego zostanie dodany nasz serwer. + + + + Server list selection combo box + Pole wyboru listy serwerów + + + + Server Name + Nazwa serwera + + + + Server name line edit + Pole edycji nazwy serwera + + + + Location City + Lokalizacja serwera - miasto + + + + 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. + W tym okne można ustawić nazwę miasta, w którym znajduje się serwer. Jeżeli nazwa miasta jest wprowadzona, zostanie wyświetlona w oknie dialogowym połączenia, na liście dostępnych serwerów. + + + + City where the server is located line edit + Pole wyboru miasta, w którym zlokalizowany jest serwer + + + + Location country + Lokalizacja serwera - kraj + + + + 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. + W tym okne można ustawić nazwę kraju, w którym znajduje się serwer. Jeżeli nazwa kraju jest wprowadzona, zostanie wyświetlona na liście serwerów dialogowych połączeń u klientów. + + + + Country where the server is located combo box + Pole wyboru kraju, w którym zlokalizowany jest serwer + + + + Display dialog to select recording directory button + Wyświetla okno wyboru katalogu nagrywania + + + + + Main Recording Directory + Główny katalog nagrywania + + + + Click the button to open the dialog that allows the main recording directory to be selected.The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). + Kliknij przycisk aby otworzyć okno dialogowe wyboru głownego katalogu nagrywania. Wybrana ścieżka musi istnieć i mieć ustawione prawo do zapisu i tworzenia podkatalogów przez użytkownika uruchamiającego Jamulus-a. + + + + Main recording directory text box (read-only) + Pole tekstowe katalogu nagrywania (tylko do odczytu) + + + + The current value of the main recording directory. The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). Click the button to open the dialog that allows the main recording directory to be selected. + Aktualny głowny katalog nagrywania. Wybrana ścieżka musi istnieć i mieć ustawione prawo do zapisu i tworzenia podkatalogów przez użytkownika uruchamiającego Jamulus-a. Kliknij przycisk aby otworzyć okno dialogowe wyboru głownego katalogu nagrywania. + + + + Clear the recording directory button + Przycisk czyszczenia katalogu nagrywania + + + + Clear Recording Directory + Wyczyść katalog nagrywania + + + + Click the button to clear the currently selected recording directory. This will prevent recording until a new value is selected. + Naciśnij przycisk aby wyczyścić wybraną ścieżkę katalogu. NAgrywanie będzie niemożliwe dopóki nowa ścieżka nie zostanie podana. + + + + Checkbox to turn on or off server recording + Pole wyboru do włączania lub wyłączania nagrywania na serwerze + + + + Enable Recorder + Nagrywanie włączone + + + + Checked when the recorder is enabled, otherwise unchecked. The recorder will run when a session is in progress, if (set up correctly and) enabled. + Zaznaczone kiedy nagrywanie jest możliwe. Nagrywanie rozpocznie się razem z sesją, jeżeli jest ustawione prawidłowo i dostępne. + + + + Current session directory text box (read-only) + Pole tekstowe aktualnego katalogu sesji (tylko do odczytu) + + + + Current Session Directory + Katalog sesji bieżących + + + + Enabled during recording and holds the current recording session directory. Disabled after recording or when the recorder is not enabled. + Dostępne podczas nagrywania, przechowuje ścieżkę do aktualnego katalogu nagrywania. Wyłączone po nagraniu lub gdy rejestrator nie jest włączony. + + + + Recorder status label + Etykieta statusu nagrywania + + + + Recorder Status + Status nagrywania + + + + Select Main Recording Directory + Wybierz katalog nagrywania + + + + ERROR + BŁĄD + + + + Request new recording button + Nowy przycisk nagrywania + + + + New Recording + Nowe nagranie + + + + During a recording session, the button can be used to start a new recording. + Podczas sesji nagraniowej można użyć przycisku w celu rozpoczęcia nowego nagrania. + + + + + E&xit + &Wyjdź + + + + &Hide + &Zminimalizuj + + + + + + server + serwer + + + + &Open + &Otwórz + + + + Recording + Nagrywanie włączone + + + + Not recording + Nagrywanie wyłączone + + + + Not initialised + Nie rozpoczęte + + + + Not enabled + Niedostępny + + + + Server + Serwer + + + + If the start minimized on operating system start check box is checked, the server will be started when the operating system starts up and is automatically minimized to a system task bar icon. + Jeśli pole wyboru start zminimalizowany na starcie systemu operacyjnego jest zaznaczone, serwer zostanie uruchomiony po uruchomieniu systemu operacyjnego i zostanie automatycznie zminimalizowany do ikony paska zadań systemowych. + + + + If the Make My Server Public check box is checked, this server registers itself at the central server so that all users of the application can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. + Jeśli pole wyboru Ustaw serwer jako publiczny jest zaznaczone, serwer ten rejestruje się na centralnym serwerze, tak aby wszyscy użytkownicy aplikacji mogli zobaczyć serwer na liście serwerów połączeń i połączyć się z nim. Rejestracja serwera jest okresowo odnawiana, aby upewnić się, że wszystkie serwery na liście serwerów połączeniowych są rzeczywiście dostępne. + + + + The server name identifies your server in the connect dialog server list at the clients. + Nazwa serwera identyfikuje twój serwer w oknie dialogowym połączenia na liście dostępnych serwerów. + + + + Displays the current status of the recorder. The following values are possible: + Wyświetla aktualny stan nagrywania. Możliwe są następujące wartości: + + + + No recording directory has been set or the value is not useable + Nie wybrano katalogu nagrywania lub nie da się go używać + + + + Recording has been switched off + Nagrywanie zostało wyłączone + + + + by the UI checkbox + w polu wyboru ustawień + + + + , either by the UI checkbox or SIGUSR2 being received + , albo przez pole wyboru lub sygnał SIGUSR2 + + + + There is no one connected to the server to record + Nikt nie jest podłączony do serwera żeby nagrywać + + + + The performers are being recorded to the specified session directory + Wykonawcy są nagrywani do wybranego katalogu + + + + NOTE + UWAGA + + + + If the recording directory is not useable, the problem will be displayed in place of the directory. + Jeżeli nie da się korzystać z katalogu nagrywania, problem będzie wyświetlony zamiast ściżki. + + + + Server welcome message edit box + Pole edycji tekstu powitania na serwerze + + + + Server Welcome Message + Tekst powitania na serwerze + + + + A server welcome message text is displayed in the chat window if a musician enters the server. If no message is set, the server welcome is disabled. + Tekst powitania pojawi się w oknie czatu kidy muzyk połaczy sie z serwerem. Jeżli tekst nie jest podany okno czatu nie zostanie wyświetlone po połączeniu. + + + + Type a message here. If no message is set, the server welcome is disabled. + Wpisz tutaj tekst. Jeżli tekst nie jest podany okno czatu nie zostanie wyświetlone po połączeniu. + + + + software upgrade available + dostępna aktualizacja oprogramowania + + + + &Window + &Okno + + + + Unregistered + Niezarejstrowany + + + + Bad address + Błędny adres + + + + Registration requested + Wymagana rejestracja + + + + Registration failed + Rejestraca nie powiodła się + + + + Check server version + Sprawdź wersję serwera + + + + Registered + Zarejestrowany + + + + Central Server full + Pełny serwer centralny + + + + Your server version is too old + Wersja twojego serwera jest przestarzała + + + + Requirements not fulfilled + Niespełniono wymagań + + + + Unknown value + Nieznana wartość + + + + CServerDlgBase + + + Client IP:Port + IP użytkownika:port + + + + + Name + Nazwa + + + + Jitter Buffer Size + Rozmiar bufora odchyleń + + + + Server Setup + Ustawienia serwera + + + + Genre + Styl + + + + Enable Jam Recorder + Zezwalaj na nagrywanie sesji + + + + New Recording + Nowe nagranie + + + + Chat Window Welcome (HTML/CSS Supported) + Powitanie w oknie czatu (obsługiwane HTML/CSS) + + + + Options + Opcje + + + + Language + Język + + + + Recording Directory + Katalog nagrywania + + + + Start Minimized on Windows Start + Uruchom w tle przy starcie systemu Windows + + + + Update check + Sprawdź uaktualnienia + + + + Make My Server Public (Register My Server in the Server List) + Ustaw mój serwer jako publiczny (zarejestruj na liście) + + + + + STATUS + STATUS + + + + Custom Central Server Address: + Własny adres serwera centralnego: + + + + My Server Info + Informacje o tym serwerze + + + + Location: City + Lokalizacja: Miasto + + + + Location: Country + Lokalizacja: Kraj + + + + CSound + + + The Jack server is not running. This software requires a Jack server to run. Normally if the Jack server is not running this software will automatically start the Jack server. It seems that this auto start has not worked. Try to start the Jack server manually. + Serwer JACK nie jest uruchomiony. Do poprawnego działania, ten program wymaga serwera JACK. Normalnie, jeśli JACK nie jest uruchomiony, Jamulus uruchomi go automatycznie, jednak wygląda na to, że automatyczny start nie działa. Spróbuj uruchomić serwer JACK ręcznie. + + + + The Jack server sample rate is different from the required one. The required sample rate is: + Częstotliwość próbkowania serwera Jack jest inna niż wymagana. Wymagana częstotliwość próbkowania wynosi: + + + + You can use a tool like <i><a href=http://qjackctl.sourceforge.net>QJackCtl</a></i> to adjust the Jack server sample rate. + Możesz użyć programu <i><a href=http://qjackctl.sourceforge.net>QJackCtl</a></i> aby ustawić częstotliwość próbkowania JACK-a. + + + + Make sure to set the Frames/Period to a low value like + Upewnij się, że ramki/okres są ustawione na niską wartość taką jak + + + + to achieve a low delay. + aby uzyskać najniższe opóźnienie. + + + + + The Jack port registering failed. + Rejestracja portu Jack'a nie powiodła się. + + + + Cannot activate the Jack client. + Nie może aktywować klienta Jacka. + + + + The Jack server was shut down. This software requires a Jack server to run. Try to restart the software to solve the issue. + Serwer Jack został wyłączony. Ten program wymaga serwera Jack'a do uruchomienia. Spróbuj zrestartować oprogramowanie, aby rozwiązać problem. + + + + CoreAudio input AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. + CoreAudio wywołanie wejścia AudioHardwareGetProperty nie powiodło się. Wydaje się, że karta dźwiękowa nie jest dostępna w systemie. + + + + CoreAudio output AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. + CoreAudio wywołanie wyjścia AudioHardwareGetProperty nie powiodło się. Wydaje się, że karta dźwiękowa nie jest dostępna w systemie. + + + + Current system audio input device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. + Aktualna systemowa częstotliwość próbkowania urządzenia wejściowego audio %1 Hz nie jest obsługiwana. Proszę otworzyć Audio-MIDI-Setup w Applications->Utilities i spróbować ustawić częstotliwość próbkowania %2 Hz. + + + + + The current selected audio device is no longer present in the system. + + + + + The audio input device is no longer available. + + + + + The audio output device is no longer available. + + + + + Current system audio output device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. + Aktualna systemowa częstotliwość próbkowania urządzenia wyjściowego audio %1 Hz nie jest obsługiwana. Proszę otworzyć Audio-MIDI-Setup w Applications->Utilities i spróbować ustawić częstotliwość próbkowania %2 Hz. + + + + The audio input stream format for this audio device is not compatible with this software. + Format strumienia wejściowego audio dla tego urządzenia audio nie jest kompatybilny z tym oprogramowaniem. + + + + The audio output stream format for this audio device is not compatible with this software. + Format strumienia wyjściowego audio dla tego urządzenia audio nie jest kompatybilny z tym oprogramowaniem. + + + + The buffer sizes of the current input and output audio device cannot be set to a common value. Please choose other input/output audio devices in your system settings. + Rozmiary bufora bieżącego wejściowego i wyjściowego urządzenia audio nie mogą być ustawione na wspólną wartość. Proszę wybrać inne wejściowe/wyjściowe urządzenia audio w ustawieniach systemowych. + + + + The audio driver could not be initialized. + Sterownik audio nie może zostać zainicjalizowany. + + + + The audio device does not support the required sample rate. The required sample rate is: + Urządzenie audio nie obsługuje wymaganej częstotliwości próbkowania. Wymagana częstotliwość próbkowania wynosi: + + + + The audio device does not support setting the required sampling rate. This error can happen if you have an audio interface like the Roland UA-25EX where you set the sample rate with a hardware switch on the audio device. If this is the case, please change the sample rate to + Urządzenie audio nie obsługuje ustawiania wymaganej częstotliwości próbkowania. Błąd ten może się zdarzyć, jeśli posiadasz interfejs audio taki jak Roland UA-25EX, w którym ustawiasz częstotliwość próbkowania za pomocą przełącznika sprzętowego na urządzeniu audio. W takim przypadku należy zmienić częstotliwość próbkowania na + + + + Hz on the device and restart the + Hz na urządzeniu i ponownie uruchom program + + + + software. + program. + + + + The audio device does not support the required number of channels. The required number of channels for input and output is: + Urządzenie audio nie obsługuje wymaganej liczby kanałów. Wymagana liczba kanałów dla wejścia i wyjścia wynosi: + + + + + Required audio sample format not available. + Wymagany format próbkowania audio nie jest dostępny. + + + + No ASIO audio device (driver) found. + Urządzenie (sterownik) ASIO nie został znaleziony. + + + + software requires the low latency audio interface ASIO to work properly. This is not a 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 the ASIO4All driver. + program wymaga do poprawnego działania interfejsu audio ASIO o niskim opóźnieniu. Nie jest to standardowy interfejs systemu Windows i dlatego wymagany jest specjalny sterownik audio. Albo twoja karta dźwiękowa posiada natywny sterownik ASIO (co jest zalecane) lub możesz użyć alternatywnych sterowników, takich jak ASIO4All. + + + + The + + + + + Error closing stream: $s + Błąd podczas zamykania strumienia: $s + + + + CSoundBase + + Invalid device selection. + Wybrano niepoprawne urządzenie. + + + The audio driver properties have changed to a state which is incompatible with this software. The selected audio device could not be used because of the following error: + Właściwości sterownika audio zmieniły i są niekompatybilne z Jamulus-em. Wybrane urządzenie audio nie mogło zostać użyte z powodu następującego błędu: + + + Please restart the software. + Uruchom program ponownie. + + + Close + Zamknij + + + + The selected audio device could not be used because of the following error: + Wybrane urządzenie audio nie mogło być użyte z powodu następującego błędu: + + + + The previous driver will be selected. + Został wybrany poprzedni sterownik. + + + + The previously selected audio device is no longer available or the audio driver properties have changed to a state which is incompatible with this software. We now try to find a valid audio device. This new audio device might cause audio feedback. So, before connecting to a server, please check the audio device setting. + + + + + No usable + Nieużywany + + + + audio device (driver) found. + znaleziono urządzenie dźwiękowe (sterownik). + + + + In the following there is a list of all available drivers with the associated error message: + Poniżej znajduje się lista wszystkich dostępnych sterowników wraz z powiązanymi komunikatami błędów: + + + + Do you want to open the ASIO driver setups? + Czy chcesz otworzyć konfigurację sterownika ASIO? + + + + could not be started because of audio interface issues. + nie mógł być uruchomiony z powodu problemów z interfejsem audio. + + + + QCoreApplication + + + , Version + , Wersja + + + + Internet Jam Session Software + Program do sesji dźwiękowych przez internet + + + + Released under the GNU General Public License (GPL) + Wydane na zasadach licencji GNU General Public License (GPL) + + + + global + + + For more information use the What's This help (help menu, right mouse button or Shift+F1) + Aby uzyskać więcej informacji użyj "Co to" (Pomoc, prawy przycisk myszy lub Shift+F1) + + + diff --git a/src/res/translation/translation_pt_BR.qm b/src/res/translation/translation_pt_BR.qm new file mode 100644 index 0000000000..9b84e007f7 Binary files /dev/null and b/src/res/translation/translation_pt_BR.qm differ diff --git a/src/res/translation/translation_pt_BR.ts b/src/res/translation/translation_pt_BR.ts new file mode 100644 index 0000000000..3191319ee5 --- /dev/null +++ b/src/res/translation/translation_pt_BR.ts @@ -0,0 +1,3446 @@ + + + + + CAboutDlg + + The + O + + + software enables musicians to perform real-time jam sessions over the internet. There is a + permite aos músicos realizar jam sessions em tempo real pela Internet. Existe um servidor + + + software enables musicians to perform real-time jam sessions over the internet. + permite aos músicos realizar jam sessions em tempo real pela Internet. + + + server which collects the audio data from each + que reúne os dados de áudio de cada cliente + + + There is a + Existe um servidor + + + client, mixes the audio data and sends the mix back to each client. + , que mistura os dados de áudio e envia a mistura de volta para cada cliente. + + + uses the following libraries, resources or code snippets: + utiliza as seguintes bibliotecas, recursos ou partes de código: + + + + Qt cross-platform application framework + Estrutura de aplicações multiplataforma Qt + + + + Audio reverberation code by Perry R. Cook and Gary P. Scavone + Código de reverberação de áudio por Perry R. Cook e Gary P. Scavone + + + + Some pixmaps are from the + Alguns pixmaps são do + + + Country flag icons from Mark James + Ícones de bandeira do país de Mark James + + + + This app enables musicians to perform real-time jam sessions over the internet. + Esta aplicação permite aos músicos realizar jam sessions em tempo real pela Internet. + + + + There is a server which collects the audio data from each client, mixes the audio data and sends the mix back to each client. + Existe um servidor que reúne os dados de áudio de cada cliente, mixa e envia a mixagem de volta para cada cliente. + + + + This app uses the following libraries, resources or code snippets: + Esta aplicação utiliza as seguintes bibliotecas, recursos ou partes de código: + + + + Country flag icons by Mark James + Ícones das bandeiras dos países por Mark James + + + + For details on the contributions check out the + Para detalhes sobre as contribuições, consulte a + + + + Github Contributors list + lista de colaboradores do Github + + + + Spanish + Espanhol + + + + French + Francês + + + + Portuguese + Português + + + + Dutch + Holandês + + + + Italian + Italiano + + + + German + Alemão + + + + Polish + Polonês + + + + Swedish + Suíço + + + + Slovak + Eslovaco + + + + About + Sobre o + + + , Version + , Versão + + + Internet Jam Session Software + Programa de Jam Sessions pela Internet + + + Released under the GNU General Public License (GPL) + Lançado sob a Licença Pública Geral GNU (GPL) + + + Under the GNU General Public License (GPL) + Sob a Licença Pública Geral GNU (GPL) + + + + CAboutDlgBase + + + About + Sobre + + + + TextLabelVersion + TextLabelVersion + + + + Copyright (C) 2005-2020 Volker Fischer and others + Copyright (C) 2005-2020 Volker Fischer e outros + + + + A&bout + &Sobre + + + + &Libraries + Should we keep the same shortcut key if possible? + &Bibliotecas + + + + &Contributors + &Colaboradores + + + + &Translation + &Tradução + + + Author: Volker Fischer + Autor: Volker Fischer + + + Copyright (C) 2005-2020 + Copyright (C) 2005-2020 + + + + &OK + &OK + + + + CAnalyzerConsole + + + Analyzer Console + Console de Análise + + + + Error Rate of Each Buffer Size + Taxa de Erros de Cada Tamanho de Buffer + + + + CAudioMixerBoard + + + Personal Mix at the Server + Mixagem Pessoal no Servidor + + + + When connected to a server, the controls here allow you to set your local mix without affecting what others hear from you. The title shows the server name and, when known, whether it is actively recording. + Quando conectado a um servidor, estes controles permite definir sua mixagem local sem afetar o que os outros ouvem de você. O título exibe o nome do servidor e, quando conhecido, se está ativamente gravando. + + + + Server + Servidor + + + + T R Y I N G T O C O N N E C T + T E N T A N D O C O N E C T A R + + + + RECORDING ACTIVE + GRAVAÇÃO ATIVA + + + + Personal Mix at: + Mixagem Pessoal em: + + + + CChannelFader + + + Channel Level + Nível do Canal + + + 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. + Mostra o nível de áudio pré-fader deste canal. Todos os clientes ligados ao servidor terão atribuído um nível de áudio, o mesmo valor para cada cliente. + + + + Input level of the current audio channel at the server + Nível de entrada deste canal de áudio no servidor + + + + Mixer Fader + Fader do Mixer + + + 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. + Ajusta o nível de áudio deste canal. Por cada cliente ligado ao servidor será atribuído um fader de áudio em todos os clientes, podendo cada um ajustar a sua mistura local. + + + + Local mix level setting of the current audio channel at the server + Configuração do nível de mixagem local deste canal de áudio no servidor + + + + Status Indicator + Indicador de Estado + + + + Shows a status indication about the client which is assigned to this channel. Supported indicators are: + Mostra uma indicação de estado do cliente que está atribuído a este canal. Os indicadores suportados são: + + + Speaker with cancellation stroke: Indicates that the other client has muted you. + Alti-falante com sinal de proibição: Indica que o cliente silenciou o teu canal. + + + + Status indicator label + Etiqueta do indicador de estado + + + + Panning + Panorâmica + + + Sets the panning position from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. + Define a posição de panorâmica da esquerda para a direita do canal. Funciona apenas no modo estéreo ou, de preferência, no modo Entrada Mono/Saída Estéreo. + + + + Local panning position of the current audio channel at the server + Posição de panorâmica local deste canal de áudio no servidor + + + + With the Mute checkbox, the audio channel can be muted. + Com a caixa de seleção Mute, o canal de áudio pode ser silenciado. + + + + Mute button + Botão Mute + + + 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. + Com a caixa de seleção Solo, o canal de áudio pode ser definido como solo, o que significa que todos os outros canais, exceto o canal atual, serão silenciados. É possível definir mais que um canal no modo solo. + + + + Solo button + Botão Solo + + + + Fader Tag + Identificador do Fader + + + 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. + O Identificador do fader identifica o cliente ligado. O nome no identificador, a imagem do instrumento e a bandeira do país podem ser definidos no Meu Perfil. + + + + Grp + Grp + + + + Displays the pre-fader audio level of this channel. All clients connected to the server will be assigned an audio level, the same value for every client. + Mostra o nível de áudio pré-fader deste canal. A todos os clientes conectados ao servidor será atribuído um nível de áudio, o mesmo valor para cada cliente. + + + + &No grouping + &Sem grupo + + + + + + + Assign to group + Atribuir ao grupo + + + + Adjusts the audio level of this channel. All clients connected to the server will be assigned an audio fader, displayed at each client, to adjust the local mix. + Ajusta o nível de áudio deste canal. A todos os clientes ligados ao servidor será atribuído um fader de áudio,exibido em cada cliente, para ajustar a mixagem local. + + + + Speaker with cancellation stroke: Indicates that another client has muted you. + Alto-falante com sinal de proibido: Indica que outro cliente silenciou o teu canal. + + + + Sets the pan from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. + Define a posição de panorâmica da esquerda para a direita do canal. Funciona apenas no modo estéreo ou, de preferência, no modo Entrada Mono/Saída Estéreo. + + + + With the Solo checkbox, the audio channel can be set to solo which means that all other channels except the soloed channel are muted. It is possible to set more than one channel to solo. + Com a caixa de seleção Solo, o canal de áudio pode ser definido como solo, o que significa que todos os outros canais, exceto o canal atual, serão silenciados. É possível definir mais que um canal no modo solo. + + + + Group + Grupo + + + + With the Grp checkbox, a group of audio channels can be defined. All channel faders in a group are moved in proportional synchronization if any one of the group faders are moved. + Com a caixa de seleção Grp, um grupo de canais de áudio pode ser definido. Todos os faders de canal de um grupo são movidos em sincronização proporcional se algum dos faders do grupo for movido. + + + + Group button + Botão Grupo + + + + The fader tag identifies the connected client. The tag name, a picture of your instrument and the flag of your country can be set in the main window. + O Identificador do fader identifica o cliente conectado. O nome no identificador, a imagem do instrumento e a bandeira do país podem ser definidos em Meu Perfil. + + + + Mixer channel instrument picture + Imagem do instrumento do canal do mixer + + + + Mixer channel label (fader tag) + Identificação do canal do mixer (identificador do fader) + + + + Mixer channel country flag + Bandeira do país do canal do mixer + + + + PAN + PAN + + + + MUTE + MUTE + + + + SOLO + SOLO + + + + GRP + GRP + + + + M + M + + + + S + S + + + + G + G + + + + Alias/Name + Apelido/Nome + + + + Instrument + Instrumento + + + + Location + Localização + + + + + + Skill Level + Nível de Habilidade + + + + Alias + Apelido + + + + Beginner + Principiante + + + + Intermediate + Intermediário + + + + Expert + Avançado + + + + Musician Profile + Perfil do músico + + + + + + Mute + Mute + + + + + + Pan + Pan + + + + + + Solo + Solo + + + + CChatDlg + + + Chat Window + Janela de Mensagens + + + + The chat window shows a history of all chat messages. + A janela de mensagens mostra um histórico de todas as mensagens enviadas durante a sessão. + + + + Chat history + Histórico de Mensagens + + + + Input Message Text + Texto da Mensagem + + + + 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. + Digite o texto da mensagem no campo de digitação e pressione Enter para enviar a mensagem ao servidor, que distribui a mensagem a todos os clientes conectados. A sua mensagem será então exibida na janela de mensagens. + + + + New chat text edit box + Campo de edição de texto da mensagem + + + + Type a message here + Digite uma mensagem aqui + + + + &Edit + &Editar + + + + Cl&ear Chat History + Didn't include "de Mensagens" in order to keep the menu a bit shorter. + &Limpar Histórico + + + + Do you want to open the link + Você quer abrir o link + + + + in an external browser? + em um navegador externo? + + + + CChatDlgBase + + + Chat + Mensagens + + + + &Send + &Enviar + + + Cl&ear + &Limpar + + + &Close + &Fechar + + + + CClientDlg + + + Input Level Meter + Indicador do Nível de Entrada + + + The input level indicators show the input level of the two stereo channels of the current selected audio input. + Os indicadores do nível de entrada mostram o nível dos dois canais stereo da entrada de áudio selecionada. + + + + Make sure not to clip the input signal to avoid distortions of the audio signal. + Certifique-se de não clipar o sinal de entrada para evitar distorções no sinal de áudio. + + + If the + Se o cliente + + + software, you should not hear your singing/instrument in the loudspeaker or your headphone when the + , não deve ouvir a sua voz/instrumento diretamente nas colunas ou nos headphones enquanto o cliente + + + software is connected and you play your instrument/sing in the microphone, the LED level 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. + estiver ligado a um servidor e tocar o seu instrumento/cantar no microfone, os LEDs do medidor do nível de entrada devem piscar. Se tal não acontecer, provavelmente selecionou o canal de entrada errado (por exemplo, entrada de linha em vez da entrada do microfone) ou ajustou o ganho da entrada muito baixo no misturador de áudio (Windows) ou na placa de som. + + + For a proper usage of the + Para um uso adequado do cliente + + + software is not connected. This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). + não estiver ligado a um servidor. Isso pode ser feito silenciando (mute) o canal da entrada de áudio no dispositivo de reprodução (não no dispositivo de captura!) + + + + Input level meter + Medidor do nível de entrada + + + + Simulates an analog LED level meter. + Simula um medidor de nível analógico LED. + + + + Connect/Disconnect Button + Botão de Conectar/Desconectar + + + Push this button to connect to a server. A dialog where you can select a server will open. If you are connected, pressing this button will end the session. + Pressione este botão para se ligar a um servidor. Uma janela será aberta onde pode selecionar um servidor. Se já estiver ligado a um servidor, pressionar este botão encerrará a sessão. + + + + Connect and disconnect toggle button + Botão de alternação entre ligar e desligar + + + Clicking on this button changes the caption of the button from Connect to Disconnect, i.e., it implements a toggle functionality for connecting and disconnecting the + Clicar nesse botão altera a legenda do botão de Ligar para Desligar, ou seja, implementa uma funcionalidade de alternação para conectar e desconectar o cliente + + + software. + . + + + + Local Audio Input Fader + Fader da Entrada Local de Áudio + + + + Local audio input fader (left/right) + Fader de entrada local de áudio (esquerdo/direito) + + + Reverberation effect level setting + Ajuste do nível do efeito de reverberação + + + Left channel selection for reverberation + Seleção do canal esquerdo para reverberação + + + Right channel selection for reverberation + Seleção do canal direito para reverberação + + + If this LED indicator turns red, you will not have much fun using the + Se este indicador LED ficar vermelho, não se vai divertir muito ao usar o + + + + This shows the level of the two stereo channels for your audio input. + Isto mostra o nível dos dois canais estéreo para a sua entrada de áudio. + + + + If the application is connected to a server and you play your instrument/sing into the microphone, the VU 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. + Se a aplicação estiver conectada a um servidor e você tocar o seu instrumento/cantar no microfone, os LEDs do medidor do nível de entrada devem piscar. Se isso não acontecer, você provavelmente selecionou o canal de entrada errado (Ex.: entrada de linha em vez da entrada do microfone) ou ajustou o ganho da entrada muito baixo no mixer de áudio (Windows) ou na placa de som. + + + + For proper usage of the application, you should not hear your singing/instrument through the loudspeaker or your headphone when the software is not connected.This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). + Para um uso adequado da aplicação, não deve ouvir a sua voz/instrumento diretamente nos alto-falantes ou nos fones enquanto a aplicação não estiver conectada a um servidor. Isso pode ser feito silenciando (mute) o canal da entrada de áudio no dispositivo de reprodução (não no dispositivo de captura!). + + + Clicking on this button changes the caption of the button from Connect to Disconnect, i.e., it implements a toggle functionality for connecting and disconnecting the application. + Clicar nesse botão altera a legenda do botão de Conectar para Desconectar, ou seja, implementa uma funcionalidade de alternação para conectar e desconectar a aplicação. + + + + Controls the relative levels of the left and right local audio channels. For a mono signal it acts as a pan between the two channels.For example, if 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 + Controla os níveis relativos dos canais locais esquerdo e direito. Para um sinal mono, atua como uma panorâmica entre os dois canais. Por exemplo, se um microfone estiver ligado no canal direito e um instrumento estiver ligado no canal esquerdo, mais alto que o microfone, mova o fader de áudio numa direção em que a etiqueta acima do fader mostre + + + + Reverb effect + Efeito de Reverberação + + + + Reverb can be applied to one local mono audio channel or to both channels in stereo mode. The mono channel selection and the reverb level can be modified. For example, if a microphone signal is fed in to the right audio channel of the sound card and a reverb effect needs to be applied, set the channel selector to right and move the fader upwards until the desired reverb level is reached. + O efeito de reverberação pode ser aplicado a um canal local de áudio mono ou a ambos os canais no modo estéreo. A seleção do canal mono e o nível de reverberação podem ser modificados. Por exemplo, se o sinal do microfone for alimentado no canal de áudio direito da placa de som, e for necessário aplicar um efeito de reverberação, ajuste o seletor de canal para a direita e mova o fader para cima até que o nível de reverberação desejado seja atingido. + + + + Reverb effect level setting + Ajuste do nível do efeito de reverberação + + + + Reverb Channel Selection + Seleção do Canal de Reverberação + + + + With these radio buttons the audio input channel on which the reverb effect is applied can be chosen. Either the left or right input channel can be selected. + Com estes botões de seleção, pode ser escolhido o canal de entrada de áudio no qual o efeito de reverberação é aplicado. Pode ser selecionado o canal de entrada esquerdo ou direito. + + + + Left channel selection for reverb + Seleção do canal esquerdo para reverberação + + + + Right channel selection for reverb + Seleção do canal direito para reverberação + + + + Green + Verde + + + + The delay is perfect for a jam session. + A latência é perfeita para uma jam session. + + + + Yellow + Amarelo + + + + Red + Vermelho + + + + Delay status LED indicator + Indicador LED do estado de latência + + + + Opens a dialog where you can select a server to connect to. If you are connected, pressing this button will end the session. + Abre uma caixa de diálogo onde pode selecionar a que servidor conectar-se. Se estiver conectado, pressionar este botão vai terminar a sessão. + + + + Shows the current audio delay status: + Mostra o estado atual da latência de áudio: + + + + A session is still possible but it may be harder to play. + Ainda é possível fazer uma sessão, mas poderá ser mais difícil tocar no tempo. + + + + The delay is too large for jamming. + A latência é demasiada para tocar no tempo. + + + + If this LED indicator turns red, you will not have much fun using the application. + Se este indicador LED ficar vermelho, você não vai divertir-se muito ao usar a aplicação. + + + + The buffers status LED shows the current audio/streaming status. If the light is red, the audio stream is interrupted. This is caused by one of the following problems: + O indicador LED do estado dos buffers mostra o estado atual do áudio/transmissão. Se a luz estiver vermelha, o fluxo de áudio é interrompido. Isto é causado por um dos seguintes problemas: + + + + The sound card's buffer delay (buffer size) is too small (see Settings window). + O buffer (tamanho do buffer) da placa de som é demasiado pequeno (verificar janela das Definições). + + + + The upload or download stream rate is too high for your internet bandwidth. + A taxa de upload ou download é muito elevada para a sua largura de banda da Internet. + + + + Buffers status LED indicator + Indicador LED do estado dos buffers + + + + + C&onnect + C&onectar + + + + software upgrade available + atualização de software disponível + + + + &File + &Arquivo + + + + &View + &Ver + + + + &Connection Setup... + &Conectar a Servidor... + + + + My &Profile... + Meu &Perfil... + + + + C&hat... + &Mensagens... + + + + &Settings... + &Definições... + + + + &Analyzer Console... + Console de &Análise... + + + + Use &Two Rows Mixer Panel + Usar Duas Fileiras para &Painel do Mixer + + + + &Clear All Stored Solo and Mute Settings + Limpar Todas as &Configurações de Solo e Mute + + + + Ok + Ok + + + + E&xit + &Sair + + + + &Edit + &Editar + + + &Sort Users by Name + Ordenar os Canais por &Nome... + + + None + Nenhum + + + + Center + Centro + + + + R + R + + + + + L + L + + + 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 + Com o fader de áudio, os níveis relativos dos canais locais esquerdo e direito podem ser alterados. Para um sinal mono, atua como uma panorâmica entre os dois canais. Se, por exemplo, um microfone estiver ligado no canal direito e um instrumento estiver ligado no canal esquerdo, mais alto que o microfone, mova o fader de áudio numa direção em que a etiqueta acima do fader mostre + + + + , where + , onde + + + + is the current attenuation indicator. + é o indicador de atenuação atual. + + + Reverberation Level + Nível de Reverberação + + + 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. + Um efeito de reverberação pode ser aplicado a um canal local de áudio mono ou a ambos os canais no modo estéreo. A seleção do canal mono e o nível de reverberação podem ser modificados. Se, por exemplo, o sinal do microfone for alimentado no canal de áudio direito da placa de som, e for aplicado um efeito de reverberação, ajuste o seletor de canal para a direita e mova o fader para cima até que o nível de reverberação desejado seja atingido. + + + The reverberation effect requires significant CPU so 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 switched off and does not cause any additional CPU usage. + O efeito de reverberação requer uma utilização do CPU significativa, de forma a que só deve ser usado em PCs rápidos. Se o atenuador do nível de reverberação estiver definido como mínimo (que é a configuração padrão), o efeito de reverberação será desativado e não causará nenhum uso adicional do CPU. + + + Reverberation Channel Selection + Seleção do Canal de Reverberação + + + 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. + Com estes botões de seleção, pode ser escolhido o canal de entrada de áudio no qual o efeito de reverberação é aplicado. Pode ser selecionado o canal de entrada esquerdo ou direito. + + + + Delay Status LED + LED do Estado da Latência + + + 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 large for jamming. + O indicador LED do estado da latência mostra o estado atual do atraso do áudio. Se a luz estiver verde, o atraso é perfeito para uma jam session. Se a luz estiver amarela, uma sessão ainda é possível, mas pode ser mais difícil tocar sincronizado. Se a luz estiver vermelha, o atraso é demasiado grande para uma sessão de jamming. + + + + Buffers Status LED + LED do Estado dos Buffers + + + 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: + O indicador LED do estado dos buffers mostra o estado atual do áudio/transmissão. Se a luz estiver verde, não haverá buffer em excesso/déficit e o fluxo de áudio não será interrompido. Se a luz estiver vermelha, o fluxo de áudio é interrompido devido a um dos seguintes problemas: + + + + The network jitter buffer is not large enough for the current network/audio interface jitter. + O jitter buffer da rede não é grande o suficiente para o jitter atual da interface de rede/áudio. + + + The sound card buffer delay (buffer size) is set to too small a value. + O atraso do buffer da placa de som (buffer size) está definido para um valor demasiado baixo. + + + The upload or download stream rate is too high for the current available internet bandwidth. + A taxa de upload ou download é muito alta para a largura de banda disponível na ligação à Internet. + + + + The CPU of the client or server is at 100%. + O CPU do cliente ou servidor está em 100%. + + + + &Load Mixer Channels Setup... + &Carregar Configuração de Canais do Mixer... + + + + &Save Mixer Channels Setup... + &Salvar Configuração de Canais do Mixer... + + + + N&o User Sorting + S&em Ordenação de Canais + + + + Sort Users by &Name + Ordenar os Canais por &Nome + + + + Sort Users by &Instrument + Ordenar os Canais por &Instrumento + + + + Sort Users by &Group + Ordenar os Canais por &Grupo + + + + Sort Users by &City + Ordenar os Canais por &Cidade + + + &Clear All Stored Solo Settings + &Limpar Todos Ajustes de Solo Armazenados + + + + Set All Faders to New Client &Level + Todos os Faders para Nível de Novo C&liente + + + + Central Server + Servidor Central + + + + + Select Channel Setup File + Selecione Arquivo de Configuraçao de Canal + + + + user + usuário + + + + users + usuários + + + + Your sound card is not working correctly. Please open the settings dialog and check the device selection and the driver settings. + Sua placa de som não está funcionando corretamente. Por favor abra a janela de ajustes e verifique a seleção do dispositivo e as configurações de driver. + + + + D&isconnect + Opted by Desligar instead of Desconectar to keep same keyboard shortcut + Desl&igar + + + + CClientDlgBase + + + Delay + Latência + + + + Buffers + Buffers + + + + Input + Entrada + + + + L + L + + + + R + R + + + + &Mute Myself + Silenciar-&me + + + + &Settings + Definiçõe&s + + + + &Chat + Me&nsagens + + + + C&onnect + C&onectar + + + + Pan + Pan + + + + Center + Centro + + + + Reverb + Reverb + + + + Left + Esquerdo + + + + Right + Direito + + + + MUTED (Other people won't hear you) + SILENCIADO (As pessoas não te ouvirão) + + + + Update check + Verificação de atualização + + + + CClientSettingsDlg + + + Jitter Buffer Size + Tamanho do Jitter Buffer + + + 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). + O jitter buffer (ou buffer de instabilidade) compensa os desvios de temporização da rede e da placa de som. O tamanho desse jitter buffer influencia, portanto, a qualidade do fluxo de áudio (quantas interrupções ocorrem) e a latência geral (quanto maior o buffer, maior a latência). + + + 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 below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun took place and the audio stream is interrupted. + O tamanho do jitter buffer pode ser escolhido manualmente para o cliente local e o servidor remoto. Para o jitter buffer local, as interrupções no fluxo de áudio são indicadas pela luz na parte inferior dos faders do jitter buffer. Se a luz ficar vermelha, ocorreu um excesso/déficit do buffer e o fluxo de áudio é interrompido. + + + + The jitter buffer setting is therefore a trade-off between audio quality and overall delay. + A configuração do jitter buffer é, portanto, uma troca entre a qualidade do áudio e o atraso geral. + + + 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 Auto check is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). + Está disponível uma configuração automática do tamanho do jitter buffer. Se a opção Auto estiver ativada, os jitter buffers do cliente local e do servidor remoto serão configurados automaticamente com base nas medições da instabilidade de sincronização da rede e da placa de som. Se a opção Auto estiver ativada, os faders do jitter buffer serão desativados (não poderão ser movidos manualmente). + + + If 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 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 with a red light. + Caso a configuração automática do jitter buffer estiver ativada, os buffers de rede do cliente local e do servidor remoto são configurados com um valor conservador para minimizar a probabilidade de perda de áudio. Para ajustar o atraso/latência do áudio, é recomendável desativar a funcionalidade de configuração automática e diminuir o tamanho do jitter buffer manualmente usando os controles deslizantes até que a quantidade de perdas de áudio lhe sejam pessoalmente aceitáveis. O indicador LED representará as interrupções de áudio do jitter buffer local através de uma luz vermelha. + + + + Local jitter buffer slider control + Controle deslizante do jitter buffer local + + + + Server jitter buffer slider control + Controle deslizante do jitter buffer do servidor + + + + Auto jitter buffer switch + Interruptor do jitter buffer automático + + + + Jitter buffer status LED indicator + Indicador LED de estado do jitter buffer + + + + Sound Card Device + Dispositivo de Som + + + + The ASIO driver (sound card) can be selected using + O driver ASIO (placa de som) pode ser selecionado usando o + + + + 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. + no Windows. No MacOS/Linux, não é possível seleccionar a placa de som. Se o driver ASIO selecionado não for válido, uma mensagem de erro será exibida e o driver válido anterior será selecionado. + + + + If the driver is selected during an active connection, the connection is stopped, the driver is changed and the connection is started again automatically. + Se o driver for selecionado durante uma sessão ativa, a conexão será interrompida, o driver será alterado e a conexão reiniciada automaticamente. + + + + Sound card device selector combo box + Seletor de dispositivo de som + + + + If 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. + Caso o driver ASIO4ALL seja usado, note que esse driver geralmente introduz aprox. 10-30 ms de atraso de áudio adicional. Dado isto, é recomendável usar uma placa de som com um driver ASIO nativo. + + + + If you are using the kX ASIO driver, make sure to connect the ASIO inputs in the kX DSP settings panel. + Se estiver utilizando o driver kX ASIO, certifique-se de conectar as entradas ASIO no painel de configurações do kX DSP. + + + + Sound Card Channel Mapping + Mapeamento de Canais da Placa de Som + + + + If the selected sound card device offers more than one input or output channel, the Input Channel Mapping and Output Channel Mapping settings are visible. + Caso o dispositivo selecionado da placa de som ofereça mais que um canal de entrada ou saída, as configurações de Mapeamento de canais de entrada e de saída estarão visíveis. + + + + For each + Para cada canal de entrada/saída do + + + + input/output channel (Left and Right channel) a different actual sound card channel can be selected. + (canal esquerdo e direito), um canal real da placa de som pode ser selecionado. + + + + Left input channel selection combo box + Seletor de canal de entrada esquerdo + + + + Right input channel selection combo box + Seletor de canal de entrada direito + + + + Left output channel selection combo box + Seletor de canal de saída esquerdo + + + + Right output channel selection combo box + Seletor de canal de saída direito + + + + Enable Small Network Buffers + Habilitar Buffers de Rede Pequenos + + + + 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 + Se habilitado, o suporte para pacotes de áudio de rede muito pequenos é ativado. Pacotes de rede muito pequenos serão apenas realmente usados se o atraso do buffer da placa de som for menor que + + + + samples. The smaller the network buffers, the lower the audio latency. But at the same time the network load increases and the probability of audio dropouts also increases. + amostras. Quanto menor o buffer da rede, menor a latência do áudio. Mas, ao mesmo tempo, a carga da rede e a probabilidade de interrupção do áudio também aumentam. + + + + Enable small network buffers check box + Caixa de ativação de buffers de rede pequenos + + + + Sound Card Buffer Delay + Atraso do Buffer da Placa de Som + + + The buffer delay setting is a fundamental setting of the + A configuração do atraso do buffer (buffer delay) é uma configuração fundamental do cliente + + + software. This setting has influence on many connection properties. + . Esta configuração tem influência em muitas propriedades da ligação. + + + + Three buffer sizes are supported + Três tamanhos de buffer são suportados + + + 64 samples: This is the preferred setting since it provides the lowest latency but does not work with all sound cards. + 64 amostras: esta é a configuração preferida, pois oferece menor latência, mas não funciona com todas as placas de som. + + + 128 samples: This setting should work for most available sound cards. + 128 amostras: esta configuração deve funcionar na maioria das placas de som disponíveis. + + + 256 samples: This setting should only be used if only a very slow computer or a slow internet connection is available. + 256 amostras: esta configuração deve ser usada se apenas estiver disponível um computador muito lento ou uma ligação lenta à Internet. + + + Some sound card drivers do not allow the buffer delay to be changed from within the + Alguns drivers da placa de som não permitem que o atraso do buffer seja alterado pelo cliente + + + 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. + . Nesse caso, a configuração de atraso do buffer estará desativada. Para alterar o atraso do buffer, essa configuração deve ser alterada no driver da placa de som. No Windows, pressione o botão Configuração do Driver para abrir o painel de configurações do driver. No Linux, use a ferramenta de configuração Jack para alterar o atraso do buffer. + + + If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The + Se nenhum atraso do buffer estiver selecionado e todas as configurações estiverem desativadas, um atraso do buffer não suportado será usado pelo driver. O cliente + + + software will still work with this setting but with restricted performance. + ainda funcionará com essa configuração, mas com desempenho restrito. + + + + 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 a red light in the status indicator (drop outs) and the higher the upload rate and the lower the overall delay. + O atraso do buffer influencia o estado da conexão, a taxa de upload atual e a latência geral. Quanto menor o tamanho do buffer, maior a probabilidade de a luz vermelha no indicador de estado (interrupções), maior a taxa de upload e menor a latência geral. + + + + The buffer setting is therefore a trade-off between audio quality and overall delay. + A configuração do buffer é, portanto, uma troca entre qualidade de áudio e latência geral. + + + If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the + Se as configurações de atraso do buffer estiverem desativadas, é porque o driver de áudio proibe modificar essa configuração a partir do cliente + + + software. 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. + . No Windows, pressione o botão <i>Configuração do Driver</i> para abrir o painel de configurações do driver. No Linux, use a ferramenta de configuração <i>Jack</i> para alterar o atraso do buffer. + + + + 64 samples setting radio button + Botão de configuração de 64 amostras + + + + 128 samples setting radio button + Botão de configuração de 128 amostras + + + + 256 samples setting radio button + Botão de configuração de 256 amostras + + + + ASIO setup push button + Botão de configuração do ASIO + + + Fancy Skin + Skin Sofisticada + + + If enabled, a fancy skin will be applied to the main window. + Se ativada, uma skin sofisticada será aplicada à janela principal. + + + Fancy skin check box + Caixa de ativação da skin sofisticada + + + Display Channel Levels + Mostrar Níveis de Canais + + + If enabled, each client channel will display a pre-fader level bar. + Se habilitado, cada canal de cliente exibirá uma barra de nível pré-fader. + + + Display channel levels check box + Caixa de ativação para exibir níveis de canais + + + + Audio Channels + Canais de Áudio + + + 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 mono-in/stereo-out mode the audio signal which is sent to the server is mono but the return signal is stereo. This is useful if the sound card has 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. + Selecione o número de canais de áudio a serem usados. Existem três modos disponíveis. Os modos mono e estéreo usam um e dois canais de áudio, respectivamente. No modo Entrada Mono/Saída Estéreo, o sinal de áudio enviado ao servidor é mono, mas o sinal de retorno é estéreo. Isso é útil quando a placa de som coloca o instrumento e o microfone em canais diferentes. Nesse caso, os dois sinais de entrada podem ser misturados num canal mono, mas a mistura do servidor pode ser ouvida em estéreo. + + + 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. + Ativar o modo de transmissão estéreo aumenta a taxa do fluxo de dados. Verifique que a taxa de transmissão não excede a largura de banda disponível da sua ligação à Internet. + + + In 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. + No modo de transmissão estéreo, nenhuma seleção de canal de áudio para o efeito de reverberação estará disponível na janela principal, pois o efeito é aplicado em ambos os canais. + + + + Audio channels combo box + Seletor de canais áudio + + + + Audio Quality + Qualidade de Áudio + + + 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 upload rate does not exceed the available bandwidth of your internet connection. + Selecione a qualidade de áudio desejada. Pode ser selecionada uma qualidade de áudio baixa, normal ou alta. Quanto maior a qualidade do áudio, maior a taxa de dados do fluxo de áudio. Verifique que a taxa de transmissão não excede a largura de banda disponível da sua ligação à Internet. + + + + Audio quality combo box + Seletor de qualidade áudio + + + + New Client Level + Nível de Novo Cliente + + + 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 of that client was already stored. + A configuração de nível de novo cliente define, em percentagem, o nível do fader de um novo cliente ligado. Por exemplo, se um cliente novo se ligar ao servidor atual, o seu canal terá o nível inicial do fader especificado, excepto quando um diferente nível do fader de uma ligação anterior desse mesmo cliente já tenha sido definido. + + + + New client level edit box + Caixa de edição no nível de novo cliente + + + + Custom Central Server Address + Endereço de Servidor Central Personalizado + + + The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. This address is only used if the custom server list is selected in the connection dialog. + O endereço personalizado do servidor central é o endereço IP ou URL do servidor central no qual a lista de servidores da Configuração de Ligação é gerida. Este endereço é usado apenas se a lista de servidores personalizados estiver selecionada na Configuração de Ligação. + + + Central Server Address + Endereço do servidor central + + + 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. + O endereço do servidor central é o endereço IP ou URL do servidor central a partir do qual é gerida a lista de servidores do diálogo de ligação. Com a configuração do endereço do servidor central, é possível selecionar um dos servidores centrais padrão ou especificar um endereço manual. + + + Default central server type combo box + Seletor de servidor central padrão + + + Central server address line edit + Caixa de edição do endereço de servidor central + + + + Current Connection Status Parameter + Parâmetros do Estado da Conexão + + + The ping time is the time required for the audio stream to travel from the client to the server and back again. 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. + A latência da ligação é o tempo necessário para o fluxo de áudio viajar do cliente para o servidor e vice-versa. Esta latência é introduzida pela rede. Esta latência deve ser tão baixa quanto 20-30 ms. Se esta latência for maior (por exemplo, 50-60 ms), a distância até ao servidor é muito grande ou sua ligação à Internet não é suficiente. + + + The overall delay is calculated from the current ping time and the delay which is introduced by the current buffer settings. + A latência geral é calculada a partir da latência da ligação atual e do atraso introduzido pelas configurações do buffer. + + + 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 your internet connection by, e.g., using speedtest.net). + A taxa de transmissão depende do tamanho do pacote de áudio e da configuração de compactação de áudio. Verifique se a taxa de transmissão não é maior que a taxa disponível (verifique a taxa de upload da sua ligação à Internet usando, por exemplo, o speedtest.net). + + + + If this LED indicator turns red, you will not have much fun using the + Se este indicador LED ficar vermelho, você não irá divertir-se muito ao usar o + + + + software. + . + + + + ASIO Setup + Configuração ASIO + + + + + Mono + Mono + + + + mode will increase your stream's data rate. Make sure your upload rate does not exceed the available upload speed of your internet connection. + vai aumentar a quantidade de dados da transmissão. Verifique se a taxa de upload não ultrapassa a velocidade de upload disponível da sua conexão à Internet. + + + + Audio Upstream Rate depends on the current audio packet size and compression setting. Make sure that the upstream rate is not higher than your available internet upload speed (check this with a service such as speedtest.net). + A taxa de transmissão do áudio depende do tamanho do pacote de áudio e da configuração de compactação de áudio. Verifique se a taxa de transmissão não é maior que a taxa disponível (verifique a taxa de upload da sua conexão à Internet usando, por exemplo, o speedtest.net). + + + + Mono-in/Stereo-out + Entrada Mono/Saída Estéreo + + + + + + Stereo + Estéreo + + + + The jitter buffer compensates for network and sound card timing jitters. The size of the buffer therefore influences the quality of the audio stream (how many dropouts occur) and the overall delay (the longer the buffer, the higher the delay). + O jitter buffer (ou buffer de instabilidade) compensa os desvios de temporização da rede e da placa de som. O tamanho do buffer influencia, portanto, a qualidade do fluxo de áudio (quantas interrupções ocorrem) e a latência geral (quanto maior o buffer, maior a latência). + + + + You can set the jitter buffer size manually for the local client and the remote server. For the local jitter buffer, dropouts in the audio stream are indicated by the light below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun has taken place and the audio stream is interrupted. + Pode escolher o tamanho do jitter buffer manualmente para o cliente local e o servidor remoto. Para o jitter buffer local, as interrupções no fluxo de áudio são indicadas pela luz na parte inferior dos faders do jitter buffer. Se a luz ficar vermelha, ocorreu um excesso/déficit do buffer e o fluxo de áudio é interrompido. + + + + If the Auto setting 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 Auto is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). + Caso a configuração automática estiver ativada, os buffers de rede do cliente local e do servidor remoto são configurados automaticamente com um valor conservador para minimizar a probabilidade de perda de áudio. Se o modo automático estiver ligado, os controlos estarão desativados (não podem ser alterados pelo usuário). + + + + If the Auto setting 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 tweak the audio delay/latency it is recommended to disable the Auto setting and to lower the jitter buffer size manually by using the sliders until your personal acceptable amount of dropouts is reached. The LED indicator will display the audio dropouts of the local jitter buffer with a red light. + Caso a configuração automática do jitter buffer estiver ativada, os buffers de rede do cliente local e do servidor remoto são configurados com um valor conservador para minimizar a probabilidade de perda de áudio. Para ajustar o atraso/latência do áudio, é recomendável desativar a funcionalidade de configuração automática e diminuir o tamanho do jitter buffer manualmente usando os controles deslizantes até que a quantidade de perdas de áudio lhe sejam pessoalmente aceitáveis. O indicador LED representará as interrupções de áudio do jitter buffer local através de uma luz vermelha. + + + + The buffer delay setting is a fundamental setting of this software. This setting has an influence on many connection properties. + A configuração do atraso do buffer (buffer delay) é uma configuração fundamental da aplicação. Esta configuração tem influência em muitas propriedades da conexão. + + + + 64 samples: The preferred setting. Provides the lowest latency but does not work with all sound cards. + 64 amostras: Configuração preferida. Fornece menor latência, mas não funciona com todas as placas de som. + + + + 128 samples: Should work for most available sound cards. + 128 amostras: Deve funcionar na maioria das placas de som disponíveis. + + + + 256 samples: Should only be used on very slow computers or with a slow internet connection. + 256 amostras: Deve apenas ser usada se tiver um computador muito lento ou uma ligação lenta à Internet. + + + + Some sound card drivers do not allow the buffer delay to be changed from within the application. In this case the buffer delay setting is disabled and has to be changed using 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. + Alguns drivers da placa de som não permitem que o atraso do buffer seja alterado pela aplicação. Nesse caso, a configuração de atraso do buffer estará desativada e deve ser alterada no driver da placa de som. No Windows, pressione o botão Configuração do Driver para abrir o painel de configurações do driver. No Linux, use a ferramenta de configuração Jack para alterar o atraso do buffer. + + + + If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The application will still work with this setting but with restricted performance. + Se nenhum atraso do buffer estiver selecionado e todas as configurações estiverem desativadas, um atraso do buffer não suportado será usado pelo driver. A aplicação ainda funcionará com essa configuração, mas com desempenho restrito. + + + + If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the software. 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. + Se as configurações de atraso do buffer estiverem desativadas, é porque o driver de áudio proibe modificar essa configuração a partir da aplicação. No Windows, pressione o botão Configuração do Driver para abrir o painel de configurações do driver. No Linux, use a ferramenta de configuração Jack para alterar o atraso do buffer. + + + + Skin + Aparência + + + + Select the skin to be used for the main window. + Selecione a aparência para utilizada na janela principal. + + + + Skin combo box + Seletor de aparência + + + + Selects the number of audio channels to be used for communication between client and server. There are three modes available: + Seleciona o número de canais de áudio a serem usados para a comunicação entre cliente e servidor. Existem três modos disponíveis: + + + + and + e + + + + These modes use one and two audio channels respectively. + Estes modos usam um e dois canais de áudio, respectivamente. + + + + Mono in/Stereo-out + Entrada Mono/Saída Estéreo + + + + The audio signal sent to the server is mono but the return signal is stereo. This is useful if the sound card has the instrument on one input channel and the microphone on the other. In that case the two input signals can be mixed to one mono channel but the server mix is heard in stereo. + O sinal de áudio enviado ao servidor é mono, mas o sinal de retorno é estéreo. Isso é útil quando a placa de som coloca o instrumento e o microfone em canais diferentes. Nesse caso, os dois sinais de entrada podem ser misturados num canal mono, mas a mistura do servidor pode ser ouvida em estéreo. + + + + Enabling + Habilitando o modo + + + + In stereo streaming mode, no audio channel selection for the reverb effect will be available on the main window since the effect is applied to both channels in this case. + No modo de transmissão estéreo, nenhuma seleção de canal de áudio para o efeito de reverberação estará disponível na janela principal, pois o efeito é aplicado em ambos os canais. + + + + The higher the audio quality, the higher your audio stream's data rate. Make sure your upload rate does not exceed the available bandwidth of your internet connection. + Quanto maior a qualidade de áudio, maior a quantidade de dados da transmissão. Verifique se a taxa de upload não ultrapassa a velocidade de upload disponível da sua conexão à Internet. + + + + This setting defines the fader level of a newly connected client in percent. If a new client connects to the current server, they will get the specified initial fader level if no other fader level from a previous connection of that client was already stored. + Esta opção define o nível do fader de um cliente novo, em percentagem. Se um cliente novo conecta-se ao mesmo servidor, este irá ter o nível do fader específicado, exceto se já definiu o nível do fader desse cliente a partir de uma conexão anterior que tenha sido guardada. + + + + Leave this blank unless you need to enter the address of a central server other than the default. + Deixe este campo em branco exceto se necessitar de introduzir um endereço alternativo de um servidor central. + + + + Central server address combo box + Caixa do endereço do servidor central + + + + The Ping Time is the time required for the audio stream to travel from the client to the server and back again. This delay is introduced by the network and should be about 20-30 ms. If this delay is higher than about 50 ms, your distance to the server is too large or your internet connection is not sufficient. + A latência da conexão é o tempo necessário para o fluxo de áudio viajar do cliente para o servidor e vice-versa. Esta latência é introduzida pela rede e deve ser cerca de 20-30 ms. Se esta latência for maior que 50 ms, a distância até ao servidor é muito grande ou sua conexão à Internet não é suficiente. + + + + Overall Delay is calculated from the current Ping Time and the delay introduced by the current buffer settings. + A latência geral é calculada a partir da latência da conexão atual e do atraso introduzido pelas configurações de buffer. + + + + Low + Baixa + + + + + Normal + Normal + + + + High + Alta + + + + Fancy + Sofisticada + + + + Compact + Compacta + + + Manual + Manual + + + + Custom + Personalizado + + + + All Genres + Servidor Geral + + + + Genre Rock + Servidor Rock + + + + Genre Jazz + Servidor Jazz + + + Genre Rock/Jazz + Servidor Rock/Jazz + + + + Genre Classical/Folk/Choral + Serv. Clássica/Folclore/Coro + + + + Default + Servidor Padrão + + + Default (North America) + Servidor Padrão (America do Norte) + + + + preferred + preferido + + + + + Size: + Tamanho: + + + + Buffer Delay + Atraso do buffer + + + + Buffer Delay: + Atraso do buffer: + + + The selected audio device could not be used because of the following error: + O dispositivo de áudio selecionado não pôde ser usado devido ao seguinte erro: + + + The previous driver will be selected. + O driver anterior será selecionado. + + + Ok + Ok + + + + CClientSettingsDlgBase + + + Settings + Definições + + + + Soundcard + Placa de Som + + + + Device + Dispositivo + + + + Input Channel Mapping + Mapeamento do Canal de Entrada + + + + + L + L + + + + + R + R + + + + Output Channel Mapping + Mapeamento do Canal de Saída + + + + Enable Small Network Buffers + Habilitar Buffers de Rede Pequenos + + + + Buffer Delay + Atraso do buffer + + + + (preferred) + (preferido) + + + + (default) + (padrão) + + + + (safe) + (seguro) + + + + Driver Setup + Configuração do Driver + + + + Jitter Buffer + Jitter Buffer + + + + Auto + Auto + + + + Local + Local + + + + Server + Servidor + + + + + Size + Tamanho + + + + Misc + Outras Config + + + + Audio Channels + Canais de Áudio + + + + Audio Quality + Qualidade de Áudio + + + + New Client Level + Nível de Novo Cliente + + + + Skin + Aparência + + + + Language + Idioma + + + + % + % + + + Fancy Skin + Skin Sofisticada + + + Display Channel Levels + Mostrar Níveis de Canais + + + + Custom Central Server Address: + Endereço do Servidor Central Personalizado: + + + Central Server Address: + Endereço do Servidor Central: + + + + Audio Stream Rate + Taxa de Transmissão de Áudio + + + + + + val + val + + + + Ping Time + Latência da Ligação + + + + Overall Delay + Latência Geral + + + + CConnectDlg + + + Server List + Lista de servidores + + + 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. + A lista de servidores mostra a os servidores disponíveis registados no servidor central. Selecione um servidor da lista e pressione o botão Ligar para se ligar a este servidor. Como alternativa, clique duas vezes num servidor da lista para se ligar ao mesmo. Se um servidor estiver ocupado, uma lista dos músicos ligados estará disponível expandindo o item da lista. Os servidores permanentes são mostrados em negrito. + + + 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. + Observe que pode demorar algum tempo para obter a lista de servidores do servidor central. Se nenhum endereço de servidor central válido for especificado nas definições, nenhuma lista de servidores estará disponível. + + + + Server list view + Vista da lista de servidores + + + + Server Address + Endereço do servidor + + + The IP address or URL of the server running the + O endereço IP ou URL do servidor executando o servidor + + + 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: + deve ser definido aqui. Um número de porta opcional pode ser adicionado após o endereço IP ou URL usando o caractere dois pontos como separador, por exemplo, example.org: + + + . A list of the most recent used server IP addresses or URLs is available for selection. + . Uma lista dos endereços IP ou URLs dos servidores usados recentemente está disponível para seleção. + + + + The Connection Setup window shows a list of available servers. Server operators can optionally list their servers by music genre. Use the List dropdown to select a genre, click on the server you want to join and press the Connect button to connect to it. Alternatively, double click on on the server name. Permanent servers (those that have been listed for longer than 48 hours) are shown in bold. + A janela Configuração da conexão mostra uma lista de servidores disponíveis. Os operadores dos servidores podem registrar os seus servidores por gênero musical. Utilize o menu Lista para selecionar um gênero, clique no servidor ao qual se deseja ingressar e pressione o botão Conectar. Como alternativa, clique duas vezes no nome do servidor. Servidores permanentes (aqueles que estão registrados há mais de 48 horas) são mostrados em negrito. + + + + If you know the IP address or URL of a server, you can connect to it using the Server name/Address field. An optional port number can be added after the IP address or URL using a colon as a separator, e.g, example.org: + Se souber o endereço IP ou URL de um servidor, pode conectar-se a este utilizando o campo Nome/Endereço do Servidor. Um número de porta opcional pode ser adicionado após o endereço IP ou URL usando o caractere dois pontos como separador, por exemplo, example.org: + + + + . The field will also show a list of the most recently used server addresses. + . Este campo também irá mostrar uma lista dos endereços IP ou URLs dos servidores usados recentemente. + + + + Server address edit box + Caixa de edição do endereço do servidor + + + + Holds the current server IP address or URL. It also stores old URLs in the combo box list. + Contém o endereço IP ou URL do servidor atual. Também armazena URLs antigos na lista do seletor. + + + + Server List Selection + Seleção da Lista de Servidores + + + + Selects the server list to be shown. + Seleciona a lista de servidores a ser apresentada. + + + + Server list selection combo box + Caixa de seleção de lista de servidores + + + + Filter + Filtro + + + + The server list is filtered by the given text. Note that the filter is case insensitive. + A lista de servidores é filtrada pelo texto fornecido. Note que o filtro não diferencia maiúsculas de minúsculas. + + + + Filter edit box + Caixa de edição do filtro + + + + Show All Musicians + Mostrar Todos os Músicos + + + + 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. + Se marcar esta caixa de seleção, os músicos de todos os servidores serão mostrados. Se desmarcar a caixa de seleção, todos os itens em exibição na lista serão recolhidos. + + + + Show all musicians check box + Caixa de seleção para mostrar músicos + + + + Type # for occupied servers + Digite # para servidores ocupados + + + + CConnectDlgBase + + + Connection Setup + Configuração de Conexão + + + + List + Lista + + + + Filter + Filtro + + + + Show All Musicians + Mostrar Todos os Músicos + + + + Server Name + Nome do Servidor + + + + Ping Time + Latência + + + + Musicians + Músicos + + + + Location + Localização + + + + Server Address + Endereço do Servidor + + + + C&ancel + C&ancelar + + + + &Connect + &Conectar + + + + CHelpMenu + + + &Help + &Ajuda + + + + + Getting &Started... + Como Começa&r... + + + + Software &Manual... + &Manual do Programa... + + + + What's &This + O que é &isto + + + + &About... + &Sobre... + + + + CLanguageComboBox + + + Restart Required + É necessário reiniciar + + + + Please restart the application for the language change to take effect. + Reinicie a aplicação para que a alteração de idioma entre em vigor. + + + + CLicenceDlg + + I &agree to the above licence terms + Eu &aceito os termos da licença acima + + + + This server requires you accept conditions before you can join. Please read these in the chat window. + Este servidor requer que você aceite as condições antes de entrar. Por favor leia-as na janela de chat. + + + + I have read the conditions and &agree. + Li as condições e &concordo. + + + + Accept + Aceitar + + + + Decline + Rejeitar + + + By connecting to this server and agreeing to this notice, you agree to the following: + Ao conectar-se a este servidor e concordar com este aviso, você concorda com o seguinte: + + + 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 + Você concorda que todos os dados, sons ou outros trabalhos transmitidos para este servidor pertencem e são criados por você ou por seus licenciadores, e que você está disponibilizando esses dados, sons ou outros trabalhos através da seguinte licença Creative Commons (para obter mais informações sobre esta licença, consulte + + + You are free to: + Você tem o direito de: + + + Share + Compartilhar + + + copy and redistribute the material in any medium or format + copiar e redistribuir o material em qualquer suporte ou formato + + + Adapt + Adaptar + + + remix, transform, and build upon the material + remixar, transformar, e criar a partir do material + + + The licensor cannot revoke these freedoms as long as you follow the license terms. + O licenciante não pode revogar estes direitos desde que você respeite os termos da licença. + + + Under the following terms: + De acordo com os termos seguintes: + + + Attribution + Atribuição + + + You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. + Você deve atribuir o devido crédito, fornecer um link para a licença, e indicar se foram feitas alterações. Você pode fazê-lo de qualquer forma razoável, mas não de uma forma que sugira que o licenciante o apoia ou aprova o seu uso. + + + NonCommercial + NãoComercial + + + You may not use the material for commercial purposes. + Você não pode usar o material para fins comerciais. + + + ShareAlike + CompartilhaIgual + + + If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. + Se você remixar, transformar, ou criar a partir do material, deve distribuir as suas contribuições sob a mesma licença que do original. + + + No additional restrictions + Sem restrições adicionais + + + You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. + Você não pode aplicar termos jurídicos ou medidas de caráter tecnológico que restrinjam legalmente outros de fazerem algo que a licença permita. + + + + CMultiColorLED + + + Red + Vermelho + + + + Yellow + Amarelo + + + + Green + Verde + + + + CMusProfDlg + + server. This tag will also show up at each client which is connected to the same server as you. If the name is left empty, the IP address is shown instead. + . Esta identificação também será exibida em cada cliente ligado ao mesmo servidor que você. Se o nome estiver vazio, o endereço IP será mostrado. + + + + Alias or name edit box + Caixa de edição do nome ou apelido + + + + Instrument picture button + Botão da imagem do instrumento + + + + Country flag button + Botão da bandeira do país + + + + City edit box + Caixa de edição da cidade + + + + Skill level combo box + Seletor do nível de habilidade + + + + + + None + Nenhum + + + + + Musician Profile + Perfil do músico + + + + Alias/Name + Apelido/Nome + + + + Instrument + Instrumento + + + + Country + País + + + + City + Cidade + + + + Skill + Habilidade + + + + &Close + &Fechar + + + + Beginner + Principiante + + + + Intermediate + Intermediário + + + + Expert + Avançado + + + 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 in. The city you live in and the skill level playing your instrument may also be added. + Defina o seu nome ou um pseudônimo aqui para que os outros músicos com quem quer tocar saibam quem você é. Além disso, pode definir uma imagem do instrumento que toca e uma bandeira do país em que vive. A cidade em que vive e o nível de habilidade com o seu instrumento também podem ser adicionados. + + + What you set here will appear at your fader on the mixer board when you are connected to a + O que definir aqui aparecerá por baixo do seu fader na secção de mistura quando estiver ligado a um servidor + + + + Write your name or an alias here so the other musicians you want to play with know who you are. You may also add a picture of the instrument you play and a flag of the country you are located in. Your city and skill level playing your instrument may also be added. + Escreva o seu nome ou um apelido aqui para que os outros músicos com quem quer tocar saibam quem você é. Além disso, pode também definir uma imagem do instrumento que toca e uma bandeira do país onde vive. A cidade onde vive e o nível de habilidade com o seu instrumento também podem ser adicionados. + + + + What you set here will appear at your fader on the mixer board when you are connected to a Jamulus server. This tag will also be shown at each client which is connected to the same server as you. + O que definir aqui aparecerá abaixo do seu fader no mixer quando estiver conectado a um servidor Jamulus. Esta etiqueta também será exibida em cada cliente que estiver conectado ao mesmo servidor. + + + + Drum Set + Bateria + + + + Djembe + Djembe + + + + Electric Guitar + Guitarra Elétrica + + + + Acoustic Guitar + Guitarra Acústica + + + + Bass Guitar + Baixo + + + + Keyboard + Teclado + + + + Synthesizer + Sintetizador + + + + Grand Piano + Piano de Cauda + + + + Accordion + Acordeão + + + + Vocal + Voz + + + + Microphone + Microfone + + + + Harmonica + Harmónica + + + + Trumpet + Trompete + + + + Trombone + Trombone + + + + French Horn + Trompa Francesa + + + + Tuba + Tuba + + + + Saxophone + Saxofone + + + + Clarinet + Clarinete + + + + Flute + Flauta + + + + Violin + Violino + + + + Cello + Violoncelo + + + + Double Bass + Contrabaixo + + + + Recorder + Gravador + + + + Streamer + Streamer + + + + Listener + Ouvinte + + + + Guitar+Vocal + Guitarra+Voz + + + + Keyboard+Vocal + Teclado+Voz + + + + Bodhran + Bodhrán + + + + Bassoon + Fagote + + + + Oboe + Oboé + + + + Harp + Harpa + + + + Viola + Viola de Arco + + + + Congas + Congas + + + + Bongo + Bongo + + + + Vocal Bass + Voz Baixo + + + + Vocal Tenor + Voz Tenor + + + + Vocal Alto + Voz Alto + + + + Vocal Soprano + Voz Soprano + + + + Banjo + Banjo + + + + Mandolin + Bandolim + + + + Ukulele + Ukulele + + + + Bass Ukulele + Ukulele Baixo + + + + Vocal Baritone + Voz Barítono + + + + Vocal Lead + Voz Principal + + + + Mountain Dulcimer + Saltério dos Apalaches + + + + Scratching + Scratching + + + + Rapping + Rap + + + + No Name + Sem Nome + + + + CServerDlg + + + Client List + Lista de Clientes + + + + 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. + A lista de clientes mostra todos os clientes que estão atualmente conectados neste servidor. Algumas informações sobre os clientes, como o endereço IP e o nome, são fornecidas para cada cliente ligado. + + + + Connected clients list view + Lista de clientes conectados + + + + Start Minimized on Operating System Start + Iniciar Minimizado com o Sistema Operacional + + + If the start minimized on operating system start check box is checked, the + Se a caixa de seleção Iniciar Minimizado com o Sistema Operativo estiver marcada, o servidor + + + server will be started when the operating system starts up and is automatically minimized to a system task bar icon. + será iniciado quando o sistema operativo for iniciado, e minimizado automaticamente para um ícone da barra de tarefas do sistema. + + + Show Creative Commons Licence Dialog + Mostrar Diálogo da Licença Creative Commons + + + If enabled, a Creative Commons BY-NC-SA 4.0 Licence dialog is shown each time a new user connects the server. + Se habilitada, uma caixa de diálogo Creative Commons BY-NC-SA 4.0 será exibida sempre que um novo utilizador se ligar ao servidor. + + + + Make My Server Public + Tornar Meu Servidor Público + + + If the Make My Server Public check box is checked, this server registers itself at the central server so that all + Se a caixa de seleção Tornar Servidor Público estiver marcada, esse servidor irá registar-se no servidor central para que todos os utilizadores do + + + users can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. + possam ver o servidor na lista do diálogo de ligação e ligar-se a ele. O registo do servidor é renovado periodicamente para garantir que todos os servidores na lista de diálogo de ligação estejam realmente disponíveis. + + + + Register Server Status + Estado de Registro do Servidor + + + If the Make My Server Public check box is checked, this will show whether registration with the central server is successful. + Se a caixa de seleção Tornar Servidor Público estiver marcada, isto mostrará o sucesso ou insucesso do registo no servidor central. + + + Central Server Address + Endereço do servidor central + + + 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. + O endereço do servidor central é o endereço IP ou o URL do servidor central no qual esse servidor será registado. Com o menu dos servidores centrais, é possível selecionar um dos servidores centrais padrão ou especificar um endereço manual. + + + + If the Make My Server Public check box is checked, this will show whether registration with the central server is successful. If the registration failed, please choose another server list. + Se a caixa de seleção Tornar Meu Servidor Público for selecionada, isto apresentará se o registro no servidor central foi bem-sucedido. Se o registro falhar, escolha outra lista de servidores. + + + Default central server type combo box + Seletor do servidor central padrão + + + + If the start minimized on operating system start check box is checked, the server will be started when the operating system starts up and is automatically minimized to a system task bar icon. + Se a caixa de seleção Iniciar Minimizado com o Sistema Operacional estiver marcada, o servidor será iniciado quando o sistema operacional for iniciado, e minimizado automaticamente para um ícone da barra de tarefas do sistema. + + + + If the Make My Server Public check box is checked, this server registers itself at the central server so that all users of the application can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. + Se a caixa de seleção Tornar Meu Servidor Público estiver marcada, este servidor irá registrar-se no servidor central para que todos os usuários da aplicação possam vê-lo na lista de servidores e conectar-se a ele. O registro dos servidores é renovado periodicamente para garantir que todos os servidores na lista estão realmente disponíveis. + + + + Custom Central Server Address + Endereço do Servidor Central Personalizado + + + + The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. + O endereço do servidor central personalizado é o endereço IP ou URL do servidor central no qual a lista de servidores da Configuração da Conexão é gerida. + + + + Central server address line edit + Caixa de edição do endereço do servidor central + + + + Server List Selection + Seleção da Lista de Servidores + + + + Selects the server list (i.e. central server address) in which your server will be added. + Seleciona a lista de servidores (ou seja, endereço do servidor central) à qual seu servidor será adicionado. + + + + Server list selection combo box + Seletor de lista de servidores + + + + Server Name + Nome do Servidor + + + 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. + O nome do servidor identifica o servidor na lista do diálogo de ligação exibido nos clientes. Se nenhum nome for fornecido, o endereço IP será mostrado. + + + + The server name identifies your server in the connect dialog server list at the clients. + O nome do servidor identifica seu servidor na lista do diálogo de conexão exibido nos clientes. + + + + Server name line edit + Caixa de edição do nome do servidor + + + + Location City + Localização: Cidade + + + + 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. + A cidade onde este servidor está localizado pode ser definida aqui. Se um nome de cidade for inserido, este será mostrado na lista do diálogo de conexão dos clientes. + + + + City where the server is located line edit + Caixa de edição da cidade onde o servidor se encontra + + + + Location country + Localização: País + + + + 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. + O país em que este servidor está localizado pode ser definido aqui. Se um país for inserido, ele será mostrado na lista do diálogo de conexão dos clientes. + + + + Country where the server is located combo box + Seletor do país onde o servidor se encontra + + + + Display dialog to select recording directory button + Botão que mostra diálogo para selecionar diretório de gravação + + + + + Main Recording Directory + Diretório Principal de Gravação + + + + Click the button to open the dialog that allows the main recording directory to be selected.The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). + Clique no botão para abrir a caixa de diálogo que permite selecionar o diretório principal de gravação. O valor escolhido deve existir e ser gravável(permite a criação de subdiretórios pelo usuário que está executando Jamulus). + + + + Main recording directory text box (read-only) + Caixa de texto com o diretório principal de gravação (apenas leitura) + + + + The current value of the main recording directory. The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). Click the button to open the dialog that allows the main recording directory to be selected. + Valor atual do diretório principal de gravação. O valor escolhido deve existir e ser gravável (permite criação de subdiretórios pelo usuário que está executando Jamulus). Clique no botão para abrir a caixa de diálogo que permite selecionar o diretório principal de gravação. + + + + Clear the recording directory button + Botão para limpar o diretório de gravação + + + + Clear Recording Directory + Limpar Diretório de Gravação + + + + Click the button to clear the currently selected recording directory. This will prevent recording until a new value is selected. + Clique no botão para limpar o atual diretório de gravação. Isso impedirá a gravação até que um novo valor seja selecionado. + + + + Checkbox to turn on or off server recording + Caixa de seleção para ligar ou desligar a gravação no servidor + + + + Enable Recorder + Habilitar Gravador + + + + Checked when the recorder is enabled, otherwise unchecked. The recorder will run when a session is in progress, if (set up correctly and) enabled. + Ativo quando o gravador estiver ligado, caso contrário inativo. O gravador irá rodar quando uma sessão estiver a decorrer, se (corretamente configurado e ) ativo. + + + + Current session directory text box (read-only) + Caixa de texto com a pasta da gravação (apenas leitura) + + + + Current Session Directory + Pasta da Sessão Atual + + + + Enabled during recording and holds the current recording session directory. Disabled after recording or when the recorder is not enabled. + Ativo durante a gravação e exibe a pasta da gravação atual. Inativo depois de gravação ou quando o gravador não estiver habilitado. + + + + Recorder status label + Etiqueta do estado do Gravador + + + + Recorder Status + Estado do Gravador + + + + Displays the current status of the recorder. The following values are possible: + Exibe o estado atual do gravador. Os seguintes valores são possíveis: + + + + No recording directory has been set or the value is not useable + Nenhum diretório de gravação definido ou o valor não é utilizável + + + + Recording has been switched off + Gravação foi desligada + + + + by the UI checkbox + pela caixa de ativação da IU + + + + , either by the UI checkbox or SIGUSR2 being received + , pela caixa de ativação da IU ou SIGUSR2 foi recebido + + + + There is no one connected to the server to record + Não há ninguém conectado ao servidor para gravar + + + + The performers are being recorded to the specified session directory + Os artistas estão sendo gravados no diretório de sessão especificado + + + + NOTE + NOTA + + + + If the recording directory is not useable, the problem will be displayed in place of the directory. + Se o diretório de gravação não é utilizável, o problema será exibido no lugar do diretório. + + + + Server welcome message edit box + Caixa de edição da mensagem de boas-vindas do servidor + + + + Server Welcome Message + Mensagem de Boas-vindas do Servidor + + + + A server welcome message text is displayed in the chat window if a musician enters the server. If no message is set, the server welcome is disabled. + Uma mensagem de boas-vindas é exibida na janela de chat se um músico entra no servidor. Se nenhuma mensagem for definida, a mensagem de boas-vindas do servidor é desativada. + + + + Type a message here. If no message is set, the server welcome is disabled. + Digite uma mensagem de boas-vindas aqui. Se nenhuma mensagem for definida, a mensagem de boas-vindas do servidor será desativada. + + + + software upgrade available + atualização de software disponível + + + + ERROR + ERRO + + + Displays the current status of the recorder. + Mostra o estado atual do gravador. + + + + Request new recording button + Botão para começar nova gravação + + + + New Recording + Nova Gravação + + + + During a recording session, the button can be used to start a new recording. + Durante uma sessão de gravação, este botão pode ser usado para começar uma nova gravação. + + + + + E&xit + &Sair + + + + &Hide + &Esconder servidor + + + + + + server + + + + + &Open + &Abrir servidor + + + server + + + + + Select Main Recording Directory + Selecione Diretório Principal de Gravação + + + Predefined Address + Endereço Predefinido + + + + Recording + Gravando + + + + Not recording + Não gravando + + + + Not initialised + Não inicializado + + + + Not enabled + Desativado + + + Manual + Manual + + + Default + Servidor Padrão + + + Default (North America) + Servidor Padrão (America do Norte) + + + + Server + - Servidor + + + + &Window + &Janela + + + + Unregistered + Não Registrado + + + + Bad address + Endereço incorreto + + + + Registration requested + Registro solicitado + + + + Registration failed + Falha no registro + + + + Check server version + Verifique versão do servidor + + + + Registered + Registrado + + + + Central Server full + Servidor Central Cheio + + + + Your server version is too old + A versão do seu servidor está muito desatualizada + + + + Requirements not fulfilled + Requisitos não atendidos + + + + Unknown value + Valor desconhecido + + + + CServerDlgBase + + + Client IP:Port + IP do Cliente:Porta + + + + + Name + Nome do Servidor + + + + Jitter Buffer Size + Tamanho do Jitter Buffer + + + + Server Setup + Configuração do Servidor + + + + Chat Window Welcome (HTML/CSS Supported) + Mensagem de Boas-vindas do Chat (Suporta HTML/CSS) + + + + Options + Opções + + + + Start Minimized on Windows Start + Iniciar Minimizado com o Sistema Operacional + + + Show Creative Commons BY-NC-SA 4.0 Licence Dialog + Mostrar Diálogo da Licença Creative Commons BY-NC-SA 4.0 + + + + Update check + Verificação de atualização + + + + Make My Server Public (Register My Server in the Server List) + Tornar Meu Servidor Público (Registrar na Lista de Servidores) + + + + Genre + + + + + + STATUS + ESTADO + + + + Custom Central Server Address: + Endereço do Servidor Central Personalizado: + + + + Recording Directory + Diretório de Gravação + + + + Enable Jam Recorder + Ativar Gravação da Jam + + + + New Recording + Nova Gravação + + + + Language + Idioma + + + Central Server Address: + Endereço do Servidor Central: + + + + My Server Info + Informação do Meu Servidor + + + + Location: City + Localização: Cidade + + + + Location: Country + Localização: País + + + Enable jam recorder + Habilitar gravação + + + New recording + Nova gravação + + + Recordings folder + Pasta de gravações + + + TextLabelNameVersion + TextLabelNameVersion + + + + CSound + + + The Jack server is not running. This software requires a Jack server to run. Normally if the Jack server is not running this software will automatically start the Jack server. It seems that this auto start has not worked. Try to start the Jack server manually. + O servidor Jack não está em execução. Este programa requer um servidor Jack para ser executado. Normalmente, se o servidor Jack não estiver em execução, este programa iniciará automaticamente o servidor Jack. Parece que esse início automático não funcionou. Tente iniciar o servidor Jack manualmente. + + + + The Jack server sample rate is different from the required one. The required sample rate is: + A taxa de amostragem (sample rate) do servidor Jack é diferente da necessária. A taxa de amostragem necessária é: + + + + You can use a tool like <i><a href=http://qjackctl.sourceforge.net>QJackCtl</a></i> to adjust the Jack server sample rate. + Pode usar uma ferramenta como <i><a href=http://qjackctl.sourceforge.net>QJackCtl</a></i> para ajustar a taxa de amostragem do servidor Jack. + + + + Make sure to set the Frames/Period to a low value like + Certifique-se de definir Quadros/Período para um valor baixo como + + + + to achieve a low delay. + para obter uma latência baixa. + + + + + The Jack port registering failed. + O registro da porta Jack falhou. + + + + Cannot activate the Jack client. + Não é possível ativar o cliente Jack. + + + + The Jack server was shut down. This software requires a Jack server to run. Try to restart the software to solve the issue. + O servidor Jack foi desligado. Este programa requer um servidor Jack para ser executado. Tente reiniciar o programa para resolver o problema. + + + + CoreAudio input AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. + A entrada do CoreAudio falhou na chamada AudioHardwareGetProperty. Parece que nenhuma placa de som está disponível no sistema. + + + + CoreAudio output AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. + A saída do CoreAudio falhou na chamada AudioHardwareGetProperty. Parece que nenhuma placa de som está disponível no sistema. + + + + Current system audio input device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. + A taxa de amostragem (sample rate) de %1 Hz do dispositivo de entrada de áudio atual não é suportada. Por favor, abra o Audio-MIDI-Setup em Aplicativos-> Utilitários e tente definir uma taxa de amostragem de %2 Hz. + + + + + The current selected audio device is no longer present in the system. + O dispositivo de áudio selecionado não está mais presente no sistema. + + + + The audio input device is no longer available. + O dispositivo de entrada de áudio não está mais disponível. + + + + The audio output device is no longer available. + O dispositivo de saída de áudio não está mais disponível. + + + + Current system audio output device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. + A taxa de amostragem (sample rate) de %1 Hz do dispositivo de saída de áudio atual não é suportada. Por favor, abra o Audio-MIDI-Setup em Aplicativos-> Utilitários e tente definir uma taxa de amostragem de %2 Hz. + + + + The audio input stream format for this audio device is not compatible with this software. + O formato do fluxo de entrada de áudio para este dispositivo de áudio não é compatível com este programa. + + + + The audio output stream format for this audio device is not compatible with this software. + O formato do fluxo de saída de áudio para este dispositivo de áudio não é compatível com este programa. + + + + The buffer sizes of the current input and output audio device cannot be set to a common value. Please choose other input/output audio devices in your system settings. + Os tamanhos de buffer do dispositivo de áudio de entrada e saída atual não podem ser definidos para um valor comum. Por favor, escolha outros dispositivos de áudio de entrada/saída nas configurações do seu sistema. + + + + The audio driver could not be initialized. + O driver de áudio não pôde ser inicializado. + + + + The audio device does not support the required sample rate. The required sample rate is: + O dispositivo de áudio não suporta a taxa de amostragem (sample rate) necessária. A taxa de amostragem necessária é: + + + + The audio device does not support setting the required sampling rate. This error can happen if you have an audio interface like the Roland UA-25EX where you set the sample rate with a hardware switch on the audio device. If this is the case, please change the sample rate to + O dispositivo de áudio não suporta definir a taxa de amostragem (sample rate) necessária. Este erro pode ocorrer se você tiver uma interface de áudio como o Roland UA-25EX, onde se define a taxa de amostragem através de um interruptor de hardware no dispositivo de áudio. Se for esse o caso, altere a taxa de amostragem para + + + + Hz on the device and restart the + Hz no dispositivo e reinicie o cliente + + + + software. + . + + + + The audio device does not support the required number of channels. The required number of channels for input and output is: + O dispositivo de áudio não suporta o número necessário de canais. O número necessário de canais para entrada e saída é: + + + + + Required audio sample format not available. + Formato de amostra de áudio necessário não disponível. + + + + No ASIO audio device (driver) found. + Nenhum dispositivo de áudio ASIO (driver) encontrado. + + + + The + O programa + + + + software requires the low latency audio interface ASIO to work properly. This is not a 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 the ASIO4All driver. + requer que a interface de áudio de baixa latência ASIO funcione corretamente. Esta não é uma interface de áudio padrão do Windows e, portanto, é necessário um driver de áudio especial. Ou a sua placa de som possui um driver ASIO nativo (recomendado), ou pode usar drivers alternativos, como o driver ASIO4All. + + + + Error closing stream: $s + Erro ao fechar o stream: $s + + + + CSoundBase + + Invalid device selection. + Seleção de dispositivo inválida. + + + The audio driver properties have changed to a state which is incompatible with this software. The selected audio device could not be used because of the following error: + As propriedades do driver de áudio foram alteradas para um estado incompatível com este programa. O dispositivo de áudio selecionado não pôde ser usado devido ao seguinte erro: + + + Please restart the software. + Por favor reinicie o programa. + + + Close + Fechar + + + + The selected audio device could not be used because of the following error: + O dispositivo de áudio selecionado não pôde ser usado devido ao seguinte erro: + + + + The previous driver will be selected. + O driver anterior será selecionado. + + + + The previously selected audio device is no longer available or the audio driver properties have changed to a state which is incompatible with this software. We now try to find a valid audio device. This new audio device might cause audio feedback. So, before connecting to a server, please check the audio device setting. + O dispositivo de áudio selecionado anteriormente não está mais disponível ou as propriedades do driver de áudio foram alteradas para um estado o qual é incompatível com este software. Tentaremos encontrar um dispositivo de áudio válido. Este novo dispositivo poderá causar realimentação de áudio. Portanto, antes de conectar-se a um servidor, confira as configurações do dispositivo. + + + + No usable + Nenhum dispositivo de áudio (driver) + + + + audio device (driver) found. + utilizável encontrado. + + + + In the following there is a list of all available drivers with the associated error message: + Abaixo uma lista de todos os drivers disponíveis com a mensagem de erro associada: + + + + Do you want to open the ASIO driver setups? + Deseja abrir as configurações do driver ASIO? + + + + could not be started because of audio interface issues. + não pôde ser iniciado devido a problemas na interface de áudio. + + + + QCoreApplication + + + , Version + , Versão + + + + Internet Jam Session Software + Programa de Jam Sessions pela Internet + + + + Released under the GNU General Public License (GPL) + Lançado sob a Licença Pública Geral GNU (GPL) + + + + global + + + For more information use the What's This help (help menu, right mouse button or Shift+F1) + Para mais informações, use O que é isto (menu Ajuda, botão direito do mouse ou Shift + F1) + + + diff --git a/src/res/translation/translation_pt_PT.qm b/src/res/translation/translation_pt_PT.qm index 1ddfcd85c4..ded778a9b5 100644 Binary files a/src/res/translation/translation_pt_PT.qm and b/src/res/translation/translation_pt_PT.qm differ diff --git a/src/res/translation/translation_pt_PT.ts b/src/res/translation/translation_pt_PT.ts index 5c7f5e4d61..f002c0f3f3 100644 --- a/src/res/translation/translation_pt_PT.ts +++ b/src/res/translation/translation_pt_PT.ts @@ -4,118 +4,147 @@ CAboutDlg - The - O + O software enables musicians to perform real-time jam sessions over the internet. There is a permite aos músicos realizar jam sessions em tempo real pela Internet. Existe um servidor - software enables musicians to perform real-time jam sessions over the internet. - permite aos músicos realizar jam sessions em tempo real pela Internet. + permite aos músicos realizar jam sessions em tempo real pela Internet. - server which collects the audio data from each - que reúne os dados de áudio de cada cliente + que reúne os dados de áudio de cada cliente - There is a - Existe um servidor + Existe um servidor - client, mixes the audio data and sends the mix back to each client. - , que mistura os dados de áudio e envia a mistura de volta para cada cliente. + , que mistura os dados de áudio e envia a mistura de volta para cada cliente. - uses the following libraries, resources or code snippets: - utiliza as seguintes bibliotecas, recursos ou partes de código: + utiliza as seguintes bibliotecas, recursos ou partes de código: - + Qt cross-platform application framework Estrutura de aplicações multiplataforma Qt - + Audio reverberation code by Perry R. Cook and Gary P. Scavone Código de reverberação de áudio por Perry R. Cook e Gary P. Scavone - + Some pixmaps are from the Alguns pixmaps são do - Country flag icons from Mark James - Ícones de bandeira do país de Mark James + Ícones de bandeira do país de Mark James - + + This app enables musicians to perform real-time jam sessions over the internet. + Esta aplicação permite aos músicos realizar jam sessions em tempo real pela Internet. + + + + There is a server which collects the audio data from each client, mixes the audio data and sends the mix back to each client. + Existe um servidor que reúne os dados de áudio de cada cliente, mistura os dados de áudio e envia a mistura de volta para cada cliente. + + + + This app uses the following libraries, resources or code snippets: + Esta aplicação utiliza as seguintes bibliotecas, recursos ou partes de código: + + + + Country flag icons by Mark James + Ícones das bandeiras dos países por Mark James + + + For details on the contributions check out the Para detalhes sobre as contribuições, consulte a - + Github Contributors list lista de colaboradores do Github - + Spanish Espanhol - + French Francês - + Portuguese Português - + Dutch Holandês - + Italian - + Italiano - + German Alemão - + + Polish + Polaco + + + + Swedish + Sueco + + + + Slovak + + + + About Sobre o - , Version - , Versão + , Versão - Internet Jam Session Software - Programa de Jam Sessions pela Internet + Programa de Jam Sessions pela Internet + + + Released under the GNU General Public License (GPL) + Lançado sob a Licença Pública Geral GNU (GPL) - Under the GNU General Public License (GPL) - Sob a Licença Pública Geral GNU (GPL) + Sob a Licença Pública Geral GNU (GPL) @@ -172,12 +201,12 @@ CAnalyzerConsole - + Analyzer Console Consola de Análise - + Error Rate of Each Buffer Size Taxa de Erros de Cada Tamanho de Buffer @@ -185,207 +214,305 @@ CAudioMixerBoard - + + Personal Mix at the Server + Mistura Pessoal no Servidor + + + + When connected to a server, the controls here allow you to set your local mix without affecting what others hear from you. The title shows the server name and, when known, whether it is actively recording. + Quando ligado a um servidor, estes controles permitem que defina a sua mistura local sem afectar o que os outros ouvem. O título mostra o nome do servidor e, quando conhecido, se está gravando activamente. + + + Server Servidor - + T R Y I N G T O C O N N E C T T E N T A N D O L I G A R - - Personal Mix at the Server: + + RECORDING ACTIVE + GRAVAÇÃO ACTIVA + + + + Personal Mix at: Mistura Pessoal no Servidor: CChannelFader - + Channel Level Nível do Canal - 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. - Mostra o nível de áudio pré-fader deste canal. Todos os clientes ligados ao servidor terão atribuído um nível de áudio, o mesmo valor para cada cliente. + Mostra o nível de áudio pré-fader deste canal. Todos os clientes ligados ao servidor terão atribuído um nível de áudio, o mesmo valor para cada cliente. - + Input level of the current audio channel at the server Nível de entrada deste canal de áudio do servidor - + Mixer Fader Fader da Mistura - 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. - Ajusta o nível de áudio deste canal. Por cada cliente ligado ao servidor será atribuído um fader de áudio em todos os clientes, podendo cada um ajustar a sua mistura local. + Ajusta o nível de áudio deste canal. Por cada cliente ligado ao servidor será atribuído um fader de áudio em todos os clientes, podendo cada um ajustar a sua mistura local. - + Local mix level setting of the current audio channel at the server Configuração do nível de mistura local deste canal de áudio do servidor - + Status Indicator Indicador de Estado - + Shows a status indication about the client which is assigned to this channel. Supported indicators are: Mostra uma indicação de estado sobre o cliente que está atribuído a este canal. Os indicadores suportados são: - Speaker with cancellation stroke: Indicates that the other client has muted you. - Alti-falante com sinal de proibição: Indica que o cliente silenciou o teu canal. + Alti-falante com sinal de proibição: Indica que o cliente silenciou o teu canal. - + Status indicator label Etiqueta do indicador de estado - + Panning Panorâmica - Sets the panning position from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. - Define a posição de panorâmica da esquerda para a direita do canal. Funciona apenas no modo estéreo ou, de preferência, no modo Entrada Mono/Saída Estéreo. + Define a posição de panorâmica da esquerda para a direita do canal. Funciona apenas no modo estéreo ou, de preferência, no modo Entrada Mono/Saída Estéreo. - + Local panning position of the current audio channel at the server Posição de panorâmica local do canal de áudio actual no servidor - + With the Mute checkbox, the audio channel can be muted. Com a caixa de seleção Mute, o canal de áudio pode ser silenciado. - + Mute button Botão Mute - 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. - Com a caixa de seleção Solo, o canal de áudio pode ser definido como solo, o que significa que todos os outros canais, exceto o canal atual, serão silenciados. É possível definir mais que um canal no modo solo. + Com a caixa de seleção Solo, o canal de áudio pode ser definido como solo, o que significa que todos os outros canais, exceto o canal atual, serão silenciados. É possível definir mais que um canal no modo solo. - + Solo button Botão Solo - + Fader Tag Identificador do Fader - 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. + O Identificador do fader identifica o cliente ligado. O nome no identificador, a imagem do instrumento e a bandeira do país podem ser definidos no Meu Perfil. + + + + Grp + Grp + + + + Displays the pre-fader audio level of this channel. All clients connected to the server will be assigned an audio level, the same value for every client. + Mostra o nível de áudio pré-fader deste canal. A todos os clientes ligados ao servidor será atribuído um nível de áudio, o mesmo valor para cada cliente. + + + + &No grouping + &Sem grupo + + + + + + + Assign to group + Atribuir a grupo + + + + Adjusts the audio level of this channel. All clients connected to the server will be assigned an audio fader, displayed at each client, to adjust the local mix. + Ajusta o nível de áudio deste canal. A todos os clientes ligados ao servidor será atribuído um fader de áudio,exibido em cada cliente, para ajustar a mistura local. + + + + Speaker with cancellation stroke: Indicates that another client has muted you. + Alti-falante com sinal de proibição: Indica que o cliente silenciou o teu canal. + + + + Sets the pan from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. + Define a posição de panorâmica da esquerda para a direita do canal. Funciona apenas no modo estéreo ou, de preferência, no modo Entrada Mono/Saída Estéreo. + + + + With the Solo checkbox, the audio channel can be set to solo which means that all other channels except the soloed channel are muted. It is possible to set more than one channel to solo. + Com a caixa de seleção Solo, o canal de áudio pode ser definido como solo, o que significa que todos os outros canais, exceto o canal atual, serão silenciados. É possível definir mais que um canal no modo solo. + + + + Group + Grupo + + + + With the Grp checkbox, a group of audio channels can be defined. All channel faders in a group are moved in proportional synchronization if any one of the group faders are moved. + Com a caixa de seleção Grp, um grupo de canais de áudio pode ser definido. Todos os faders de canal de um grupo são movidos em sincronização se algum dos faders do grupo for movido. + + + + Group button + Botão de Grupo + + + + The fader tag identifies the connected client. The tag name, a picture of your instrument and the flag of your country can be set in the main window. O Identificador do fader identifica o cliente ligado. O nome no identificador, a imagem do instrumento e a bandeira do país podem ser definidos no Meu Perfil. - + Mixer channel instrument picture Imagem do instrumento do canal da mistura - + Mixer channel label (fader tag) Identificação do canal da mistura (identificador do fader) - + Mixer channel country flag Bandeira do país do canal da mistura - + PAN PAN - + MUTE MUTE - + SOLO SOLO - + + GRP + GRP + + + + M + M + + + + S + S + + + + G + G + + + Alias/Name Nome/Alcunha - + Instrument Instrumento - + Location Localização - - - + + + Skill Level Nível de Habilidade - + + Alias + + + + Beginner Principiante - + Intermediate Intermediário - + Expert Avançado - + Musician Profile Perfil do músico - - + + Mute Mute - + + Pan Pan - - + + Solo Solo @@ -422,66 +549,88 @@ New chat text edit box Campo de edição de texto da mensagem + + + Type a message here + Insira uma mensagem aqui + + + + &Edit + E&ditar + + + + Cl&ear Chat History + &Limpar Histórico + + + + Do you want to open the link + + + + + in an external browser? + + CChatDlgBase - + Chat Mensagens - + + &Send + &Enviar + + Cl&ear - &Limpar + &Limpar - &Close - &Fechar + &Fechar CClientDlg - + Input Level Meter Medidor do Nível de Entrada - The input level indicators show the input level of the two stereo channels of the current selected audio input. - Os indicadores do nível de entrada mostram o nível dos dois canais stereo da entrada de áudio selecionada. + Os indicadores do nível de entrada mostram o nível dos dois canais stereo da entrada de áudio selecionada. - + Make sure not to clip the input signal to avoid distortions of the audio signal. Certifique-se de não clipar o sinal de entrada para evitar distorções no sinal de áudio. - If the - Se o cliente + Se o cliente - software, you should not hear your singing/instrument in the loudspeaker or your headphone when the - , não deve ouvir a sua voz/instrumento diretamente nas colunas ou nos headphones enquanto o cliente + , não deve ouvir a sua voz/instrumento diretamente nas colunas ou nos headphones enquanto o cliente - software is connected and you play your instrument/sing in the microphone, the LED level 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. - estiver ligado a um servidor e tocar o seu instrumento/cantar no microfone, os LEDs do medidor do nível de entrada devem piscar. Se tal não acontecer, provavelmente selecionou o canal de entrada errado (por exemplo, entrada de linha em vez da entrada do microfone) ou ajustou o ganho da entrada muito baixo no misturador de áudio (Windows) ou na placa de som. + estiver ligado a um servidor e tocar o seu instrumento/cantar no microfone, os LEDs do medidor do nível de entrada devem piscar. Se tal não acontecer, provavelmente selecionou o canal de entrada errado (por exemplo, entrada de linha em vez da entrada do microfone) ou ajustou o ganho da entrada muito baixo no misturador de áudio (Windows) ou na placa de som. - For a proper usage of the - Para um uso adequado do cliente + Para um uso adequado do cliente - software is not connected. This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). - não estiver ligado a um servidor. Isso pode ser feito silenciando (mute) o canal da entrada de áudio no dispositivo de reprodução (não no dispositivo de captura!) + não estiver ligado a um servidor. Isso pode ser feito silenciando (mute) o canal da entrada de áudio no dispositivo de reprodução (não no dispositivo de captura!) @@ -499,225 +648,410 @@ Botão de Ligar/Desligar - Push this button to connect to a server. A dialog where you can select a server will open. If you are connected, pressing this button will end the session. - Pressione este botão para se ligar a um servidor. Uma janela será aberta onde pode selecionar um servidor. Se já estiver ligado a um servidor, pressionar este botão encerrará a sessão. + Pressione este botão para se ligar a um servidor. Uma janela será aberta onde pode selecionar um servidor. Se já estiver ligado a um servidor, pressionar este botão encerrará a sessão. - + Connect and disconnect toggle button Botão de alternação entre ligar e desligar - Clicking on this button changes the caption of the button from Connect to Disconnect, i.e., it implements a toggle functionality for connecting and disconnecting the - Clicar nesse botão altera a legenda do botão de Ligar para Desligar, ou seja, implementa uma funcionalidade de alternação para conectar e desconectar o cliente + Clicar nesse botão altera a legenda do botão de Ligar para Desligar, ou seja, implementa uma funcionalidade de alternação para conectar e desconectar o cliente - - software. - . + . - + Local Audio Input Fader Fader da Entrada Local de Áudio - + Local audio input fader (left/right) Fader de entrada local de áudio (esquerdo/direito) - Reverberation effect level setting - Ajuste do nível do efeito de reverberação + Ajuste do nível do efeito de reverberação - Left channel selection for reverberation - Seleção do canal esquerdo para reverberação + Seleção do canal esquerdo para reverberação - Right channel selection for reverberation - Seleção do canal direito para reverberação + Seleção do canal direito para reverberação - If this LED indicator turns red, you will not have much fun using the - Se este indicador LED ficar vermelho, não se vai divertir muito ao usar o + Se este indicador LED ficar vermelho, não se vai divertir muito ao usar o + + + + This shows the level of the two stereo channels for your audio input. + Isto mostra o nível dos dois canais estéreo para a sua entrada de áudio. + + + + If the application is connected to a server and you play your instrument/sing into the microphone, the VU 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. + Se a aplicação estiver ligada a um servidor e tocar o seu instrumento/cantar no microfone, os LEDs do medidor do nível de entrada devem piscar. Se tal não acontecer, provavelmente selecionou o canal de entrada errado (por exemplo, entrada de linha em vez da entrada do microfone) ou ajustou o ganho da entrada muito baixo no misturador de áudio (Windows) ou na placa de som. + + + + For proper usage of the application, you should not hear your singing/instrument through the loudspeaker or your headphone when the software is not connected.This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). + Para um uso adequado da aplicação, não deve ouvir a sua voz/instrumento diretamente nas colunas ou nos headphones enquanto a aplicação não estiver ligada a um servidor. Isso pode ser feito silenciando (mute) o canal da entrada de áudio no dispositivo de reprodução (não no dispositivo de captura!). + + + Clicking on this button changes the caption of the button from Connect to Disconnect, i.e., it implements a toggle functionality for connecting and disconnecting the application. + Clicar nesse botão altera a legenda do botão de Ligar para Desligar, ou seja, implementa uma funcionalidade de alternação para conectar e desconectar a aplicação. + + + + Controls the relative levels of the left and right local audio channels. For a mono signal it acts as a pan between the two channels.For example, if 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 + Controla os níveis relativos dos canais esquerdo e direito. Para um sinal mono, actua como uma panorâmica entre os dois canais. Por exemplo, se um microfone estiver ligado no canal direito e um instrumento estiver ligado no canal esquerdo, mais alto que o microfone, mova o fader de áudio numa direção em que a etiqueta acima do fader mostre + + + + Reverb effect + Efeito de Reverberação + + + + Reverb can be applied to one local mono audio channel or to both channels in stereo mode. The mono channel selection and the reverb level can be modified. For example, if a microphone signal is fed in to the right audio channel of the sound card and a reverb effect needs to be applied, set the channel selector to right and move the fader upwards until the desired reverb level is reached. + O efeito de reverberação pode ser aplicado a um canal local de áudio mono ou a ambos os canais no modo estéreo. A seleção do canal mono e o nível de reverberação podem ser modificados. Por exemplo, se o sinal do microfone for alimentado no canal de áudio direito da placa de som, e for necessário aplicar um efeito de reverberação, ajuste o seletor de canal para a direita e mova o fader para cima até que o nível de reverberação desejado seja atingido. - + + Reverb effect level setting + Ajuste do nível do efeito de reverberação + + + + Reverb Channel Selection + Seleção do Canal de Reverberação + + + + With these radio buttons the audio input channel on which the reverb effect is applied can be chosen. Either the left or right input channel can be selected. + Com estes botões de seleção, pode ser escolhido o canal de entrada de áudio no qual o efeito de reverberação é aplicado. Pode ser selecionado o canal de entrada esquerdo ou direito. + + + + Left channel selection for reverb + Seleção do canal esquerdo para reverberação + + + + Right channel selection for reverb + Seleção do canal direito para reverberação + + + + Green + Verde + + + + The delay is perfect for a jam session. + A latência é perfeita para uma jam session. + + + + Yellow + Amarelo + + + + Red + Vermelho + + + Delay status LED indicator Indicador LED do estado de latência - + + Opens a dialog where you can select a server to connect to. If you are connected, pressing this button will end the session. + Abre uma caixa de diálogo onde pode seleccionar a que servidor se ligar. Se estiver ligado, pressionar este botão vai terminar a sessão. + + + + Shows the current audio delay status: + Mostra o estado actual da latência de áudio: + + + + A session is still possible but it may be harder to play. + Ainda é possível fazer uma sessão, mas poderá ser mais difícil tocar a tempo. + + + + The delay is too large for jamming. + A latência é demasiada para tocar a tempo. + + + + If this LED indicator turns red, you will not have much fun using the application. + Se este indicador LED ficar vermelho, não se vai divertir muito ao usar a aplicação. + + + + The buffers status LED shows the current audio/streaming status. If the light is red, the audio stream is interrupted. This is caused by one of the following problems: + O indicador LED do estado dos buffers mostra o estado atual do áudio/transmissão. Se a luz estiver vermelha, o fluxo de áudio é interrompido. Isto é causado por um dos seguintes problemas: + + + + The sound card's buffer delay (buffer size) is too small (see Settings window). + O buffer (tamanho do buffer) da placa de som é demasiado pequeno (verificar janela das Definições). + + + + The upload or download stream rate is too high for your internet bandwidth. + A taxa de upload ou download é muito elevada para a sua largura de banda da Internet. + + + Buffers status LED indicator Indicador LED do estado dos buffers - - + + C&onnect &Ligar - + + software upgrade available + + + + + &File + &Ficheiro + + + &View &Ver - + &Connection Setup... &Ligar a Servidor... - + My &Profile... Meu &Perfil... - + C&hat... &Mensagens... - + &Settings... &Definições... - + &Analyzer Console... Consola de &Análise... - + + Use &Two Rows Mixer Panel + + + + + &Clear All Stored Solo and Mute Settings + + + + + Ok + Ok + + + E&xit &Sair - + + &Edit + &Editar + + + &Sort Users by Name + Ordenar os Canais por &Nome... + + None - Nenhum + Nenhum - + Center Centro - + R R - - + + L L - 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 - Com o fader de áudio, os níveis relativos dos canais locais esquerdo e direito podem ser alterados. Para um sinal mono, atua como uma panorâmica entre os dois canais. Se, por exemplo, um microfone estiver ligado no canal direito e um instrumento estiver ligado no canal esquerdo, mais alto que o microfone, mova o fader de áudio numa direção em que a etiqueta acima do fader mostre + Com o fader de áudio, os níveis relativos dos canais locais esquerdo e direito podem ser alterados. Para um sinal mono, atua como uma panorâmica entre os dois canais. Se, por exemplo, um microfone estiver ligado no canal direito e um instrumento estiver ligado no canal esquerdo, mais alto que o microfone, mova o fader de áudio numa direção em que a etiqueta acima do fader mostre - + , where , onde - + is the current attenuation indicator. é o indicador de atenuação atual. - Reverberation Level - Nível de Reverberação + Nível de Reverberação - 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. - Um efeito de reverberação pode ser aplicado a um canal local de áudio mono ou a ambos os canais no modo estéreo. A seleção do canal mono e o nível de reverberação podem ser modificados. Se, por exemplo, o sinal do microfone for alimentado no canal de áudio direito da placa de som, e for aplicado um efeito de reverberação, ajuste o seletor de canal para a direita e mova o fader para cima até que o nível de reverberação desejado seja atingido. + Um efeito de reverberação pode ser aplicado a um canal local de áudio mono ou a ambos os canais no modo estéreo. A seleção do canal mono e o nível de reverberação podem ser modificados. Se, por exemplo, o sinal do microfone for alimentado no canal de áudio direito da placa de som, e for aplicado um efeito de reverberação, ajuste o seletor de canal para a direita e mova o fader para cima até que o nível de reverberação desejado seja atingido. - The reverberation effect requires significant CPU so 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 switched off and does not cause any additional CPU usage. - O efeito de reverberação requer uma utilização do CPU significativa, de forma a que só deve ser usado em PCs rápidos. Se o atenuador do nível de reverberação estiver definido como mínimo (que é a configuração padrão), o efeito de reverberação será desativado e não causará nenhum uso adicional do CPU. + O efeito de reverberação requer uma utilização do CPU significativa, de forma a que só deve ser usado em PCs rápidos. Se o atenuador do nível de reverberação estiver definido como mínimo (que é a configuração padrão), o efeito de reverberação será desativado e não causará nenhum uso adicional do CPU. - Reverberation Channel Selection - Seleção do Canal de Reverberação + Seleção do Canal de Reverberação - 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. - Com estes botões de seleção, pode ser escolhido o canal de entrada de áudio no qual o efeito de reverberação é aplicado. Pode ser selecionado o canal de entrada esquerdo ou direito. + Com estes botões de seleção, pode ser escolhido o canal de entrada de áudio no qual o efeito de reverberação é aplicado. Pode ser selecionado o canal de entrada esquerdo ou direito. - + Delay Status LED LED do Estado da Latência - 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 large for jamming. - O indicador LED do estado da latência mostra o estado atual do atraso do áudio. Se a luz estiver verde, o atraso é perfeito para uma jam session. Se a luz estiver amarela, uma sessão ainda é possível, mas pode ser mais difícil tocar sincronizado. Se a luz estiver vermelha, o atraso é demasiado grande para uma sessão de jamming. + O indicador LED do estado da latência mostra o estado atual do atraso do áudio. Se a luz estiver verde, o atraso é perfeito para uma jam session. Se a luz estiver amarela, uma sessão ainda é possível, mas pode ser mais difícil tocar sincronizado. Se a luz estiver vermelha, o atraso é demasiado grande para uma sessão de jamming. - + Buffers Status LED LED do Estado dos Buffers - 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: - O indicador LED do estado dos buffers mostra o estado atual do áudio/transmissão. Se a luz estiver verde, não haverá buffer em excesso/déficit e o fluxo de áudio não será interrompido. Se a luz estiver vermelha, o fluxo de áudio é interrompido devido a um dos seguintes problemas: + O indicador LED do estado dos buffers mostra o estado atual do áudio/transmissão. Se a luz estiver verde, não haverá buffer em excesso/déficit e o fluxo de áudio não será interrompido. Se a luz estiver vermelha, o fluxo de áudio é interrompido devido a um dos seguintes problemas: - + The network jitter buffer is not large enough for the current network/audio interface jitter. O jitter buffer da rede não é grande o suficiente para o jitter atual da interface de rede/áudio. - The sound card buffer delay (buffer size) is set to too small a value. - O atraso do buffer da placa de som (buffer size) está definido para um valor demasiado baixo. + O atraso do buffer da placa de som (buffer size) está definido para um valor demasiado baixo. - The upload or download stream rate is too high for the current available internet bandwidth. - A taxa de upload ou download é muito alta para a largura de banda disponível na ligação à Internet. + A taxa de upload ou download é muito alta para a largura de banda disponível na ligação à Internet. - + The CPU of the client or server is at 100%. O CPU do cliente ou servidor está a 100%. - + + &Load Mixer Channels Setup... + A&brir configuração da mistura... + + + + &Save Mixer Channels Setup... + Salvar &configuração da mistura... + + + + N&o User Sorting + + + + + Sort Users by &Name + Ordenar Utilizadores por &Nome + + + + Sort Users by &Instrument + Ordenar canais por &Instrumento + + + + Sort Users by &Group + Ordenar canais por &Grupo + + + + Sort Users by &City + + + + + Set All Faders to New Client &Level + + + + Central Server Servidor Central - + + + Select Channel Setup File + Selecione o ficheiro de configuração da mistura + + + user utilizador - + users utilizadores - + + Your sound card is not working correctly. Please open the settings dialog and check the device selection and the driver settings. + + + + D&isconnect Desl&igar @@ -725,367 +1059,353 @@ CClientDlgBase - + Delay Latência - + Buffers Buffers - + Input Entrada - + L L - + R R - - Settings - Definições + + &Mute Myself + Silenciar-&me - - Chat - Mensagens + + &Settings + Definiçõe&s - - Mute Myself - Silenciar-me + + &Chat + Me&nsagens - + C&onnect &Ligar - + Pan Pan - + Center Centro - + Reverb - Reverb. + Reverb - + Left Esquerdo - + Right Direito + + + MUTED (Other people won't hear you) + MUDO (Outras pessoas não o vão ouvir) + + + + Update check + + CClientSettingsDlg - + Jitter Buffer Size Tamanho do Jitter Buffer - 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). - O jitter buffer (ou buffer de instabilidade) compensa os desvios de temporização da rede e da placa de som. O tamanho desse jitter buffer influencia, portanto, a qualidade do fluxo de áudio (quantas interrupções ocorrem) e a latência geral (quanto maior o buffer, maior a latência). + O jitter buffer (ou buffer de instabilidade) compensa os desvios de temporização da rede e da placa de som. O tamanho desse jitter buffer influencia, portanto, a qualidade do fluxo de áudio (quantas interrupções ocorrem) e a latência geral (quanto maior o buffer, maior a latência). - 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 below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun took place and the audio stream is interrupted. - O tamanho do jitter buffer pode ser escolhido manualmente para o cliente local e o servidor remoto. Para o jitter buffer local, as interrupções no fluxo de áudio são indicadas pela luz na parte inferior dos faders do jitter buffer. Se a luz ficar vermelha, ocorreu um excesso/déficit do buffer e o fluxo de áudio é interrompido. + O tamanho do jitter buffer pode ser escolhido manualmente para o cliente local e o servidor remoto. Para o jitter buffer local, as interrupções no fluxo de áudio são indicadas pela luz na parte inferior dos faders do jitter buffer. Se a luz ficar vermelha, ocorreu um excesso/déficit do buffer e o fluxo de áudio é interrompido. - + The jitter buffer setting is therefore a trade-off between audio quality and overall delay. A configuração do jitter buffer é, portanto, uma troca entre a qualidade do áudio e o atraso geral. - 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 Auto check is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). - Está disponível uma configuração automática do tamanho do jitter buffer. Se a opção Auto estiver ativada, os jitter buffers do cliente local e do servidor remoto serão configurados automaticamente com base nas medições da instabilidade de sincronização da rede e da placa de som. Se a opção Auto estiver ativada, os faders do jitter buffer serão desativados (não poderão ser movidos manualmente). + Está disponível uma configuração automática do tamanho do jitter buffer. Se a opção Auto estiver ativada, os jitter buffers do cliente local e do servidor remoto serão configurados automaticamente com base nas medições da instabilidade de sincronização da rede e da placa de som. Se a opção Auto estiver ativada, os faders do jitter buffer serão desativados (não poderão ser movidos manualmente). - If 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 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 with a red light. - Caso a configuração automática do jitter buffer estiver ativada, os buffers de rede do cliente local e do servidor remoto são configurados com um valor conservador para minimizar a probabilidade de perda de áudio. Para ajustar o atraso/latência do áudio, é recomendável desativar a funcionalidade de configuração automática e diminuir o tamanho do jitter buffer manualmente usando os controles deslizantes até que a quantidade de perdas de áudio lhe sejam pessoalmente aceitáveis. O indicador LED representará as interrupções de áudio do jitter buffer local através de uma luz vermelha. + Caso a configuração automática do jitter buffer estiver ativada, os buffers de rede do cliente local e do servidor remoto são configurados com um valor conservador para minimizar a probabilidade de perda de áudio. Para ajustar o atraso/latência do áudio, é recomendável desativar a funcionalidade de configuração automática e diminuir o tamanho do jitter buffer manualmente usando os controles deslizantes até que a quantidade de perdas de áudio lhe sejam pessoalmente aceitáveis. O indicador LED representará as interrupções de áudio do jitter buffer local através de uma luz vermelha. - + Local jitter buffer slider control Controle deslizante do jitter buffer local - + Server jitter buffer slider control Controle deslizante do jitter buffer do servidor - + Auto jitter buffer switch Interruptor do jitter buffer automático - + Jitter buffer status LED indicator Indicador LED de estado do jitter buffer - + Sound Card Device Dispositivo da Placa de Som - + The ASIO driver (sound card) can be selected using O driver ASIO (placa de som) pode ser selecionado usando o - + 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. no Windows. No MacOS/Linux, não é possível seleccionar a placa de som. Se o driver ASIO selecionado não for válido, uma mensagem de erro será exibida e o driver válido anterior será selecionado. - + If the driver is selected during an active connection, the connection is stopped, the driver is changed and the connection is started again automatically. Se o driver for selecionado durante uma ligação ativa, a ligação será interrompida, o driver será alterado e a ligação reiniciada automaticamente. - + Sound card device selector combo box Seletor de dispositivo da placa de som - + If 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. Caso o driver ASIO4ALL seja usado, note que esse driver geralmente introduz aprox. 10-30 ms de atraso de áudio adicional. Dado isto, é recomendável usar uma placa de som com um driver ASIO nativo. - + If you are using the kX ASIO driver, make sure to connect the ASIO inputs in the kX DSP settings panel. Se estiver a usar o driver kX ASIO, certifique-se de ligar as entradas ASIO no painel de configurações do kX DSP. - + Sound Card Channel Mapping Mapeamento de Canais da Placa de Som - + If the selected sound card device offers more than one input or output channel, the Input Channel Mapping and Output Channel Mapping settings are visible. Caso o dispositivo selecionado da placa de som ofereça mais que um canal de entrada ou saída, as configurações de Mapeamento de canais de entrada e de saída estarão visíveis. - + For each Para cada canal de entrada/saída do - + input/output channel (Left and Right channel) a different actual sound card channel can be selected. (canal esquerdo e direito), um canal real da placa de som pode ser selecionado. - + Left input channel selection combo box Seletor de canal de entrada esquerdo - + Right input channel selection combo box Seletor de canal de entrada direito - + Left output channel selection combo box Seletor de canal de saída esquerdo - + Right output channel selection combo box Seletor de canal de saída direito - + Enable Small Network Buffers Activar Buffers de Rede Pequenos - + 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 Se ativado, o suporte para pacotes de áudio de rede muito pequenos é ativado. Pacotes de rede muito pequenos serão apenas realmente usados se o atraso do buffer da placa de som for menor que - + samples. The smaller the network buffers, the lower the audio latency. But at the same time the network load increases and the probability of audio dropouts also increases. amostras. Quanto menor o buffer da rede, menor a latência do áudio. Mas, ao mesmo tempo, a carga da rede e a probabilidade de interrupção do áudio também aumentam. - + Enable small network buffers check box Caixa de activação de buffers de rede pequenos - + Sound Card Buffer Delay Atraso do Buffer da Placa de Som - The buffer delay setting is a fundamental setting of the - A configuração do atraso do buffer (buffer delay) é uma configuração fundamental do cliente + A configuração do atraso do buffer (buffer delay) é uma configuração fundamental do cliente - software. This setting has influence on many connection properties. - . Esta configuração tem influência em muitas propriedades da ligação. + . Esta configuração tem influência em muitas propriedades da ligação. - + Three buffer sizes are supported Três tamanhos de buffer são suportados - 64 samples: This is the preferred setting since it provides the lowest latency but does not work with all sound cards. - 64 amostras: esta é a configuração preferida, pois oferece menor latência, mas não funciona com todas as placas de som. + 64 amostras: esta é a configuração preferida, pois oferece menor latência, mas não funciona com todas as placas de som. - 128 samples: This setting should work for most available sound cards. - 128 amostras: esta configuração deve funcionar na maioria das placas de som disponíveis. + 128 amostras: esta configuração deve funcionar na maioria das placas de som disponíveis. - 256 samples: This setting should only be used if only a very slow computer or a slow internet connection is available. - 256 amostras: esta configuração deve ser usada se apenas estiver disponível um computador muito lento ou uma ligação lenta à Internet. + 256 amostras: esta configuração deve ser usada se apenas estiver disponível um computador muito lento ou uma ligação lenta à Internet. - Some sound card drivers do not allow the buffer delay to be changed from within the - Alguns drivers da placa de som não permitem que o atraso do buffer seja alterado pelo cliente + Alguns drivers da placa de som não permitem que o atraso do buffer seja alterado pelo cliente - 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. - . Nesse caso, a configuração de atraso do buffer estará desativada. Para alterar o atraso do buffer, essa configuração deve ser alterada no driver da placa de som. No Windows, pressione o botão Configuração do Driver para abrir o painel de configurações do driver. No Linux, use a ferramenta de configuração Jack para alterar o atraso do buffer. + . Nesse caso, a configuração de atraso do buffer estará desativada. Para alterar o atraso do buffer, essa configuração deve ser alterada no driver da placa de som. No Windows, pressione o botão Configuração do Driver para abrir o painel de configurações do driver. No Linux, use a ferramenta de configuração Jack para alterar o atraso do buffer. - If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The - Se nenhum atraso do buffer estiver selecionado e todas as configurações estiverem desativadas, um atraso do buffer não suportado será usado pelo driver. O cliente + Se nenhum atraso do buffer estiver selecionado e todas as configurações estiverem desativadas, um atraso do buffer não suportado será usado pelo driver. O cliente - software will still work with this setting but with restricted performance. - ainda funcionará com essa configuração, mas com desempenho restrito. + ainda funcionará com essa configuração, mas com desempenho restrito. - + 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 a red light in the status indicator (drop outs) and the higher the upload rate and the lower the overall delay. O atraso do buffer influencia o estado da ligação, a taxa de upload atual e a latência geral. Quanto menor o atraso do buffer, maior a probabilidade de a luz vermelha no indicador de estado (interrupções), maior a taxa de upload e menor a latência geral. - + The buffer setting is therefore a trade-off between audio quality and overall delay. A configuração do buffer é, portanto, uma troca entre qualidade de áudio e latência geral. - If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the - Se as configurações de atraso do buffer estiverem desativadas, é porque o driver de áudio proibe modificar essa configuração a partir do cliente + Se as configurações de atraso do buffer estiverem desativadas, é porque o driver de áudio proibe modificar essa configuração a partir do cliente - software. 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. - . No Windows, pressione o botão <i>Configuração do Driver</i> para abrir o painel de configurações do driver. No Linux, use a ferramenta de configuração <i>Jack</i> para alterar o atraso do buffer. + . No Windows, pressione o botão <i>Configuração do Driver</i> para abrir o painel de configurações do driver. No Linux, use a ferramenta de configuração <i>Jack</i> para alterar o atraso do buffer. - + 64 samples setting radio button Botão de configuração de 64 amostras - + 128 samples setting radio button Botão de configuração de 128 amostras - + 256 samples setting radio button Botão de configuração de 256 amostras - + ASIO setup push button Botão de configuração do ASIO - Fancy Skin - Skin Sofisticada + Skin Sofisticada - If enabled, a fancy skin will be applied to the main window. - Se ativada, uma skin sofisticada será aplicada à janela principal. + Se ativada, uma skin sofisticada será aplicada à janela principal. - Fancy skin check box - Caixa de ativação da skin sofisticada + Caixa de ativação da skin sofisticada - Display Channel Levels - Mostrar Níveis de Canais + Mostrar Níveis de Canais - If enabled, each client channel will display a pre-fader level bar. - Se ativado, cada canal de cliente exibirá uma barra de nível pré-fader. + Se ativado, cada canal de cliente exibirá uma barra de nível pré-fader. - Display channel levels check box - Caixa de activação para exibir níveis de canais + Caixa de activação para exibir níveis de canais - + Audio Channels Canais de Áudio - 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 mono-in/stereo-out mode the audio signal which is sent to the server is mono but the return signal is stereo. This is useful if the sound card has 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. - Selecione o número de canais de áudio a serem usados. Existem três modos disponíveis. Os modos mono e estéreo usam um e dois canais de áudio, respectivamente. No modo Entrada Mono/Saída Estéreo, o sinal de áudio enviado ao servidor é mono, mas o sinal de retorno é estéreo. Isso é útil quando a placa de som coloca o instrumento e o microfone em canais diferentes. Nesse caso, os dois sinais de entrada podem ser misturados num canal mono, mas a mistura do servidor pode ser ouvida em estéreo. + Selecione o número de canais de áudio a serem usados. Existem três modos disponíveis. Os modos mono e estéreo usam um e dois canais de áudio, respectivamente. No modo Entrada Mono/Saída Estéreo, o sinal de áudio enviado ao servidor é mono, mas o sinal de retorno é estéreo. Isso é útil quando a placa de som coloca o instrumento e o microfone em canais diferentes. Nesse caso, os dois sinais de entrada podem ser misturados num canal mono, mas a mistura do servidor pode ser ouvida em estéreo. - 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. - Ativar o modo de transmissão estéreo aumenta a taxa do fluxo de dados. Verifique que a taxa de transmissão não excede a largura de banda disponível da sua ligação à Internet. + Ativar o modo de transmissão estéreo aumenta a taxa do fluxo de dados. Verifique que a taxa de transmissão não excede a largura de banda disponível da sua ligação à Internet. - In 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. - No modo de transmissão estéreo, nenhuma seleção de canal de áudio para o efeito de reverberação estará disponível na janela principal, pois o efeito é aplicado em ambos os canais. + No modo de transmissão estéreo, nenhuma seleção de canal de áudio para o efeito de reverberação estará disponível na janela principal, pois o efeito é aplicado em ambos os canais. @@ -1098,39 +1418,36 @@ Qualidade de Áudio - 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 upload rate does not exceed the available bandwidth of your internet connection. - Selecione a qualidade de áudio desejada. Pode ser selecionada uma qualidade de áudio baixa, normal ou alta. Quanto maior a qualidade do áudio, maior a taxa de dados do fluxo de áudio. Verifique que a taxa de transmissão não excede a largura de banda disponível da sua ligação à Internet. + Selecione a qualidade de áudio desejada. Pode ser selecionada uma qualidade de áudio baixa, normal ou alta. Quanto maior a qualidade do áudio, maior a taxa de dados do fluxo de áudio. Verifique que a taxa de transmissão não excede a largura de banda disponível da sua ligação à Internet. - + Audio quality combo box Seletor de qualidade áudio - + New Client Level Nível de Novo Cliente - 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 of that client was already stored. - A configuração de nível de novo cliente define, em percentagem, o nível do fader de um novo cliente ligado. Por exemplo, se um cliente novo se ligar ao servidor atual, o seu canal terá o nível inicial do fader especificado, excepto quando um diferente nível do fader de uma ligação anterior desse mesmo cliente já tenha sido definido. + A configuração de nível de novo cliente define, em percentagem, o nível do fader de um novo cliente ligado. Por exemplo, se um cliente novo se ligar ao servidor atual, o seu canal terá o nível inicial do fader especificado, excepto quando um diferente nível do fader de uma ligação anterior desse mesmo cliente já tenha sido definido. - + New client level edit box Caixa de edição no nível de novo cliente - + Custom Central Server Address Endereço do Servidor Central Personalizado - The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. This address is only used if the custom server list is selected in the connection dialog. - O endereço personalizado do servidor central é o endereço IP ou URL do servidor central no qual a lista de servidores da Configuração de Ligação é gerida. Este endereço é usado apenas se a lista de servidores personalizados estiver selecionada na Configuração de Ligação. + O endereço personalizado do servidor central é o endereço IP ou URL do servidor central no qual a lista de servidores da Configuração de Ligação é gerida. Este endereço é usado apenas se a lista de servidores personalizados estiver selecionada na Configuração de Ligação. Central Server Address @@ -1145,96 +1462,251 @@ Seletor de servidor central padrão - Central server address line edit - Caixa de edição do endereço do servidor central + Caixa de edição do endereço do servidor central - + Current Connection Status Parameter Parâmetros do Estado da Ligação - The ping time is the time required for the audio stream to travel from the client to the server and back again. 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. - A latência da ligação é o tempo necessário para o fluxo de áudio viajar do cliente para o servidor e vice-versa. Esta latência é introduzida pela rede. Esta latência deve ser tão baixa quanto 20-30 ms. Se esta latência for maior (por exemplo, 50-60 ms), a distância até ao servidor é muito grande ou sua ligação à Internet não é suficiente. + A latência da ligação é o tempo necessário para o fluxo de áudio viajar do cliente para o servidor e vice-versa. Esta latência é introduzida pela rede. Esta latência deve ser tão baixa quanto 20-30 ms. Se esta latência for maior (por exemplo, 50-60 ms), a distância até ao servidor é muito grande ou sua ligação à Internet não é suficiente. - The overall delay is calculated from the current ping time and the delay which is introduced by the current buffer settings. - A latência geral é calculada a partir da latência da ligação atual e do atraso introduzido pelas configurações do buffer. + A latência geral é calculada a partir da latência da ligação atual e do atraso introduzido pelas configurações do buffer. - 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 your internet connection by, e.g., using speedtest.net). - A taxa de transmissão depende do tamanho do pacote de áudio e da configuração de compactação de áudio. Verifique se a taxa de transmissão não é maior que a taxa disponível (verifique a taxa de upload da sua ligação à Internet usando, por exemplo, o speedtest.net). + A taxa de transmissão depende do tamanho do pacote de áudio e da configuração de compactação de áudio. Verifique se a taxa de transmissão não é maior que a taxa disponível (verifique a taxa de upload da sua ligação à Internet usando, por exemplo, o speedtest.net). - + If this LED indicator turns red, you will not have much fun using the Se este indicador LED ficar vermelho, não se irá divertir muito ao usar o - + software. . - + ASIO Setup Configuração ASIO - - Mono - Mono + + + Mono + Mono + + + + mode will increase your stream's data rate. Make sure your upload rate does not exceed the available upload speed of your internet connection. + vai aumentar a quantidade de dados da transmissão. Verifique se a taxa de upload não ultrapassa a velocidade de upload disponível da sua ligação à Internet. + + + + Audio Upstream Rate depends on the current audio packet size and compression setting. Make sure that the upstream rate is not higher than your available internet upload speed (check this with a service such as speedtest.net). + A taxa de transmissão do áudio depende do tamanho do pacote de áudio e da configuração da compactação de áudio. Verifique se a taxa de transmissão não é maior que a sua taxa de upload disponível (verifique isto com um serviço como o speedtest.net). + + + + Mono-in/Stereo-out + Entrada Mono/Saída Estéreo + + + + + + Stereo + Estéreo + + + + The jitter buffer compensates for network and sound card timing jitters. The size of the buffer therefore influences the quality of the audio stream (how many dropouts occur) and the overall delay (the longer the buffer, the higher the delay). + O jitter buffer (ou buffer de instabilidade) compensa os desvios de temporização da rede e da placa de som. O tamanho do buffer influencia, portanto, a qualidade do fluxo de áudio (quantas interrupções ocorrem) e a latência geral (quanto maior o buffer, maior a latência). + + + + You can set the jitter buffer size manually for the local client and the remote server. For the local jitter buffer, dropouts in the audio stream are indicated by the light below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun has taken place and the audio stream is interrupted. + Pode escolher o tamanho do jitter buffer manualmente para o cliente local e o servidor remoto. Para o jitter buffer local, as interrupções no fluxo de áudio são indicadas pela luz na parte inferior dos faders do jitter buffer. Se a luz ficar vermelha, ocorreu um excesso/déficit do buffer e o fluxo de áudio é interrompido. + + + + If the Auto setting 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 Auto is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). + Caso a configuração automática estiver activada, os buffers de rede do cliente local e do servidor remoto são configurados automaticamente com um valor conservador para minimizar a probabilidade de perda de áudio. Se o modo automático estiver ligado, os controlos estarão desactivados (não podem ser alterados pelo utilizador). + + + + If the Auto setting 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 tweak the audio delay/latency it is recommended to disable the Auto setting and to lower the jitter buffer size manually by using the sliders until your personal acceptable amount of dropouts is reached. The LED indicator will display the audio dropouts of the local jitter buffer with a red light. + Caso a configuração automática do jitter buffer estiver ativada, os buffers de rede do cliente local e do servidor remoto são configurados com um valor conservador para minimizar a probabilidade de perda de áudio. Para ajustar o atraso/latência do áudio, é recomendável desativar a funcionalidade de configuração automática e diminuir o tamanho do jitter buffer manualmente usando os controles deslizantes até que a quantidade de perdas de áudio lhe sejam pessoalmente aceitáveis. O indicador LED representará as interrupções de áudio do jitter buffer local através de uma luz vermelha. + + + + The buffer delay setting is a fundamental setting of this software. This setting has an influence on many connection properties. + A configuração do atraso do buffer (buffer delay) é uma configuração fundamental da aplicação. Esta configuração tem influência em muitas propriedades da ligação. + + + + 64 samples: The preferred setting. Provides the lowest latency but does not work with all sound cards. + 64 amostras: Cnfiguração preferida. Fornece menor latência, mas não funciona com todas as placas de som. + + + + 128 samples: Should work for most available sound cards. + 128 amostras: Deve funcionar na maioria das placas de som disponíveis. + + + + 256 samples: Should only be used on very slow computers or with a slow internet connection. + 256 amostras: Deve apenas ser usada se tiver um computador muito lento ou uma ligação lenta à Internet. + + + + Some sound card drivers do not allow the buffer delay to be changed from within the application. In this case the buffer delay setting is disabled and has to be changed using 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. + Alguns drivers da placa de som não permitem que o atraso do buffer seja alterado pela aplicação. Nesse caso, a configuração de atraso do buffer estará desativada e deve ser alterada no driver da placa de som. No Windows, pressione o botão Configuração do Driver para abrir o painel de configurações do driver. No Linux, use a ferramenta de configuração Jack para alterar o atraso do buffer. + + + + If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The application will still work with this setting but with restricted performance. + Se nenhum atraso do buffer estiver selecionado e todas as configurações estiverem desativadas, um atraso do buffer não suportado será usado pelo driver. A aplicação ainda funcionará com essa configuração, mas com desempenho restrito. + + + + If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the software. 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. + Se as configurações de atraso do buffer estiverem desativadas, é porque o driver de áudio proibe modificar essa configuração a partir da aplicação. No Windows, pressione o botão Configuração do Driver para abrir o painel de configurações do driver. No Linux, use a ferramenta de configuração Jack para alterar o atraso do buffer. + + + + Skin + Tema + + + + Select the skin to be used for the main window. + Selecione o tema a ser usado na janela principal. + + + + Skin combo box + Caixa de selecção do tema + + + + Selects the number of audio channels to be used for communication between client and server. There are three modes available: + Selecione o número de canais de áudio a serem usados para a comunicação entre cliente e servidor. Existem três modos disponíveis: + + + + and + e + + + + These modes use one and two audio channels respectively. + Estes modos usam um e dois canais de áudio, respectivamente. + + + + Mono in/Stereo-out + Entrada Mono/Saída Estéreo + + + + The audio signal sent to the server is mono but the return signal is stereo. This is useful if the sound card has the instrument on one input channel and the microphone on the other. In that case the two input signals can be mixed to one mono channel but the server mix is heard in stereo. + O sinal de áudio enviado ao servidor é mono, mas o sinal de retorno é estéreo. Isso é útil quando a placa de som coloca o instrumento e o microfone em canais diferentes. Nesse caso, os dois sinais de entrada podem ser misturados num canal mono, mas a mistura do servidor pode ser ouvida em estéreo. + + + + Enabling + Activar o modo + + + + In stereo streaming mode, no audio channel selection for the reverb effect will be available on the main window since the effect is applied to both channels in this case. + No modo de transmissão estéreo, nenhuma seleção de canal de áudio para o efeito de reverberação estará disponível na janela principal, pois o efeito é aplicado em ambos os canais. + + + + The higher the audio quality, the higher your audio stream's data rate. Make sure your upload rate does not exceed the available bandwidth of your internet connection. + Quanto maior a qualidade de áudio, maior a quantidade de dados da transmissão. Verifique se a taxa de upload não ultrapassa a velocidade de upload disponível da sua ligação à Internet. + + + + This setting defines the fader level of a newly connected client in percent. If a new client connects to the current server, they will get the specified initial fader level if no other fader level from a previous connection of that client was already stored. + Esta opção define o nível do fader de um cliente novo, em percentagem. Se um cliente novo se liga ao mesmo servidor, este irá ter o nível do fader específicado, excepto se já definiu o nível do fader desse cliente previamente. + + + + Leave this blank unless you need to enter the address of a central server other than the default. + Deixe este campo em branco excepto se necessitar de introduzir um endereço alternativo de um servidor central. + + + + Central server address combo box + - - Mono-in/Stereo-out - Entrada Mono/Saída Estéreo + + The Ping Time is the time required for the audio stream to travel from the client to the server and back again. This delay is introduced by the network and should be about 20-30 ms. If this delay is higher than about 50 ms, your distance to the server is too large or your internet connection is not sufficient. + A latência da ligação é o tempo necessário para o fluxo de áudio viajar do cliente para o servidor e vice-versa. Esta latência é introduzida pela rede e deve ser cerca de 20-30 ms. Se esta latência for maior que 50 ms, a distância até ao servidor é muito grande ou sua ligação à Internet não é suficiente. - - Stereo - Estéreo + + Overall Delay is calculated from the current Ping Time and the delay introduced by the current buffer settings. + A latência geral é calculada a partir da latência da ligação atual e do atraso introduzido pelas configurações do buffer. - + Low Baixa - + + Normal Normal - + High Alta + + + Fancy + Sofisticado + + + + Compact + Compacto + Manual Manual - + Custom Personalizado - + All Genres Servidor Geral - + Genre Rock Servidor Rock - + Genre Jazz Servidor Jazz @@ -1243,12 +1715,12 @@ Servidor Rock/Jazz - - Genre Classical/Folk/Choir + + Genre Classical/Folk/Choral Serv. Clássica/Folclore/Coro - + Default Servidor Padrão @@ -1257,40 +1729,37 @@ Servidor Padrão (America do Norte) - + preferred preferido - - + + Size: Tamanho: - + Buffer Delay Atraso do buffer - + Buffer Delay: - Atraso do buffer: + Atraso do buffer: - The selected audio device could not be used because of the following error: - O dispositivo de áudio selecionado não pôde ser usado devido ao seguinte erro: + O dispositivo de áudio selecionado não pôde ser usado devido ao seguinte erro: - The previous driver will be selected. - O driver anterior será selecionado. + O driver anterior será selecionado. - Ok - Ok + Ok @@ -1373,58 +1842,66 @@ Auto - + Local Local - + Server Servidor - - + + Size Tamanho - + Misc Outras Config. - + Audio Channels Canais de Áudio - + Audio Quality Qualidade de Áudio - + New Client Level Nível de Novo Cliente - + + Skin + Tema + + + + Language + Linguagem + + + % % - Fancy Skin - Skin Sofisticada + Skin Sofisticada - Display Channel Levels - Mostrar Níveis de Canais + Mostrar Níveis de Canais - + Custom Central Server Address: Endereço do Servidor Central Personalizado: @@ -1433,24 +1910,24 @@ Endereço do Servidor Central: - + Audio Stream Rate Taxa de Transmissão de Áudio - - - + + + val val - + Ping Time Latência da Ligação - + Overall Delay Latência Geral @@ -1458,100 +1935,115 @@ CConnectDlg - + Server List Lista de servidores - 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. - A lista de servidores mostra a os servidores disponíveis registados no servidor central. Selecione um servidor da lista e pressione o botão Ligar para se ligar a este servidor. Como alternativa, clique duas vezes num servidor da lista para se ligar ao mesmo. Se um servidor estiver ocupado, uma lista dos músicos ligados estará disponível expandindo o item da lista. Os servidores permanentes são mostrados em negrito. + A lista de servidores mostra a os servidores disponíveis registados no servidor central. Selecione um servidor da lista e pressione o botão Ligar para se ligar a este servidor. Como alternativa, clique duas vezes num servidor da lista para se ligar ao mesmo. Se um servidor estiver ocupado, uma lista dos músicos ligados estará disponível expandindo o item da lista. Os servidores permanentes são mostrados em negrito. - 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. - Observe que pode demorar algum tempo para obter a lista de servidores do servidor central. Se nenhum endereço de servidor central válido for especificado nas definições, nenhuma lista de servidores estará disponível. + Observe que pode demorar algum tempo para obter a lista de servidores do servidor central. Se nenhum endereço de servidor central válido for especificado nas definições, nenhuma lista de servidores estará disponível. - + Server list view Vista da lista de servidores - + Server Address Endereço do servidor - The IP address or URL of the server running the - O endereço IP ou URL do servidor executando o servidor + O endereço IP ou URL do servidor executando o servidor - 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: - deve ser definido aqui. Um número de porta opcional pode ser adicionado após o endereço IP ou URL usando o caractere dois pontos como separador, por exemplo, example.org: + deve ser definido aqui. Um número de porta opcional pode ser adicionado após o endereço IP ou URL usando o caractere dois pontos como separador, por exemplo, example.org: - . A list of the most recent used server IP addresses or URLs is available for selection. - . Uma lista dos endereços IP ou URLs dos servidores usados recentemente está disponível para seleção. + . Uma lista dos endereços IP ou URLs dos servidores usados recentemente está disponível para seleção. + + + + The Connection Setup window shows a list of available servers. Server operators can optionally list their servers by music genre. Use the List dropdown to select a genre, click on the server you want to join and press the Connect button to connect to it. Alternatively, double click on on the server name. Permanent servers (those that have been listed for longer than 48 hours) are shown in bold. + A janela Configuração da ligação mostra uma lista de servidores disponíveis. Os operadores dos servidores podem registar os seus servidores por género musical. Utilize o menu Lista para selecionar um género, clique no servidor ao qual se deseja ligar e pressione o botão Ligar. Como alternativa, clique duas vezes no nome do servidor. Servidores permanentes (aqueles que estão registados há mais de 48 horas) são mostrados em negrito. + + + + If you know the IP address or URL of a server, you can connect to it using the Server name/Address field. An optional port number can be added after the IP address or URL using a colon as a separator, e.g, example.org: + Se souver o endereço IP ou URL de um servidor, pode ligar-se a este utilizando o campo Nome/Endereço do Servidor. Um número de porta opcional pode ser adicionado após o endereço IP ou URL usando o caractere dois pontos como separador, por exemplo, example.org: + + + + . The field will also show a list of the most recently used server addresses. + . Este campo também irá mostrar uma lista dos endereços IP ou URLs dos servidores usados recentemente. - + Server address edit box Caixa de edição do endereço do servidor - + Holds the current server IP address or URL. It also stores old URLs in the combo box list. Contém o endereço IP ou URL do servidor atual. Também armazena URLs antigos na lista do seletor. - + Server List Selection Selecção da Lista de Servidores - + Selects the server list to be shown. Seleciona a lista de servidores a ser apresentada. - + Server list selection combo box Caixa de selecção de lista de servidores - + Filter Filtro - + The server list is filtered by the given text. Note that the filter is case insensitive. A lista de servidores é filtrada pelo texto fornecido. Note que o filtro não diferencia maiúsculas de minúsculas. - + Filter edit box Caixa de edição do filtro - + Show All Musicians Mostrar Todos os Músicos - + 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. Se marcar esta caixa de seleção, os músicos de todos os servidores serão mostrados. Se desmarcar a caixa de seleção, todos os itens em exibição na lista serão recolhidos. - + Show all musicians check box Caixa de seleção para mostrar músicos + + + Type # for occupied servers + Escreve # para servidores ocupados + CConnectDlgBase @@ -1597,8 +2089,8 @@ - Server Name/Address - Nome/Endereço do Servidor + Server Address + Endereço do Servidor @@ -1614,495 +2106,559 @@ CHelpMenu - + &Help &Ajuda - - + + Getting &Started... Como Começa&r... - + Software &Manual... &Manual do Programa... - + What's &This O que é &isto - + &About... &Sobre... + + CLanguageComboBox + + + Restart Required + Reinicio necessário + + + + Please restart the application for the language change to take effect. + Por favor reinicie a aplicação para a alteração de linguagem ter efeito. + + CLicenceDlg - I &agree to the above licence terms - Eu &aceito os termos da licença acima + Eu &aceito os termos da licença acima - + + This server requires you accept conditions before you can join. Please read these in the chat window. + + + + + I have read the conditions and &agree. + + + + Accept Aceitar - + Decline Rejeitar - By connecting to this server and agreeing to this notice, you agree to the following: - Ao ligar-se a este servidor e concordar com este aviso, está a concordar com o seguinte: + Ao ligar-se a este servidor e concordar com este aviso, está a concordar com o seguinte: - 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 - Você concorda que todos os dados, sons ou outros trabalhos transmitidos para este servidor pertencem e são criados por você ou por seus licenciadores, e que você está disponibilizando esses dados, sons ou outros trabalhos através da seguinte licença Creative Commons (para obter mais informações sobre esta licença, consulte + Você concorda que todos os dados, sons ou outros trabalhos transmitidos para este servidor pertencem e são criados por você ou por seus licenciadores, e que você está disponibilizando esses dados, sons ou outros trabalhos através da seguinte licença Creative Commons (para obter mais informações sobre esta licença, consulte - You are free to: - Você tem o direito de: + Você tem o direito de: - Share - Compartilhar + Compartilhar - copy and redistribute the material in any medium or format - copiar e redistribuir o material em qualquer suporte ou formato + copiar e redistribuir o material em qualquer suporte ou formato - Adapt - Adaptar + Adaptar - remix, transform, and build upon the material - remisturar, transformar, e criar a partir do material + remisturar, transformar, e criar a partir do material - The licensor cannot revoke these freedoms as long as you follow the license terms. - O licenciante não pode revogar estes direitos desde que você respeite os termos da licença. + O licenciante não pode revogar estes direitos desde que você respeite os termos da licença. - Under the following terms: - De acordo com os termos seguintes: + De acordo com os termos seguintes: - Attribution - Atribuição + Atribuição - You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. - Você deve atribuir o devido crédito, fornecer um link para a licença, e indicar se foram feitas alterações. Você pode fazê-lo de qualquer forma razoável, mas não de uma forma que sugira que o licenciante o apoia ou aprova o seu uso. + Você deve atribuir o devido crédito, fornecer um link para a licença, e indicar se foram feitas alterações. Você pode fazê-lo de qualquer forma razoável, mas não de uma forma que sugira que o licenciante o apoia ou aprova o seu uso. - NonCommercial - NãoComercial + NãoComercial - You may not use the material for commercial purposes. - Você não pode usar o material para fins comerciais. + Você não pode usar o material para fins comerciais. - ShareAlike - CompartilhaIgual + CompartilhaIgual - If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. - Se você remisturar, transformar, ou criar a partir do material, tem de distribuir as suas contribuições ao abrigo da mesma licença que o original. + Se você remisturar, transformar, ou criar a partir do material, tem de distribuir as suas contribuições ao abrigo da mesma licença que o original. - No additional restrictions - Sem restrições adicionais + Sem restrições adicionais - You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. - Você não pode aplicar termos jurídicos ou medidas de caráter tecnológico que restrinjam legalmente outros de fazerem algo que a licença permita. + Você não pode aplicar termos jurídicos ou medidas de caráter tecnológico que restrinjam legalmente outros de fazerem algo que a licença permita. + + + + CMultiColorLED + + + Red + Vermelho + + + + Yellow + Amarelo + + + + Green + Verde CMusProfDlg - server. This tag will also show up at each client which is connected to the same server as you. If the name is left empty, the IP address is shown instead. - . Esta identificação também será exibida em cada cliente ligado ao mesmo servidor que você. Se o nome estiver vazio, o endereço IP será mostrado. + . Esta identificação também será exibida em cada cliente ligado ao mesmo servidor que você. Se o nome estiver vazio, o endereço IP será mostrado. - + Alias or name edit box Caixa de edição do nome ou pseudônimo - + Instrument picture button Botão da imagem do instrumento - + Country flag button Botão da bandeira do país - + City edit box Caixa de edição da cidade - + Skill level combo box Caixa do nível de habilidade - - - + + + None Nenhum - - + + Musician Profile Perfil do músico - + Alias/Name Nome/Alcunha - + Instrument Instrumento - + Country País - + City Cidade - + Skill Habilidade - + &Close &Fechar - + Beginner Principiante - + Intermediate Intermediário - + Expert Avançado - 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 in. The city you live in and the skill level playing your instrument may also be added. - Defina o seu nome ou um pseudônimo aqui para que os outros músicos com quem quer tocar saibam quem você é. Além disso, pode definir uma imagem do instrumento que toca e uma bandeira do país em que vive. A cidade em que vive e o nível de habilidade com o seu instrumento também podem ser adicionados. + Defina o seu nome ou um pseudônimo aqui para que os outros músicos com quem quer tocar saibam quem você é. Além disso, pode definir uma imagem do instrumento que toca e uma bandeira do país em que vive. A cidade em que vive e o nível de habilidade com o seu instrumento também podem ser adicionados. - What you set here will appear at your fader on the mixer board when you are connected to a - O que definir aqui aparecerá por baixo do seu fader na secção de mistura quando estiver ligado a um servidor + O que definir aqui aparecerá por baixo do seu fader na secção de mistura quando estiver ligado a um servidor + + + + Write your name or an alias here so the other musicians you want to play with know who you are. You may also add a picture of the instrument you play and a flag of the country you are located in. Your city and skill level playing your instrument may also be added. + Escreva o seu nome ou um pseudónimo aqui para que os outros músicos com quem quer tocar saibam quem você é. Além disso, pode também definir uma imagem do instrumento que toca e uma bandeira do país onde vive. A cidade onde vive e o nível de habilidade com o seu instrumento também podem ser adicionados. + + + + What you set here will appear at your fader on the mixer board when you are connected to a Jamulus server. This tag will also be shown at each client which is connected to the same server as you. + O que definir aqui aparecerá por baixo do seu fader na secção de mistura quando estiver ligado a um servidor Esta etiqueta também será exibida em cada cliente que estiver ligado ao mesmo servidor. - + Drum Set Bateria - + Djembe Djembe - + Electric Guitar Guitarra Elétrica - + Acoustic Guitar Guitarra Acústica - + Bass Guitar Baixo - + Keyboard Teclado - + Synthesizer Sintetizador - + Grand Piano Piano de Cauda - + Accordion Acordeão - + Vocal Voz - + Microphone Microfone - + Harmonica Harmónica - + Trumpet Trompete - + Trombone Trombone - + French Horn Trompa Francesa - + Tuba Tuba - + Saxophone Saxofone - + Clarinet Clarinete - + Flute Flauta - + Violin Violino - + Cello Violoncelo - + Double Bass Contrabaixo - + Recorder Gravador - + Streamer Streamer - + Listener Ouvinte - + Guitar+Vocal Guitarra+Voz - + Keyboard+Vocal Teclado+Voz - + Bodhran Bodhrán - + Bassoon Fagote - + Oboe Oboé - + Harp Harpa - + Viola Viola de Arco - + Congas Congas - + Bongo Bongo - + Vocal Bass Voz Baixo - + Vocal Tenor Voz Tenor - + Vocal Alto Voz Alto - + Vocal Soprano Voz Soprano - + Banjo Banjo - + Mandolin Bandolim + + + Ukulele + Ukulele + + + + Bass Ukulele + Ukulele Baixo + + + + Vocal Baritone + Voz Barítono + + + + Vocal Lead + Voz Principal + + + + Mountain Dulcimer + + + + + Scratching + + + + + Rapping + + + + + No Name + Sem Nome + CServerDlg - + Client List Lista de Clientes - + 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. A lista de clientes mostra todos os clientes que estão atualmente ligados a este servidor. Algumas informações sobre os clientes, como o endereço IP e o nome, são fornecidas para cada cliente ligado. - + Connected clients list view Lista de clientes ligados - + Start Minimized on Operating System Start Iniciar Minimizado com o Sistema Operativo - If the start minimized on operating system start check box is checked, the - Se a caixa de seleção Iniciar Minimizado com o Sistema Operativo estiver marcada, o servidor + Se a caixa de seleção Iniciar Minimizado com o Sistema Operativo estiver marcada, o servidor - server will be started when the operating system starts up and is automatically minimized to a system task bar icon. - será iniciado quando o sistema operativo for iniciado, e minimizado automaticamente para um ícone da barra de tarefas do sistema. + será iniciado quando o sistema operativo for iniciado, e minimizado automaticamente para um ícone da barra de tarefas do sistema. - Show Creative Commons Licence Dialog - Mostrar Diálogo da Licença Creative Commons + Mostrar Diálogo da Licença Creative Commons - If enabled, a Creative Commons BY-NC-SA 4.0 Licence dialog is shown each time a new user connects the server. - Se ativada, uma caixa de diálogo Creative Commons BY-NC-SA 4.0 será exibida sempre que um novo utilizador se ligar ao servidor. + Se ativada, uma caixa de diálogo Creative Commons BY-NC-SA 4.0 será exibida sempre que um novo utilizador se ligar ao servidor. - + Make My Server Public Tornar Servidor Público - If the Make My Server Public check box is checked, this server registers itself at the central server so that all - Se a caixa de seleção Tornar Servidor Público estiver marcada, esse servidor irá registar-se no servidor central para que todos os utilizadores do + Se a caixa de seleção Tornar Servidor Público estiver marcada, esse servidor irá registar-se no servidor central para que todos os utilizadores do - users can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. - possam ver o servidor na lista do diálogo de ligação e ligar-se a ele. O registo do servidor é renovado periodicamente para garantir que todos os servidores na lista de diálogo de ligação estejam realmente disponíveis. + possam ver o servidor na lista do diálogo de ligação e ligar-se a ele. O registo do servidor é renovado periodicamente para garantir que todos os servidores na lista de diálogo de ligação estejam realmente disponíveis. - + Register Server Status Estado de Registo do Servidor @@ -2119,7 +2675,7 @@ O endereço do servidor central é o endereço IP ou o URL do servidor central no qual esse servidor será registado. Com o menu dos servidores centrais, é possível selecionar um dos servidores centrais padrão ou especificar um endereço manual. - + If the Make My Server Public check box is checked, this will show whether registration with the central server is successful. If the registration failed, please choose another server list. Se a caixa de seleção Tornar Meu Servidor Público fôr seleccionada, isto apresentará se o registro no servidor central foi bem-sucedido. Se o registo falhar, escolha outra lista de servidores. @@ -2128,112 +2684,324 @@ Seletor do servidor central padrão - + + If the start minimized on operating system start check box is checked, the server will be started when the operating system starts up and is automatically minimized to a system task bar icon. + Se a caixa de seleção Iniciar Minimizado com o Sistema Operativo estiver marcada, o servidor será iniciado quando o sistema operativo for iniciado, e minimizado automaticamente para um ícone da barra de tarefas do sistema. + + + + If the Make My Server Public check box is checked, this server registers itself at the central server so that all users of the application can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. + Se a caixa de seleção Tornar Servidor Público estiver marcada, este servidor irá registar-se no servidor central para que todos os utilizadores da aplicação o possam ver na lista de servidores e ligar-se a ele. O registo dos servidores é renovado periodicamente para garantir que todos os servidores na lista estão realmente disponíveis. + + + Custom Central Server Address Endereço do Servidor Central Personalizado - + The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. O endereço do servidor central personalizado é o endereço IP ou URL do servidor central no qual a lista de servidores da Configuração da Ligação é gerida. - + Central server address line edit Caixa de edição do endereço do servidor central - + Server List Selection Selecção da Lista de Servidores - + Selects the server list (i.e. central server address) in which your server will be added. Seleciona a lista de servidores (ou seja, endereço do servidor central) à qual seu servidor será adicionado. - + Server list selection combo box Caixa de selecção de lista de servidores - + Server Name Nome do Servidor - 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. - O nome do servidor identifica o servidor na lista do diálogo de ligação exibido nos clientes. Se nenhum nome for fornecido, o endereço IP será mostrado. + O nome do servidor identifica o servidor na lista do diálogo de ligação exibido nos clientes. Se nenhum nome for fornecido, o endereço IP será mostrado. + + + + The server name identifies your server in the connect dialog server list at the clients. + O nome do servidor identifica o servidor na lista do diálogo de ligação exibido nos clientes. - + Server name line edit Caixa de edição do nome do servidor - + Location City ;Localização: Cidade - + 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. A cidade onde este servidor está localizado pode ser definida aqui. Se um nome de cidade for inserido, este será mostrado na lista do diálogo de ligação dos clientes. - + City where the server is located line edit Caixa de edição da cidade onde o servidor se encontra - + Location country Localização: País - + 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. O país em que este servidor está localizado pode ser definido aqui. Se um país for inserido, ele será mostrado na lista do diálogo de logação dos clientes. - + Country where the server is located combo box Seletor do país onde o servidor de encontra + + + Display dialog to select recording directory button + Mostrar diálogo para selecionar botão do directorio de gravação + + + + + Main Recording Directory + Directório Principal das Gravações + + + + Click the button to open the dialog that allows the main recording directory to be selected.The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). + Clique no botão para abrir o diálogo que permite selecionar o directório das gravações. O directório escolhido deve existir e ter permissões de escrita (deve permitir a criação de sub-pastas pelo utilizador que está a correr o Jamulus). + + + + Main recording directory text box (read-only) + Caixa de texto do directório das gravações (só de leitura) + + + + The current value of the main recording directory. The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). Click the button to open the dialog that allows the main recording directory to be selected. + O valor actual do diretório das gravações. O valor escolhido deve existir e ter permissões de escrita (deve permitir a criação de sub-pastas pelo utilizador que está a correr o Jamulus). Clique no botão para abrir a caixa de diálogo que permite selecionar o diretório das gravações. + - + Clear the recording directory button + Botão para limpar directório de gravações + + + + Clear Recording Directory + Limpar diretório de gravações + + + + Click the button to clear the currently selected recording directory. This will prevent recording until a new value is selected. + Carregue no botão para limpar o valor do directório selecionado actualmente. Isto irá prevenir gravações até que um novo valor seja selecionado. + + + + Checkbox to turn on or off server recording + Caixa de selecção para ligar ou desligar a gravação no servidor + + + + Enable Recorder + Activar Gravador + + + + Checked when the recorder is enabled, otherwise unchecked. The recorder will run when a session is in progress, if (set up correctly and) enabled. + Activo quando o gravador estiver ligado, caso contrário inactivo. O gravador irá correr quando uma sessão estiver a decorrer, se (correctamente configurado e ) activo. + + + + Current session directory text box (read-only) + Caixa de texto com a pasta da gravação actual (apenas leitura) + + + + Current Session Directory + Pasta da Sessão Acual + + + + Enabled during recording and holds the current recording session directory. Disabled after recording or when the recorder is not enabled. + Activo durante a gravação e exibe a pasta da gravação actual. Inactivo depois de gravação ou quando o gravador não estiver ligado. + + + + Recorder status label + Etiqueta do estado do Gravador + + + + Recorder Status + Estado do Gravador + + + + Displays the current status of the recorder. The following values are possible: + Mostra o estado actual do gravadro. Os valores possíveis são: + + + + No recording directory has been set or the value is not useable + O directório de gravaçãos não foi definido ou não é utilizável + + + + Recording has been switched off + As gravações foram desactivadas + + + + by the UI checkbox + pela caixa de selecção da interface + + + + , either by the UI checkbox or SIGUSR2 being received + , pela caixa de selecção da interface ou por sinal SIGUSR2 ser recebido + + + + There is no one connected to the server to record + Não está ninguém ligado ao servidor para gravar + + + + The performers are being recorded to the specified session directory + Os artistas estão a ser gravados para o directório de sessão especificado + + + + NOTE + NOTE + + + + If the recording directory is not useable, the problem will be displayed in place of the directory. + Se o directório das gravações não for utilizável, o problema será apresentado em vez do directório. + + + + Server welcome message edit box + Caixa de edição da mensagem de Boas-Vindas do Servidor + + + + Server Welcome Message + Mesagem de Boas-Vindas do Servidor + + + + A server welcome message text is displayed in the chat window if a musician enters the server. If no message is set, the server welcome is disabled. + Uma mensagem de boas vindas do servidor é exibida na janela de mensagens quando um utilizador entra no servidor. Se não houver nenhuma mensagem definida, a mensagem do servidor ficará inactiva. + + + + Type a message here. If no message is set, the server welcome is disabled. + Insira uma mensagem aqui. Se não houver nenhuma mensagem definida, a mensagem do servidor ficará inactiva. + + + + software upgrade available + + + + + ERROR + ERRO + + + Displays the current status of the recorder. + Mostra o estado actual do gravador. + + + + Request new recording button + Botão para começar nova gravação + + + + New Recording + Nova Gravação + + + + During a recording session, the button can be used to start a new recording. + Durante uma sessão de gravação, este botão pode ser usado para começar uma nova gravação. + + + + E&xit &Sair - + &Hide &Esconder servidor - - - + + + server - + &Open &Abrir servidor - server - + + + + + Select Main Recording Directory + Seleciona o directório principal das gravações - Predefined Address - Endereço Predefinido + Endereço Predefinido + + + + Recording + A gravar + + + + Not recording + Não está a gravar + + + + Not initialised + Não inicializado + + + + Not enabled + Desactivado Manual @@ -2248,52 +3016,62 @@ Servidor Padrão (America do Norte) - + Server - Servidor - + &Window &Janela - + Unregistered Não Registado - + Bad address Endereço incorrecto - + Registration requested Registo solicitado - + Registration failed Falha no registo - + Check server version Verifique versão do servidor - + Registered Registado - + Central Server full Servidor Central Cheio - + + Your server version is too old + A versão do seu servidor é muito antiga + + + + Requirements not fulfilled + Requisitos não cumpridos + + + Unknown value Valor desconhecido @@ -2307,7 +3085,7 @@ - + Name Nome do Servidor @@ -2317,14 +3095,33 @@ Tamanho do Jitter Buffer - + + Server Setup + Configuração do Servidor + + + + Chat Window Welcome (HTML/CSS Supported) + Mensagem de Boas Vindas (HTML/CSS Suportado) + + + + Options + Opções + + + Start Minimized on Windows Start Iniciar Minimizado com o Sistema Operativo - Show Creative Commons BY-NC-SA 4.0 Licence Dialog - Mostrar Diálogo da Licença Creative Commons BY-NC-SA 4.0 + Mostrar Diálogo da Licença Creative Commons BY-NC-SA 4.0 + + + + Update check + @@ -2332,38 +3129,75 @@ Tornar Servidor Público (Registar na Lista de Servidores) - + + Genre + Género + + + + STATUS ESTADO - + Custom Central Server Address: Endereço do Servidor Central Personalizado: + + + Recording Directory + Directório das Gravações + + + + Enable Jam Recorder + Activar Gravador + + + + New Recording + Nova Gravação + + + + Language + Linguagem + Central Server Address: Endereço do Servidor Central: - + My Server Info Informação do Servidor - + Location: City Localização: Cidade - + Location: Country Localização: País - + Enable jam recorder + Activar gravação + + + New recording + Nova gravação + + + Recordings folder + Pasta de gravações + + TextLabelNameVersion - TextLabelNameVersion + TextLabelNameVersion @@ -2405,98 +3239,114 @@ Não é possível ativar o cliente Jack. - + The Jack server was shut down. This software requires a Jack server to run. Try to restart the software to solve the issue. O servidor Jack foi desligado. Este programa requer um servidor Jack para ser executado. Tente reiniciar o programa para resolver o problema. - + CoreAudio input AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. A entrada do CoreAudio falhou na chamada AudioHardwareGetProperty. Parece que nenhuma placa de som está disponível no sistema. - + CoreAudio output AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. A saída do CoreAudio falhou na chamada AudioHardwareGetProperty. Parece que nenhuma placa de som está disponível no sistema. - + Current system audio input device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. A taxa de amostragem (sample rate) de %1 Hz do dispositivo de entrada de áudio atual não é suportada. Por favor, abra o Audio-MIDI-Setup em Applications-> Utilities e tente definir uma taxa de amostragem de %2 Hz. - + + + The current selected audio device is no longer present in the system. + + + + + The audio input device is no longer available. + + + + + The audio output device is no longer available. + + + + Current system audio output device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. A taxa de amostragem (sample rate) de %1 Hz do dispositivo de saída de áudio atual não é suportada. Por favor, abra o Audio-MIDI-Setup em Applications-> Utilities e tente definir uma taxa de amostragem de %2 Hz. - + The audio input stream format for this audio device is not compatible with this software. O formato do fluxo de entrada de áudio para este dispositivo de áudio não é compatível com este programa. - + The audio output stream format for this audio device is not compatible with this software. O formato do fluxo de saída de áudio para este dispositivo de áudio não é compatível com este programa. - + The buffer sizes of the current input and output audio device cannot be set to a common value. Please choose other input/output audio devices in your system settings. Os tamanhos de buffer do dispositivo de áudio de entrada e saída atual não podem ser definidos para um valor comum. Por favor, escolha outros dispositivos de áudio de entrada/saída nas configurações do seu sistema. - + The audio driver could not be initialized. O driver de áudio não pôde ser inicializado. - + The audio device does not support the required sample rate. The required sample rate is: O dispositivo de áudio não suporta a taxa de amostragem (sample rate) necessária. A taxa de amostragem necessária é: - + The audio device does not support setting the required sampling rate. This error can happen if you have an audio interface like the Roland UA-25EX where you set the sample rate with a hardware switch on the audio device. If this is the case, please change the sample rate to O dispositivo de áudio não suporta definir a taxa de amostragem (sample rate) necessária. Este erro pode ocorrer se você tiver uma interface de áudio como o Roland UA-25EX, onde se define a taxa de amostragem através de um interruptor de hardware no dispositivo de áudio. Se for esse o caso, altere a taxa de amostragem para - + Hz on the device and restart the Hz no dispositivo e reinicie o cliente - + software. . - + The audio device does not support the required number of channels. The required number of channels for input and output is: O dispositivo de áudio não suporta o número necessário de canais. O número necessário de canais para entrada e saída é: - - + + Required audio sample format not available. Formato de amostra de áudio necessário não disponível. - + No ASIO audio device (driver) found. - Nenhum dispositivo de áudio ASIO (driver) encontrado + Nenhum dispositivo de áudio ASIO (driver) encontrado. - + The O programa - + software requires the low latency audio interface ASIO to work properly. This is not a 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 the ASIO4All driver. requer que a interface de áudio de baixa latência ASIO funcione corretamente. Esta não é uma interface de áudio padrão do Windows e, portanto, é necessário um driver de áudio especial. Ou a sua placa de som possui um driver ASIO nativo (recomendado), ou pode usar drivers alternativos, como o driver ASIO4All. - + Error closing stream: $s Erro ao fechar o stream: $s @@ -2504,55 +3354,84 @@ CSoundBase - Invalid device selection. - Seleção de dispositivo inválida. + Seleção de dispositivo inválida. - The audio driver properties have changed to a state which is incompatible with this software. The selected audio device could not be used because of the following error: - As propriedades do driver de áudio foram alteradas para um estado incompatível com este programa. O dispositivo de áudio selecionado não pôde ser usado devido ao seguinte erro: + As propriedades do driver de áudio foram alteradas para um estado incompatível com este programa. O dispositivo de áudio selecionado não pôde ser usado devido ao seguinte erro: - Please restart the software. - Por favor reinicie o programa. + Por favor reinicie o programa. - Close - Fechar + Fechar + + + + The selected audio device could not be used because of the following error: + O dispositivo de áudio selecionado não pôde ser usado devido ao seguinte erro: + + + + The previous driver will be selected. + O driver anterior será selecionado. + + + + The previously selected audio device is no longer available or the audio driver properties have changed to a state which is incompatible with this software. We now try to find a valid audio device. This new audio device might cause audio feedback. So, before connecting to a server, please check the audio device setting. + - + No usable Nenhum dispositivo de áudio (driver) - + audio device (driver) found. utilizável encontrado. - + In the following there is a list of all available drivers with the associated error message: De seguida verá uma lista de todos os drivers disponíveis com a mensagem de erro associada: - + Do you want to open the ASIO driver setups? Deseja abrir as configurações do driver ASIO? - + could not be started because of audio interface issues. não pôde ser iniciado devido a problemas na interface de áudio. + + QCoreApplication + + + , Version + , Versão + + + + Internet Jam Session Software + Programa de Jam Sessions pela Internet + + + + Released under the GNU General Public License (GPL) + Lançado sob a Licença Pública Geral GNU (GPL) + + global - + For more information use the What's This help (help menu, right mouse button or Shift+F1) Para mais informações, use O que é isto (menu Ajuda, botão direito do rato ou Shift + F1) diff --git a/src/res/translation/translation_sk_SK.qm b/src/res/translation/translation_sk_SK.qm new file mode 100644 index 0000000000..30d721ab20 Binary files /dev/null and b/src/res/translation/translation_sk_SK.qm differ diff --git a/src/res/translation/translation_sk_SK.ts b/src/res/translation/translation_sk_SK.ts new file mode 100644 index 0000000000..dd07e86be4 --- /dev/null +++ b/src/res/translation/translation_sk_SK.ts @@ -0,0 +1,2931 @@ + + + + + CAboutDlg + + + Qt cross-platform application framework + Qt multiplatformový aplikačný framework + + + + Audio reverberation code by Perry R. Cook and Gary P. Scavone + Kód pre odraz zvuku (reverb): Perry R. Cook a Gary P. Scavone + + + + Some pixmaps are from the + Niektoré ikony sú z + + + + This app enables musicians to perform real-time jam sessions over the internet. + Táto aplikácia umožňuje hudobníkom v reálnom čase džemovať cez internet. + + + + There is a server which collects the audio data from each client, mixes the audio data and sends the mix back to each client. + Úlohou servera je zbierať zvukové dáta od každého klienta, zmiešať ich a poslať ich každému klientovi naspäť. + + + + This app uses the following libraries, resources or code snippets: + Tento program používa nasledujúce knižnice, zdrojové dáta alebo úryvky kódu: + + + + Country flag icons by Mark James + Vlajky krajín: Mark James + + + + For details on the contributions check out the + Podrobnosti o prispievateľoch nájdete na + + + + Github Contributors list + Zoznam prispievateľov na Githube + + + + Spanish + španielčina + + + + French + francúzština + + + + Portuguese + portugalčina + + + + Dutch + holandčina + + + + Italian + taliančina + + + + German + nemčina + + + + Polish + poľština + + + + Swedish + švédčina + + + + Slovak + + + + + About + O programe + + + + CAboutDlgBase + + + About + O programe + + + + TextLabelVersion + TextLabelVersion + + + + Copyright (C) 2005-2020 Volker Fischer and others + Autorské práva (C) 2005-2020 Volker Fischer a iní + + + + A&bout + O &programe + + + + &Libraries + &Knižnice + + + + &Contributors + &Prispievatelia + + + + &Translation + P&reklad + + + + &OK + &OK + + + + CAnalyzerConsole + + + Analyzer Console + Konzola analyzátora + + + + Error Rate of Each Buffer Size + Frekvencia chýb podľa veľkosti buffera + + + + CAudioMixerBoard + + + Personal Mix at the Server + Osobný mix servera + + + + When connected to a server, the controls here allow you to set your local mix without affecting what others hear from you. The title shows the server name and, when known, whether it is actively recording. + Po pripojení sa na server vám tieto ovládacie prvky umožnia lokálne mixovať zvuk bez toho, aby ste tým ovplyvnili to, čo od vás budú počuť ostatní. Nadpis okna zobrazuje názov servera a, ak je táto informácia k dispozícii, či je aktívne nahrávanie. + + + + Server + Server + + + + T R Y I N G T O C O N N E C T + P O K U S O P R I P O J E N I E + + + + RECORDING ACTIVE + NAHRÁVANIE AKTÍVNE + + + + Personal Mix at: + Osobný mix na: + + + + CChannelFader + + + + + Pan + Posúvanie + + + + + + Mute + Stíšiť + + + + + + Solo + Sólo + + + + Channel Level + Úroveň kanála + + + + Input level of the current audio channel at the server + Vstupná úroveň aktuálneho audio kanála na serveri + + + + Mixer Fader + Prelínač mixéra + + + + Local mix level setting of the current audio channel at the server + Nastavenie úrovne miestneho mixu aktuálneho audio kanála na serveri + + + + Status Indicator + Stavový indikátor + + + + Shows a status indication about the client which is assigned to this channel. Supported indicators are: + Zobrazuje indikáciu stavu klienta, ktorý je priradený k tomuto kanálu. Podporované indikátory sú: + + + + Status indicator label + Menovka stavového indikátora + + + + Panning + Posúvanie + + + + Local panning position of the current audio channel at the server + Miesto posúvanie pozície aktuálneho zvukového kanála na serveri + + + + With the Mute checkbox, the audio channel can be muted. + Audio kanál môžete vypnúť pomocou zaškrtávacieho políčka Stíšiť. + + + + Mute button + Tlačidlo Stíšenia + + + + Solo button + Tlačidlo Sóla + + + + Fader Tag + Značka prelínača + + + + Displays the pre-fader audio level of this channel. All clients connected to the server will be assigned an audio level, the same value for every client. + + + + + &No grouping + &Bez zoskupovania + + + + + + + Assign to group + Priradiť do skupiny + + + + Adjusts the audio level of this channel. All clients connected to the server will be assigned an audio fader, displayed at each client, to adjust the local mix. + Upraví úroveň zvuku pre tento kanál. Všetkým klientom pripojeným k serveru bude priradený zvukový prelínač a umožní im lokálne mixovanie. + + + + Speaker with cancellation stroke: Indicates that another client has muted you. + + + + + Sets the pan from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. + + + + + With the Solo checkbox, the audio channel can be set to solo which means that all other channels except the soloed channel are muted. It is possible to set more than one channel to solo. + + + + + Group + Zoskupiť + + + + With the Grp checkbox, a group of audio channels can be defined. All channel faders in a group are moved in proportional synchronization if any one of the group faders are moved. + + + + + Group button + Tlačidlo Zoskupiť + + + + The fader tag identifies the connected client. The tag name, a picture of your instrument and the flag of your country can be set in the main window. + Značka prelínača identifikuje pripojeného klienta. Názov značky, obrázok vášho nástroja a vlajku vašej krajiny môžete nastaviť v hlavnom okne. + + + + Mixer channel instrument picture + Obrázok hud. nástroja kanála mixéra + + + + Mixer channel label (fader tag) + Menovka kanála mixéra (značka prelínača) + + + + Mixer channel country flag + Vlajka krajiny na kanáli mixéra + + + + PAN + PAN + + + + MUTE + STÍŠIŤ + + + + SOLO + SÓLO + + + + GRP + SKP + + + + M + M + + + + S + S + + + + G + G + + + + Grp + Skp + + + + Alias/Name + Prez/Meno + + + + Instrument + Hud. nástroj + + + + Location + Miesto + + + + + + Skill Level + Úroveň hrania + + + + Alias + Prezývka + + + + Beginner + Začiatočník + + + + Intermediate + Pokročilý + + + + Expert + Expert + + + + Musician Profile + Profil hudobníka + + + + CChatDlg + + + Chat Window + Okno chatu + + + + The chat window shows a history of all chat messages. + Okno chatu zobrazuje históriu všetkých správ chatu. + + + + Chat history + História chatu + + + + Input Message Text + Vstupná správa chatu + + + + 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. + Zadajte text správy chatu do poľa a stlačte enter pre poslanie správy na server, ktorý ju rozpošle všetkým pripojeným klientom. Vaša správa sa potom objaví v okne chatu. + + + + New chat text edit box + Pole úprav nového textu správy + + + + Type a message here + Sem napíšte správu + + + + &Edit + Ú&pravy + + + + Cl&ear Chat History + &Vyčistiť históriu chatu + + + + Do you want to open the link + + + + + in an external browser? + + + + + CChatDlgBase + + + Chat + Chat + + + + &Send + &Poslať + + + + CClientDlg + + + Input Level Meter + + + + + Make sure not to clip the input signal to avoid distortions of the audio signal. + + + + + Input level meter + + + + + Simulates an analog LED level meter. + + + + + Connect/Disconnect Button + Tlačidlo Pripojiť/Odpojiť + + + + Connect and disconnect toggle button + Tlačidlo Pripojiť/Odpojiť + + + + Local Audio Input Fader + + + + + + L + Ľ + + + + , where + , kde + + + + is the current attenuation indicator. + je indikátor aktuálneho útlmu. + + + + Local audio input fader (left/right) + + + + + Delay Status LED + Dióda stavu oneskorenia + + + + Delay status LED indicator + LED indikátor stavu oneskorenia + + + + Buffers Status LED + Dióda stavu bufferov + + + + The network jitter buffer is not large enough for the current network/audio interface jitter. + + + + + This shows the level of the two stereo channels for your audio input. + + + + + If the application is connected to a server and you play your instrument/sing into the microphone, the VU 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. + + + + + For proper usage of the application, you should not hear your singing/instrument through the loudspeaker or your headphone when the software is not connected.This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). + + + + + Opens a dialog where you can select a server to connect to. If you are connected, pressing this button will end the session. + + + + + Controls the relative levels of the left and right local audio channels. For a mono signal it acts as a pan between the two channels.For example, if 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 + + + + + Reverb effect + Efekt odrazu (reverb) + + + + Reverb can be applied to one local mono audio channel or to both channels in stereo mode. The mono channel selection and the reverb level can be modified. For example, if a microphone signal is fed in to the right audio channel of the sound card and a reverb effect needs to be applied, set the channel selector to right and move the fader upwards until the desired reverb level is reached. + + + + + Reverb effect level setting + Nastavenie sily efektu odrazu (reverb) + + + + Reverb Channel Selection + Výber kanálu pre reverb + + + + With these radio buttons the audio input channel on which the reverb effect is applied can be chosen. Either the left or right input channel can be selected. + + + + + Left channel selection for reverb + + + + + Right channel selection for reverb + + + + + Shows the current audio delay status: + + + + + Green + Zelená + + + + The delay is perfect for a jam session. + Oneskorenie je perfektné pre džemovanie. + + + + Yellow + Žltá + + + + A session is still possible but it may be harder to play. + Džemovanie je stále možné, ale hranie môže byť sťažené. + + + + Red + Červená + + + + The delay is too large for jamming. + Oneskorenie je príliš veľké na džemovanie. + + + + If this LED indicator turns red, you will not have much fun using the application. + Ak sa dióda zmení na červenú, veľa zábavy si s touto aplikáciou neužijete. + + + + The buffers status LED shows the current audio/streaming status. If the light is red, the audio stream is interrupted. This is caused by one of the following problems: + Dióda stavu bufferov ukazuje aktuálny stav audio/streamovania. Ak je jej farba červená, audio stream je prerušený. Príčinou môže byť jeden z nasledujúcich problémov: + + + + The sound card's buffer delay (buffer size) is too small (see Settings window). + Oneskorenie buffera (veľkosť buffera) zvukovej karty je príliš malé (pozrite Nastavenia). + + + + The upload or download stream rate is too high for your internet bandwidth. + Rýchlosť uploadu alebo downloadu streamu je príliš vysoká vzhľadom na vašu rýchlosť internetového pripojenia. + + + + The CPU of the client or server is at 100%. + Procesor na klientovi alebo serveri je vytažený na 100%. + + + + Buffers status LED indicator + + + + + + C&onnect + &Pripojiť + + + + software upgrade available + dostupná aktualizácia softvéru + + + + &File + &Súbor + + + + &View + Pohľ&ad + + + + &Connection Setup... + &Nastavenie pripojenia... + + + + My &Profile... + Môj &profil... + + + + C&hat... + &Chat... + + + + &Settings... + &Nastavenia... + + + + &Analyzer Console... + &Konzola analyzátora... + + + + N&o User Sorting + + + + + Sort Users by &City + + + + + Use &Two Rows Mixer Panel + + + + + &Clear All Stored Solo and Mute Settings + + + + + Set All Faders to New Client &Level + + + + + Ok + Ok + + + + E&xit + U&končiť + + + + &Load Mixer Channels Setup... + &Načítať nastavenia kanálov mixéra... + + + + &Save Mixer Channels Setup... + &Uložiť nastavenia kanálov mixéra... + + + + &Edit + Ú&pravy + + + + Sort Users by &Name + Triediť používateľov kanálov podľa &mena + + + + Sort Users by &Instrument + Triediť používateľov kanálov podľa &nástroja + + + + Sort Users by &Group + Triediť používateľov kanálov podľa &skupiny + + + None + Żaden + + + + Center + Stred + + + + R + P + + + + Central Server + Centrálny server + + + + + Select Channel Setup File + Vyberte súbor s nastavením kanálov + + + + user + používateľ + + + + users + používatelia + + + + Your sound card is not working correctly. Please open the settings dialog and check the device selection and the driver settings. + + + + + D&isconnect + O&dpojiť + + + + CClientDlgBase + + + Delay + Oneskorenie + + + + Buffers + Buffery + + + + Input + Vstup + + + + L + Ľ + + + + R + P + + + + &Mute Myself + &Stíšiť ma + + + + &Settings + &Nastavenia + + + + &Chat + &Chat + + + + C&onnect + &Pripojiť + + + + Pan + Posun + + + + Center + Stred + + + + Reverb + Ozvena (rev) + + + + Left + Ľavý + + + + Right + Pravý + + + + MUTED (Other people won't hear you) + STÍŠENÉ (Nebude vás počuť) + + + + Update check + Kontrola aktualizácií + + + + CClientSettingsDlg + + + Jitter Buffer Size + Veľkosť jitter buffera + + + + The jitter buffer setting is therefore a trade-off between audio quality and overall delay. + + + + + Local jitter buffer slider control + + + + + Server jitter buffer slider control + + + + + Auto jitter buffer switch + + + + + Jitter buffer status LED indicator + + + + + Sound Card Device + Zariadenie zvukovej karty + + + + The ASIO driver (sound card) can be selected using + Ovládač (zvukovej karty) ASIO môžete vybrať použitím + + + + 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. + + + + + If the driver is selected during an active connection, the connection is stopped, the driver is changed and the connection is started again automatically. + + + + + Sound card device selector combo box + + + + + If you are using the kX ASIO driver, make sure to connect the ASIO inputs in the kX DSP settings panel. + + + + + Sound Card Channel Mapping + + + + + For each + Pre každý + + + + input/output channel (Left and Right channel) a different actual sound card channel can be selected. + vstupný/výstupný kanál (ľavý a pravý kanál) môžete vybrať iný kanál zvukovej karty. + + + + Left input channel selection combo box + + + + + Right input channel selection combo box + + + + + Left output channel selection combo box + + + + + Right output channel selection combo box + + + + + Enable Small Network Buffers + Povoliť malé sieťové buffre + + + + 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 + + + + + Enable small network buffers check box + + + + + Sound Card Buffer Delay + + + + + Three buffer sizes are supported + + + + + The buffer setting is therefore a trade-off between audio quality and overall delay. + + + + + 128 samples setting radio button + + + + + 256 samples setting radio button + + + + + ASIO setup push button + + + + + Central server address combo box + + + + + Fancy + Efektný + + + + Compact + Kompaktný + + + Display Channel Levels + Zobraziť úrovne kanálov + + + + Audio Channels + Zvukové kanály + + + + mode will increase your stream's data rate. Make sure your upload rate does not exceed the available upload speed of your internet connection. + + + + + Audio channels combo box + + + + + Audio Quality + Kvalita zvuku + + + + Audio quality combo box + + + + + New Client Level + Úroveň nového klienta + + + + New client level edit box + + + + + Current Connection Status Parameter + + + + + If 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. + + + + + If the selected sound card device offers more than one input or output channel, the Input Channel Mapping and Output Channel Mapping settings are visible. + + + + + samples. The smaller the network buffers, the lower the audio latency. But at the same time the network load increases and the probability of audio dropouts also increases. + + + + + 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 a red light in the status indicator (drop outs) and the higher the upload rate and the lower the overall delay. + + + + + 64 samples setting radio button + + + + + Custom Central Server Address + Adresa vlastného centrálneho servera + + + + Audio Upstream Rate depends on the current audio packet size and compression setting. Make sure that the upstream rate is not higher than your available internet upload speed (check this with a service such as speedtest.net). + + + + + If this LED indicator turns red, you will not have much fun using the + + + + + software. + + + + + ASIO Setup + Nastavenie ASIO + + + + + Mono + + + + + Mono-in/Stereo-out + Mono-dnu/Stereo-von + + + + + + Stereo + Stereo + + + + The jitter buffer compensates for network and sound card timing jitters. The size of the buffer therefore influences the quality of the audio stream (how many dropouts occur) and the overall delay (the longer the buffer, the higher the delay). + + + + + You can set the jitter buffer size manually for the local client and the remote server. For the local jitter buffer, dropouts in the audio stream are indicated by the light below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun has taken place and the audio stream is interrupted. + + + + + If the Auto setting 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 Auto is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). + + + + + If the Auto setting 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 tweak the audio delay/latency it is recommended to disable the Auto setting and to lower the jitter buffer size manually by using the sliders until your personal acceptable amount of dropouts is reached. The LED indicator will display the audio dropouts of the local jitter buffer with a red light. + + + + + The buffer delay setting is a fundamental setting of this software. This setting has an influence on many connection properties. + + + + + 64 samples: The preferred setting. Provides the lowest latency but does not work with all sound cards. + + + + + 128 samples: Should work for most available sound cards. + + + + + 256 samples: Should only be used on very slow computers or with a slow internet connection. + + + + + Some sound card drivers do not allow the buffer delay to be changed from within the application. In this case the buffer delay setting is disabled and has to be changed using 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. + Niektoré ovládače zvukových kariet nepovoľujú meniť oneskorenie buffera z prostredia aplikácie. V takom prípade je nastavenie oneskorenia buffera neaktívne a musíte ho zmeniť použitím ovládača zvukovej karty. Vo Windows kliknite na tlačidlo Nastaviť ASIO a otvoríte ovládací panel ovládača. V LInuxe použite pre zmenu veľkosti buffera konfiguračný nástroj Jack. + + + + If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The application will still work with this setting but with restricted performance. + + + + + If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the software. 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. + + + + + Skin + Vzhľad + + + + Select the skin to be used for the main window. + + + + + Skin combo box + + + + + Selects the number of audio channels to be used for communication between client and server. There are three modes available: + + + + + and + + + + + These modes use one and two audio channels respectively. + + + + + Mono in/Stereo-out + + + + + The audio signal sent to the server is mono but the return signal is stereo. This is useful if the sound card has the instrument on one input channel and the microphone on the other. In that case the two input signals can be mixed to one mono channel but the server mix is heard in stereo. + + + + + Enabling + + + + + In stereo streaming mode, no audio channel selection for the reverb effect will be available on the main window since the effect is applied to both channels in this case. + + + + + The higher the audio quality, the higher your audio stream's data rate. Make sure your upload rate does not exceed the available bandwidth of your internet connection. + + + + + This setting defines the fader level of a newly connected client in percent. If a new client connects to the current server, they will get the specified initial fader level if no other fader level from a previous connection of that client was already stored. + + + + + Leave this blank unless you need to enter the address of a central server other than the default. + + + + + The Ping Time is the time required for the audio stream to travel from the client to the server and back again. This delay is introduced by the network and should be about 20-30 ms. If this delay is higher than about 50 ms, your distance to the server is too large or your internet connection is not sufficient. + + + + + Overall Delay is calculated from the current Ping Time and the delay introduced by the current buffer settings. + + + + + Low + Nízka + + + + + Normal + Normálna + + + + High + Vysoká + + + + Custom + Vlastný + + + + All Genres + Všetky žánre + + + + Genre Rock + Žáner Rock + + + + Genre Jazz + Žáner Jazz + + + + Genre Classical/Folk/Choral + Žáner Klasika/Folk/Zbor + + + + Default + Predvolený + + + + preferred + preferované + + + + + Size: + Veľkosť: + + + + Buffer Delay + Oneskorenie buffera + + + + Buffer Delay: + Oneskorenie buffera: + + + The selected audio device could not be used because of the following error: + Vybrané audio zariadenie nie je možné použiť kvôli nasledujúcej chybe: + + + The previous driver will be selected. + Bude vybraný predchádzajúci ovládač. + + + Ok + Ok + + + + CClientSettingsDlgBase + + + Settings + Nastavenia + + + + Soundcard + Zvuková karta + + + + Device + Zariadenie + + + + Input Channel Mapping + Mapovanie vstupného kanála + + + + + L + L + + + + + R + P + + + + Output Channel Mapping + Mapovanie výstupného kanálu + + + + Enable Small Network Buffers + Povoliť malé sieťové buffre + + + + Buffer Delay + Oneskorenie buffera + + + + (preferred) + (preferované) + + + + (default) + (predvolené) + + + + (safe) + (bezpečné) + + + + Driver Setup + Konfigurácia ovládača + + + + Jitter Buffer + Jitter Buffer + + + + Auto + Automaticky + + + + Local + Lokálny + + + + Server + Server + + + + + Size + Veľkosť + + + + Misc + Rôzne + + + + Audio Channels + Audio kanály + + + + Audio Quality + Audio kvalita + + + + New Client Level + Úroveň nového klienta + + + + Skin + Vzhľad + + + + Language + Jazyk + + + + % + % + + + Display Channel Levels + Zobraziť úrovne kanálov + + + + Custom Central Server Address: + Adresa vlastného centrálneho servera: + + + + Audio Stream Rate + Rýchlosť streamovania zvuku + + + + + + val + hodn + + + + Ping Time + Čas odpovede + + + + Overall Delay + Celkové oneskorenie + + + + CConnectDlg + + + Server List + Zoznam serverov + + + + Server list view + Okno so zoznamom serverov + + + + Server Address + Adresa servera + + + + The Connection Setup window shows a list of available servers. Server operators can optionally list their servers by music genre. Use the List dropdown to select a genre, click on the server you want to join and press the Connect button to connect to it. Alternatively, double click on on the server name. Permanent servers (those that have been listed for longer than 48 hours) are shown in bold. + + + + + If you know the IP address or URL of a server, you can connect to it using the Server name/Address field. An optional port number can be added after the IP address or URL using a colon as a separator, e.g, example.org: + + + + + . The field will also show a list of the most recently used server addresses. + + + + + Server address edit box + + + + + Holds the current server IP address or URL. It also stores old URLs in the combo box list. + + + + + Server List Selection + Výber zoznamu serverov + + + + Selects the server list to be shown. + + + + + Server list selection combo box + + + + + Filter + Filter + + + + The server list is filtered by the given text. Note that the filter is case insensitive. + + + + + Filter edit box + + + + + Show All Musicians + Zobraziť všetkých hudobníkov + + + + 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. + + + + + Show all musicians check box + Zaškrtávacie políčko Zobraziť všetkých hudobníkov + + + + Type # for occupied servers + Zadajte # pre obsadené servery + + + + CConnectDlgBase + + + Connection Setup + Parametre pripojenia + + + + List + Zoznam + + + + Filter + Filter + + + + Show All Musicians + Zobraziť všetkých hudobníkov + + + + Server Name + Meno servera + + + + Ping Time + Čas odpovede + + + + Musicians + Hudobníci + + + + Location + Miesto + + + + Server Address + Adresa servera + + + Server Name/Address + Meno servera/Adresa + + + + C&ancel + &Zrušiť + + + + &Connect + &Pripojiť + + + + CHelpMenu + + + &Help + Pomo&cník + + + + + Getting &Started... + &Začíname... + + + + Software &Manual... + &Manuál programu... + + + + What's &This + Čo j&e toto + + + + &About... + &O programe... + + + + CLanguageComboBox + + + Restart Required + Vyžadovaný reštart + + + + Please restart the application for the language change to take effect. + Aby sa zmeny nastavenia jazyku prejavili, musíte reštartovať aplikáciu. + + + + CLicenceDlg + + I &agree to the above licence terms + &Súhlasím s licenčnými podmienkami vyššie + + + + This server requires you accept conditions before you can join. Please read these in the chat window. + + + + + I have read the conditions and &agree. + + + + + Accept + Súhlasím + + + + Decline + Nesúhlasím + + + By connecting to this server and agreeing to this notice, you agree to the following: + Pripojením sa na tento server a prijatím tohto oznámenia, súhlasíte s nasledovným: + + + Share + Zdieľať + + + ShareAlike + ShareAlike + + + No additional restrictions + Žiadne ďalšie obmedzenia + + + + CMultiColorLED + + + Red + Červená + + + + Yellow + Žltá + + + + Green + Zelená + + + + CMusProfDlg + + + + Musician Profile + Profil hudobníka + + + + Alias/Name + Prezývka/Meno + + + + Instrument + Hud. nástroj + + + + Country + Krajina + + + + City + Mesto + + + + Skill + Úroveň hrania + + + + &Close + &Zavrieť + + + + + + None + Nenastavené + + + + Beginner + Začiatočník + + + + Intermediate + Pokročilý + + + + Expert + Expert + + + + Write your name or an alias here so the other musicians you want to play with know who you are. You may also add a picture of the instrument you play and a flag of the country you are located in. Your city and skill level playing your instrument may also be added. + + + + + What you set here will appear at your fader on the mixer board when you are connected to a Jamulus server. This tag will also be shown at each client which is connected to the same server as you. + + + + + Alias or name edit box + + + + + Instrument picture button + Tlačidlo s obrázkom hud. nástroja + + + + Country flag button + Tlačidlo s vlajkou krajiny + + + + City edit box + + + + + Skill level combo box + + + + + Drum Set + Bicie + + + + Djembe + Djembe + + + + Electric Guitar + Elektrická gitara + + + + Acoustic Guitar + Akustická gitara + + + + Bass Guitar + Basová gitara + + + + Keyboard + Keyboard + + + + Synthesizer + Syntetizátor + + + + Grand Piano + Koncertné krídlo + + + + Accordion + Akordeón + + + + Vocal + Vokály + + + + Microphone + Mikrofón + + + + Harmonica + Ústna harmonika + + + + Trumpet + Trúbka + + + + Trombone + Trombón + + + + French Horn + Lesný roh + + + + Tuba + Tuba + + + + Saxophone + Saxofón + + + + Clarinet + Klarinet + + + + Flute + Flauta + + + + Violin + Husle + + + + Cello + Violončelo + + + + Double Bass + Kontrabas + + + + Recorder + Zobcová flauta + + + + Streamer + Streamovač + + + + Listener + Poslucháč + + + + Guitar+Vocal + Gitara+Vokály + + + + Keyboard+Vocal + Klávesnica+Vokály + + + + Bodhran + Bodhran + + + + Bassoon + Fagot + + + + Oboe + Hoboj + + + + Harp + Harfa + + + + Viola + Viola + + + + Congas + Kongy + + + + Bongo + Bongo + + + + Vocal Bass + Basové vokály + + + + Vocal Tenor + Tenorové vokály + + + + Vocal Alto + Altové vokály + + + + Vocal Soprano + Sopránové vokály + + + + Banjo + Bendžo + + + + Mandolin + Mandolína + + + + Ukulele + Ukulele + + + + Bass Ukulele + Basové ukulele + + + + Vocal Baritone + Barytónové vokály + + + + Vocal Lead + Hlavný spev + + + + Mountain Dulcimer + + + + + Scratching + + + + + Rapping + + + + + No Name + Bez názvu + + + + CServerDlg + + + Client List + Zoznam klientov + + + + 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. + Zoznam klientov zobrazuje všetkých klientov, ktorí su momentálne pripojení k serveru. Pri každom klientovi sú zobrazené dodatočné informácie, ako napr. IP adresa a meno. + + + + Connected clients list view + Zoznam pripojených klientov + + + + Start Minimized on Operating System Start + Spustiť minimalizované pri štarte operačného systému + + + Show Creative Commons Licence Dialog + Zobraziť dialógové okno s licenciou Creative Commons License + + + If enabled, a Creative Commons BY-NC-SA 4.0 Licence dialog is shown each time a new user connects the server. + Ak je povolené vždy po pripojení na server sa klientom zobrazí dialógové okno s licenciou Creative Commons BY-NC-SA 4.0. + + + + Make My Server Public + Nastaviť server ako verejný + + + + Register Server Status + Stav registrácie servera + + + + If the Make My Server Public check box is checked, this will show whether registration with the central server is successful. If the registration failed, please choose another server list. + Ak je zapnutá voľba Nastaviť server ako verejný, tu sa zobrazí, či bola registrácia na centrálnom serveri úspešná. Ak registrácia zlyhala, prosím, vyberte iný zoznam serverov. + + + + Custom Central Server Address + Adresa vlastného centrálneho servera + + + + The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. + Adresa vlastného centrálneho servera je IP adresa alebo URL centrálneho servera, kde je spravovaný zoznam serverov pre dialógové okno, ktoré je zobrazené klientom. + + + + Central server address line edit + + + + + Server List Selection + Výber zoznamu serverov + + + + Selects the server list (i.e. central server address) in which your server will be added. + Vyberá zoznam serverov (t. j. adresu centrálneho servera), do ktorého bude pridaný váš server. + + + + Server list selection combo box + + + + + Server Name + Názov servera + + + + Server name line edit + + + + + Location City + Lokácia/mesto + + + + 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. + Tu môžete nastaviť, v ktorom meste sa server nachádza. Ak je mesto zadané, zobrazí sa klientom v okne so zoznamom serverov. + + + + City where the server is located line edit + + + + + Location country + Lokácia/krajina + + + + 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. + + + + + Country where the server is located combo box + + + + + Display dialog to select recording directory button + + + + + + Main Recording Directory + Hlavný adresár na nahrávanie + + + + Click the button to open the dialog that allows the main recording directory to be selected.The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). + + + + + Main recording directory text box (read-only) + + + + + The current value of the main recording directory. The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). Click the button to open the dialog that allows the main recording directory to be selected. + + + + + Clear the recording directory button + + + + + Clear Recording Directory + + + + + Click the button to clear the currently selected recording directory. This will prevent recording until a new value is selected. + + + + + Checkbox to turn on or off server recording + + + + + Enable Recorder + Povoliť nahrávanie + + + + Checked when the recorder is enabled, otherwise unchecked. The recorder will run when a session is in progress, if (set up correctly and) enabled. + + + + + Current session directory text box (read-only) + + + + + Current Session Directory + + + + + Enabled during recording and holds the current recording session directory. Disabled after recording or when the recorder is not enabled. + + + + + Recorder status label + + + + + Recorder Status + Stav nahrávania + + + + Server welcome message edit box + + + + + Server Welcome Message + Uvítacia správa servera + + + + A server welcome message text is displayed in the chat window if a musician enters the server. If no message is set, the server welcome is disabled. + + + + + Type a message here. If no message is set, the server welcome is disabled. + Sem napíšte správu. Ak nie je nastavená žiadna správa, uvítacia správa servera bude vypnutá. + + + + software upgrade available + dostupná aktualizácia softvéru + + + + ERROR + CHYBA + + + + Request new recording button + + + + + New Recording + Nová nahrávka + + + + During a recording session, the button can be used to start a new recording. + + + + + + E&xit + U&končiť + + + + &Hide + &Skryť + + + + + + server + server + + + + &Open + &Otvoriť + + + + Select Main Recording Directory + Vybrať hlavný adresár na nahrávanie + + + + Server + Server + + + + If the start minimized on operating system start check box is checked, the server will be started when the operating system starts up and is automatically minimized to a system task bar icon. + + + + + If the Make My Server Public check box is checked, this server registers itself at the central server so that all users of the application can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. + + + + + The server name identifies your server in the connect dialog server list at the clients. + + + + + Displays the current status of the recorder. The following values are possible: + + + + + No recording directory has been set or the value is not useable + + + + + Recording has been switched off + + + + + by the UI checkbox + + + + + , either by the UI checkbox or SIGUSR2 being received + + + + + There is no one connected to the server to record + + + + + The performers are being recorded to the specified session directory + + + + + NOTE + POZNÁMKA + + + + If the recording directory is not useable, the problem will be displayed in place of the directory. + + + + + &Window + &Okno + + + + Unregistered + Neregistrovaný + + + + Bad address + Zlá adresa + + + + Registration requested + Vyžiadaná registrácia + + + + Registration failed + Registrácia zlyhala + + + + Check server version + Skontrolovať verziu servera + + + + Registered + Registrovaný + + + + Central Server full + Centrálny server je plný + + + + Your server version is too old + Verzia vášho servera je príliš stará + + + + Requirements not fulfilled + Požiadavky nie sú splnené + + + + Unknown value + Neznáma hodnota + + + + Not initialised + Neinicializované + + + + Not enabled + Nie je zapnuté + + + + Not recording + Nenahráva sa + + + + Recording + Nahráva sa + + + + CServerDlgBase + + + Client IP:Port + IP:Port klienta + + + + + Name + Meno + + + + Jitter Buffer Size + Veľkosť jitter buffera + + + + Server Setup + Nastavenie servera + + + + Chat Window Welcome (HTML/CSS Supported) + Uvítacia správa chatového okna (podporuje HTML/CSS) + + + + Options + Voľby + + + + Start Minimized on Windows Start + Spustiť minimalizované pri štarte Windows + + + + Update check + Kontrola aktualizácií + + + + Make My Server Public (Register My Server in the Server List) + Nastaviť server ako verejný (zaregistrovať v zozname serverov) + + + + Genre + Žáner + + + + + STATUS + STATUS + + + + Custom Central Server Address: + Adresa vlastného centrálneho servera: + + + + My Server Info + Informácie o serveri + + + + Location: City + Lokácia: Mesto + + + + Location: Country + Lokácia: Krajina + + + + Recording Directory + Adresár pre nahrávanie + + + + Enable Jam Recorder + Povoliť nahrávanie džemov + + + + New Recording + Nová nahrávka + + + + Language + Jazyk + + + + CSound + + + Error closing stream: $s + Chyba pri zatváraní streamu: $s + + + + The Jack server is not running. This software requires a Jack server to run. Normally if the Jack server is not running this software will automatically start the Jack server. It seems that this auto start has not worked. Try to start the Jack server manually. + Server Jack nie je spustený. Tento softvér vyžaduje pre svoj beh server Jack. Za normálnych okolností, ak Jack nebeží, tento program sa ho pokúsi automaticky naštartovať. Vyzerá, že automatické spustenie zlyhalo. Pokúste sa spustiť server Jack ručne. + + + + The Jack server sample rate is different from the required one. The required sample rate is: + + + + + You can use a tool like <i><a href=http://qjackctl.sourceforge.net>QJackCtl</a></i> to adjust the Jack server sample rate. + Pre úpravu vzorkovacej frekvencie môžete použiť nástroj ako napr. <i><a href=http://qjackctl.sourceforge.net>QJackCtl</a></i>. + + + + Make sure to set the Frames/Period to a low value like + + + + + to achieve a low delay. + + + + + + The Jack port registering failed. + Registrácia portu Jack zlyhala. + + + + Cannot activate the Jack client. + Nepodarilo sa aktivovať klienta Jack. + + + + The Jack server was shut down. This software requires a Jack server to run. Try to restart the software to solve the issue. + Server Jack bol vypnutý. Tento program vyžaduje, aby bol server Jack spustený. Pokúste sa reštartovať tento program a vyriešiť tak tento problém. + + + + CoreAudio input AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. + + + + + CoreAudio output AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. + + + + + Current system audio input device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. + + + + + + The current selected audio device is no longer present in the system. + + + + + The audio input device is no longer available. + + + + + The audio output device is no longer available. + + + + + Current system audio output device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. + + + + + The audio input stream format for this audio device is not compatible with this software. + + + + + The audio output stream format for this audio device is not compatible with this software. + + + + + The buffer sizes of the current input and output audio device cannot be set to a common value. Please choose other input/output audio devices in your system settings. + + + + + The audio driver could not be initialized. + Audio ovládač sa nepodarilo inicializovať. + + + + The audio device does not support the required sample rate. The required sample rate is: + + + + + The audio device does not support setting the required sampling rate. This error can happen if you have an audio interface like the Roland UA-25EX where you set the sample rate with a hardware switch on the audio device. If this is the case, please change the sample rate to + + + + + Hz on the device and restart the + + + + + software. + + + + + The audio device does not support the required number of channels. The required number of channels for input and output is: + + + + + + Required audio sample format not available. + + + + + No ASIO audio device (driver) found. + + + + + The + + + + + software requires the low latency audio interface ASIO to work properly. This is not a 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 the ASIO4All driver. + + + + + CSoundBase + + Invalid device selection. + Neplatný výber zariadenia. + + + Please restart the software. + Prosím, reštartujte tento program. + + + Close + Zavrieť + + + + The selected audio device could not be used because of the following error: + Vybrané audio zariadenie nie je možné použiť kvôli nasledujúcej chybe: + + + + The previous driver will be selected. + Bude vybraný predchádzajúci ovládač. + + + + The previously selected audio device is no longer available or the audio driver properties have changed to a state which is incompatible with this software. We now try to find a valid audio device. This new audio device might cause audio feedback. So, before connecting to a server, please check the audio device setting. + + + + + No usable + Nebolo nájdené + + + + audio device (driver) found. + žiadne zvukové zariadenie (ovládač). + + + + In the following there is a list of all available drivers with the associated error message: + + + + + Do you want to open the ASIO driver setups? + + + + + could not be started because of audio interface issues. + + + + + QCoreApplication + + + , Version + , Verzia + + + + Internet Jam Session Software + Softvér na džemovanie cez internet + + + + Released under the GNU General Public License (GPL) + Vydané pod licenciou GNU General Public License (GPL) + + + + global + + + For more information use the What's This help (help menu, right mouse button or Shift+F1) + Pre získanie viac informácii, použite "Čo je toto?" (ponuka pomocníka, pravé tlačidlo myši alebo Shift+F1) + + + diff --git a/src/res/translation/translation_sv_SE.qm b/src/res/translation/translation_sv_SE.qm new file mode 100644 index 0000000000..3053c77de3 Binary files /dev/null and b/src/res/translation/translation_sv_SE.qm differ diff --git a/src/res/translation/translation_sv_SE.ts b/src/res/translation/translation_sv_SE.ts new file mode 100644 index 0000000000..fbf2caa3a4 --- /dev/null +++ b/src/res/translation/translation_sv_SE.ts @@ -0,0 +1,3068 @@ + + + + + CAboutDlg + + + This app enables musicians to perform real-time jam sessions over the internet. + Applikationen gör det möjligt för musiker att spela tillsammans live över Internet. + + + + There is a server which collects the audio data from each client, mixes the audio data and sends the mix back to each client. + Det finns en server som samlar ljud från varje klient, blandar ljudet och skickar mixen tillbaka till varje klient. + + + + This app uses the following libraries, resources or code snippets: + Denna applikationen använder följande bibliotek, resurser eller kodavsnitt: + + + + Qt cross-platform application framework + Qt cross-platform applikationsramverk + + + + Audio reverberation code by Perry R. Cook and Gary P. Scavone + Ljudklangkod av Perry R. Cook och Gary P. Scavone + + + + Some pixmaps are from the + Vissa pixmaps är från + + + + Country flag icons by Mark James + Landsflaggsymboler gjorda av Mark James + + + + For details on the contributions check out the + För detaljer om bidrag, kolla in + + + + Github Contributors list + Github's bidragsgivarlista + + + + Spanish + Spanska + + + + French + Franska + + + + Portuguese + Portugisiska + + + + Dutch + Holländska + + + + Italian + Italienska + + + + German + Tyska + + + + Polish + Polska + + + + Swedish + Svenska + + + + Slovak + Slovakiska + + + + About + Om + + + + CAboutDlgBase + + + About + Om + + + + TextLabelVersion + How to translate this? + TextLabelVersion + + + + Copyright (C) 2005-2020 Volker Fischer and others + Upphovsrätt (C) 2005-2020 Volker Fischer och andra + + + + A&bout + &Om + + + + &Libraries + &Bibliotek + + + + &Contributors + Bidrags&givare + + + + &Translation + &Översättningar + + + + &OK + &Stäng + + + + CAnalyzerConsole + + + Analyzer Console + Analyskonsol + + + + Error Rate of Each Buffer Size + Felhastighet för varje buffertstorlek + + + + CAudioMixerBoard + + + Personal Mix at the Server + Personlig mix på servern + + + + When connected to a server, the controls here allow you to set your local mix without affecting what others hear from you. The title shows the server name and, when known, whether it is actively recording. + När du är ansluten till en server låter kontrollerna här ställa in din lokala mix utan att påverka vad andra hör från dig. Titeln visar servernamnet och, om det är känt, om den aktivt spelar in. + + + + Server + Server + + + + T R Y I N G T O C O N N E C T + F Ö R S Ö K E R A N S L U T A + + + + RECORDING ACTIVE + INSPELNING AKTIV + + + + Personal Mix at: + Personlig mix på: + + + + CChannelFader + + + + + Pan + Panorera + + + + + + Mute + Tyst + + + + + + Solo + Solo + + + + &No grouping + &Ingen gruppering + + + + + + + Assign to group + Tilldela grupp + + + + Channel Level + Kanalnivå + + + + Displays the pre-fader audio level of this channel. All clients connected to the server will be assigned an audio level, the same value for every client. + Visar ljudnivån för fader på denna kanal. Alla klienter som är anslutna till servern tilldelas en ljudnivå och har samma värde för alla klienter. + + + + Input level of the current audio channel at the server + Ingångsnivå för den aktuella ljudkanalen på servern + + + + Mixer Fader + Mixer + + + + Adjusts the audio level of this channel. All clients connected to the server will be assigned an audio fader, displayed at each client, to adjust the local mix. + Justerar ljudnivån på den här kanalen. Alla klienter som är anslutna till servern tilldelas en ljudfader som visas vid varje klient för att justera den lokala mixen. + + + + Local mix level setting of the current audio channel at the server + Lokal mixernivåinställning för den aktuella ljudkanalen på servern + + + + Status Indicator + Statusindikator + + + + Shows a status indication about the client which is assigned to this channel. Supported indicators are: + Visar en statusindikering om klienten som tilldelas denna kanal. Stödjade indikatorer är: + + + + Speaker with cancellation stroke: Indicates that another client has muted you. + Högtalare med streck över visar att en annan klient har stängt av ditt ljud. + + + + Status indicator label + etikett för statusindikator + + + + Panning + Panorering + + + + Sets the pan from Left to Right of the channel. Works only in stereo or preferably mono in/stereo out mode. + Ställer in panorering från vänster till höger för kanalen. Fungerar endast i stereo eller helst mono in / stereo ut-läge. + + + + Local panning position of the current audio channel at the server + Lokal panoreringsposition för den aktuella ljudkanalen på servern + + + + With the Mute checkbox, the audio channel can be muted. + Med kryssrutan "Stäng av" kan ljudkanalen stängas av. + + + + Mute button + Knapp för att stänga av + + + + With the Solo checkbox, the audio channel can be set to solo which means that all other channels except the soloed channel are muted. It is possible to set more than one channel to solo. + Med kryssrutan Solo kan ljudkanalen ställas in på solo vilket innebär att alla andra kanaler utom solokanalen är avstängda. Det är möjligt att ställa in mer än en kanal för solo. + + + + Solo button + Soloknapp + + + + Group + Grupp + + + + With the Grp checkbox, a group of audio channels can be defined. All channel faders in a group are moved in proportional synchronization if any one of the group faders are moved. + Med kryssrutan Grp kan en grupp ljudkanaler definieras. Alla kanalfadrar i en grupp flyttas i proportionell synkronisering om någon av gruppfadrarna flyttas. + + + + Group button + Gruppknapp + + + + Fader Tag + Panoreringstagg + + + + The fader tag identifies the connected client. The tag name, a picture of your instrument and the flag of your country can be set in the main window. + Fadertaggen identifierar den anslutna klienten. Etikettnamnet, en bild av ditt instrument och ditt lands flagga kan ställas in i huvudfönstret. + + + + Mixer channel instrument picture + Mixerkanalens instrumentbild + + + + Mixer channel label (fader tag) + Mixerkanalens etikett (fader) + + + + Mixer channel country flag + Mixerkanalens landsflagga + + + + PAN + PANORERA + + + + MUTE + TYST + + + + SOLO + SOLO + + + + GRP + GRUPP + + + + M + T + + + + S + S + + + + G + G + + + + Grp + Grp + + + + Alias/Name + Alias/Namn + + + + Instrument + Instrument + + + + Location + Plats + + + + + + Skill Level + Skicklighetsnivå + + + + Alias + Alias + + + + Beginner + Nybörjare + + + + Intermediate + Mellannivå + + + + Expert + Expert + + + + Musician Profile + Musikprofil + + + + CChatDlg + + + Chat Window + Chattfönster + + + + The chat window shows a history of all chat messages. + Chattfönstret visar en historik över alla chattmeddelanden. + + + + Chat history + Chatthistorik + + + + Input Message Text + Skriv meddelandetext + + + + 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. + Ange chattmeddelandetexten i textrutan och tryck på enter för att skicka meddelandet till servern som distribuerar meddelandet till alla anslutna klienter. Ditt meddelande kommer synas i chattfönstret. + + + + New chat text edit box + Ny redigeringsruta för chatt-text + + + + Type a message here + Skriv ditt meddelande här + + + + &Edit + &Redigera + + + + Cl&ear Chat History + &Rensa chatthistorik + + + + Do you want to open the link + Vill du öppna länken + + + + in an external browser? + i en extern webbläsare? + + + + CChatDlgBase + + + Chat + Chatt + + + + &Send + &Skicka + + + Cl&ear + R&ensa + + + &Close + &Stäng + + + + CClientDlg + + + Input Level Meter + Ingångsnivå + + + + This shows the level of the two stereo channels for your audio input. + Detta visar nivån på de två stereokanalerna för din ljudingång. + + + + Make sure not to clip the input signal to avoid distortions of the audio signal. + Se till att inte klippa insignalen för att undvika en snedvridning av ljudsignalen. + + + + If the application is connected to a server and you play your instrument/sing into the microphone, the VU 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. + Om applikationen är ansluten till en server och du spelar ditt instrument/sjunger in i mikrofonen, bör VU-mätaren röra sig. Om detta inte sker har du antagligen valt fel inmatningskanal (t.ex. 'line in' i stället för mikrofoningången) eller ställt in ingångsförstärkningen för (Windows) för lågt. + + + + For proper usage of the application, you should not hear your singing/instrument through the loudspeaker or your headphone when the software is not connected.This can be achieved by muting your input audio channel in the Playback mixer (not the Recording mixer!). + För korrekt användning av applikationen, bör du inte höra din sång/instrument via högtalaren eller hörlurarna när programvaran inte är ansluten. Det kan uppnås genom att stänga av din ingående ljudkanal i uppspelningsblandaren (inte inspelningsblandaren!). + + + + Input level meter + Ingångsnivåmätare + + + + Simulates an analog LED level meter. + Simulerar en analog LED-nivåmätare. + + + + Connect/Disconnect Button + Knapp för att anslut eller koppla ifrån + + + + Opens a dialog where you can select a server to connect to. If you are connected, pressing this button will end the session. + Öppnar en dialogruta där du kan välja en server att ansluta till. Om du är ansluten avslutar du sessionen genom att trycka på den här knappen. + + + + Connect and disconnect toggle button + Knapp för att anslut och koppla bort + + + Clicking on this button changes the caption of the button from Connect to Disconnect, i.e., it implements a toggle functionality for connecting and disconnecting the application. + Om du klickar på den här knappen ändras bildtexten på knappen från Ansluten till Nedkopplad, dvs. den implementerar en växelfunktion för att ansluta och koppla bort applikationen. + + + + Local Audio Input Fader + Lokal ljudingångsfader + + + + Controls the relative levels of the left and right local audio channels. For a mono signal it acts as a pan between the two channels.For example, if 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 + Kontrollerar de relativa nivåerna för vänster och höger lokala ljudkanal. För en monosignal fungerar den som en panorering mellan de två kanalerna. Om exempelvis en mikrofon är ansluten till den högra ingångskanalen och ett instrument är anslutet till den vänstra ingångskanalen som är mycket högre än mikrofonen, flytta ljudfadern i en riktning som etiketten ovanför fadern visar + + + + + L + V + + + + , where + , där + + + + is the current attenuation indicator. + är den aktuella dämpningsindikatorn. + + + + Local audio input fader (left/right) + Lokal ljudingångsfader (vänster/höger) + + + + Reverb effect + Reverb effekt + + + + Reverb can be applied to one local mono audio channel or to both channels in stereo mode. The mono channel selection and the reverb level can be modified. For example, if a microphone signal is fed in to the right audio channel of the sound card and a reverb effect needs to be applied, set the channel selector to right and move the fader upwards until the desired reverb level is reached. + Reverb kan tillämpas på en lokal monoljudkanal eller på båda kanalerna i stereoläge. Valet av monokanal och reverbnivån kan ändras. Om till exempel en mikrofonsignal matas in till höger ljudkanal på ljudkortet och en reverb-effekt måste appliceras, ställ in kanalväljaren till höger och flytta fadern uppåt tills önskad reverbnivå har uppnåtts. + + + + Reverb effect level setting + Reverbeffektnivåinställning + + + + Reverb Channel Selection + Reverbkanalval + + + + With these radio buttons the audio input channel on which the reverb effect is applied can be chosen. Either the left or right input channel can be selected. + Med dessa tryckknappar kan du välja ljudingångskanalen på vilken reverbeffekten används. Antingen kan vänster eller höger ingångskanal väljas. + + + + Left channel selection for reverb + Vänster kanal för reverb + + + + Right channel selection for reverb + Höger kanal för reverb + + + + Delay Status LED + Fördröjningsstatus-LED + + + + Shows the current audio delay status: + Visar aktuell ljudfördröjningsstatus: + + + + Green + Grön + + + + The delay is perfect for a jam session. + Fördröjningen är perfekt för en jam-session. + + + + Yellow + Gul + + + + A session is still possible but it may be harder to play. + En session är fortfarande möjlig men det kan vara svårare att spela. + + + + Red + Röd + + + + The delay is too large for jamming. + Fördröjningen är troligtvis för stor för en jam-session. + + + + If this LED indicator turns red, you will not have much fun using the application. + Om den här LED-indikatorn blir röd kommer du inte ha så kul med applikationen. + + + + Delay status LED indicator + LED-indikator för fördröjningsstatus + + + + Buffers Status LED + Buffertstatus-LED + + + + The buffers status LED shows the current audio/streaming status. If the light is red, the audio stream is interrupted. This is caused by one of the following problems: + Statuslampan för buffertar visar aktuell ljud-/strömningsstatus. Om lampan är röd avbryts ljudströmmen. Detta orsakas av ett av följande problem: + + + + The network jitter buffer is not large enough for the current network/audio interface jitter. + Nätverksjitterbufferten är inte tillräckligt stor för det nuvarande nätverks-/ljudgränssnittsjitteret. + + + + The sound card's buffer delay (buffer size) is too small (see Settings window). + Ljudkortets buffertfördröjning (buffertstorlek) är för liten (se Inställningsfönstret). + + + + The upload or download stream rate is too high for your internet bandwidth. + Uppladdnings- eller nedladdningsströmmen är för hög för din internethastighet. + + + + The CPU of the client or server is at 100%. + Klientens eller serverns CPU är 100%. + + + + Buffers status LED indicator + LED-indikator för buffertstatus + + + + + C&onnect + &Anslut + + + + software upgrade available + mjukvaruuppdatering tillgänglig + + + + &File + &Fil + + + + &View + &Vy + + + + &Connection Setup... + Anslutnings&inställningar... + + + + My &Profile... + Min &profil... + + + + C&hat... + &Chatt... + + + + &Settings... + &Inställningar... + + + + &Analyzer Console... + &Analyskonsol... + + + + N&o User Sorting + I&ngen kanalstortering + + + + Sort Users by &City + Sortera användarna efter S&tad + + + + Use &Two Rows Mixer Panel + Dela upp &mixerpanelen i två rader + + + + &Clear All Stored Solo and Mute Settings + &Rensa alla lagrade solo och tystade inställningar + + + + Your sound card is not working correctly. Please open the settings dialog and check the device selection and the driver settings. + + + + + Ok + Okej + + + &Clear All Stored Solo Settings + &Rensa alla lagrade soloinställningar + + + + Set All Faders to New Client &Level + Ställ in alla mixers till ny klient&nivå + + + + E&xit + &Avsluta + + + + &Load Mixer Channels Setup... + &Ladda in mixerkanalinställningarna... + + + + &Save Mixer Channels Setup... + &Spara mixerkanalinställningarna... + + + + &Edit + &Redigera + + + + Sort Users by &Name + Sortera kanalanvändare efter &Namn + + + + Sort Users by &Instrument + Sortera kanalanvändare efter &Instrument + + + + Sort Users by &Group + Sortera kanalanvändare efter &Grupp + + + None + Ingen + + + + Center + Mitten + + + + R + H + + + + Central Server + Central server + + + + + Select Channel Setup File + Välj kanalinställningsfil + + + + user + användare + + + + users + användare + + + The soundcard device does not work correctly. Please check the device selection and the driver settings. + Ljudkortet fungerar inte som det ska. Kontrollera enhetsvalet och drivrutinsinställningarna. + + + + D&isconnect + Koppla &ner + + + + CClientDlgBase + + + Delay + Fördröjning + + + + Buffers + Buffert + + + + Input + Ingång + + + + L + V + + + + R + H + + + + &Mute Myself + Tysta &mig själv + + + + &Settings + In&ställningar + + + + &Chat + &Chatt + + + + C&onnect + &Koppla upp + + + + Pan + Panorera + + + + Center + Mitten + + + + Reverb + Reverb + + + + Left + Vänster + + + + Right + Höger + + + + MUTED (Other people won't hear you) + Du är tystad! (andra hör inte dig) + + + + Update check + Uppdateringskontroll + + + MUTED (You are not sending any audio to the server) + TYSTAD (Du skickar inget ljud till servern) + + + + CClientSettingsDlg + + + Jitter Buffer Size + Jitterbuffertstorlek + + + + The jitter buffer compensates for network and sound card timing jitters. The size of the buffer therefore influences the quality of the audio stream (how many dropouts occur) and the overall delay (the longer the buffer, the higher the delay). + Jitterbufferten kompenserar för nätverks- och ljudkortstimning. Storleken på bufferten påverkar därför kvaliteten på ljudströmmen (hur många bortfall som inträffar) och den totala förseningen (ju längre bufferten är, desto högre fördröjning). + + + + You can set the jitter buffer size manually for the local client and the remote server. For the local jitter buffer, dropouts in the audio stream are indicated by the light below the jitter buffer size faders. If the light turns to red, a buffer overrun/underrun has taken place and the audio stream is interrupted. + Jitterbufferten kompenserar för nätverks- och ljudkortets timeing. Storleken på bufferten påverkar därför kvaliteten på ljudströmmen (hur många bortfall som inträffar) och den totala förseningen (ju längre bufferten är, desto högre är fördröjningen). + + + + The jitter buffer setting is therefore a trade-off between audio quality and overall delay. + Jitterbuffertinställningen är därför en avvägning mellan ljudkvalitet och total fördröjning. + + + + If the Auto setting 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 Auto is enabled, the jitter buffer size faders are disabled (they cannot be moved with the mouse). + Om Auto-inställningen är aktiverad, ställs jitterbuffertarna för den lokala klienten och fjärrservern in automatiskt baserat på mätningar av nätverkets och ljudkortets timingjitter. Om Auto är aktiverat inaktiveras jitterbuffertstorleken (de kan inte justeras). + + + + If the Auto setting 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 tweak the audio delay/latency it is recommended to disable the Auto setting and to lower the jitter buffer size manually by using the sliders until your personal acceptable amount of dropouts is reached. The LED indicator will display the audio dropouts of the local jitter buffer with a red light. + Om Auto-inställningen är aktiverad ställs nätverksbuffertarna för den lokala klienten och fjärrservern in på ett konservativt värde för att minimera sannorlikheten för ljudbortfall. För att justera ljudfördröjningen/latensen rekommenderas att du inaktiverar Auto-inställningen och att sänka jitterbuffertstorleken manuellt genom att använda reglagen tills din personliga acceptabla mängd bortfall uppnåtts. LED-indikatorn visar ljudavfallet från den lokala jitterbufferten med rött ljus. + + + + Local jitter buffer slider control + Lokalt jitterbuffertreglage + + + + Server jitter buffer slider control + Server jitter buffertreglage + + + + Auto jitter buffer switch + Auto-jitterbuffert reglage + + + + Jitter buffer status LED indicator + LED-indikator för jitterbufferstatus + + + + Sound Card Device + Ljudkortsenhet + + + + The ASIO driver (sound card) can be selected using + ASIO-drivrutinen (ljudkort) kan väljas med + + + + 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. + under Windows. Under MacOS/Linux är inget ljudkortsval möjligt. Om den valda ASIO-drivrutinen inte är giltig visas ett felmeddelande och den tidigare giltiga drivrutinen väljs. + + + + If the driver is selected during an active connection, the connection is stopped, the driver is changed and the connection is started again automatically. + Om drivrutinen väljs under en aktiv anslutning stoppas anslutningen, drivrutinen ändras och anslutningen startas automatiskt igen. + + + + Sound card device selector combo box + Ljudkortsenhetens kombinationsruta + + + + If 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. + Om ASIO4ALL-drivrutinen används, observera att den här drivrutinen vanligtvis introducerar ca. 10-30 ms extra ljudfördröjning. Det rekommenderas därför att använda ett ljudkort med en inbyggd ASIO-drivrutin. + + + + If you are using the kX ASIO driver, make sure to connect the ASIO inputs in the kX DSP settings panel. + Om du använder kX ASIO-drivrutinen, se till att ansluta ASIO-ingångarna på kX DSP-inställningspanelen. + + + + Sound Card Channel Mapping + Ljudkortets kanalval + + + + If the selected sound card device offers more than one input or output channel, the Input Channel Mapping and Output Channel Mapping settings are visible. + Om den valda ljudkortsenheten erbjuder mer än en ingångs- eller utgångskanal är inställningarna för inmatningskanalens mappning och utmatningskanal synliga. + + + + For each + För varje + + + + input/output channel (Left and Right channel) a different actual sound card channel can be selected. + ingångs-/utgångskanal (vänster- och högerkanal) kan en annan faktisk ljudkortkanal väljas. + + + + Left input channel selection combo box + Vänster ingångskanalvalskombinationsruta + + + + Right input channel selection combo box + Höger ingångskanalvalskombinationsruta + + + + Left output channel selection combo box + Vänster utgångskanalvalskombinationsruta + + + + Right output channel selection combo box + Höger utgångskanalvalskombinationsruta + + + + Enable Small Network Buffers + Aktivera liten nätverksbuffert + + + + 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 + Om det är aktiverat aktiveras stödet för mycket små nätverksljudpaket. Mycket små nätverkspaket används faktiskt bara om ljudkortsbuffertfördröjningen är mindre än + + + + samples. The smaller the network buffers, the lower the audio latency. But at the same time the network load increases and the probability of audio dropouts also increases. + bitars buffert. Ju mindre nätbuffertarna är, desto lägre är ljudet. Men samtidigt ökar nätverksbelastningen och sannolikheten för ljudavbrott ökar också. + + + + Enable small network buffers check box + Aktivera kryssrutan för små nätverksbuffertar + + + + Sound Card Buffer Delay + Ljudkortets buffertfördröjning + + + + The buffer delay setting is a fundamental setting of this software. This setting has an influence on many connection properties. + Inställningen för buffertfördröjning är en grundläggande inställning för denna applikation. Denna inställning påverkar många anslutningsegenskaper. + + + + Three buffer sizes are supported + Tre buffertstorlekar stöds + + + + 64 samples: The preferred setting. Provides the lowest latency but does not work with all sound cards. + 64 bitars buffert: Den rekommenderade inställningen. Den ger den lägsta latensen men fungerar inte med alla ljudkort. + + + + 128 samples: Should work for most available sound cards. + 128 bitars buffert: Bör fungera för alla ljudkort. + + + + 256 samples: Should only be used on very slow computers or with a slow internet connection. + 265 bitars buffert: Ska endast användas med långsamma datorer eller med långsam internetkoppling. + + + + Some sound card drivers do not allow the buffer delay to be changed from within the application. In this case the buffer delay setting is disabled and has to be changed using 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. + Vissa ljudkortdrivrutiner tillåter inte buffertfördröjningen att ändras från applikationen. I detta fall avbryts inställningen för buffertfördröjning och måste ändras med ljudkortsdrivrutinen. I Windows trycker du på ASIO-inställningsknappen för att öppna drivrutinsinställningspanelen. I Linux använder du Jack-konfigurationsverktyget för att ändra buffertstorleken. + + + + If no buffer size is selected and all settings are disabled, an unsupported buffer size is used by the driver. The application will still work with this setting but with restricted performance. + Om ingen buffertstorlek är vald och alla inställningar är inaktiverade, används en icke-stödd buffertstorlek av drivrutinen. Applikationen fungerar fortfarande med den här inställningen men med begränsad prestanda. + + + + 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 a red light in the status indicator (drop outs) and the higher the upload rate and the lower the overall delay. + Den faktiska buffertfördröjningen har påverkan på anslutningsstatusen, den aktuella uppladdningshastigheten och den totala förseningen. Ju lägre buffertstorlek, desto högre är sannolikheten för rött ljus i statusindikatorn (drop outs) och desto högre uppladdningshastighet och desto lägre blir den totala fördröjningen. + + + + The buffer setting is therefore a trade-off between audio quality and overall delay. + Buffertinställningen är därför en avvägning mellan ljudkvalitet och total fördröjning. + + + + If the buffer delay settings are disabled, it is prohibited by the audio driver to modify this setting from within the software. 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. + Om buffertfördröjningsinställningarna är inaktiverade är det ljuddrivrutinen som begränsar och det är inte möjligt att ändra denna inställning från applikationen. I Windows trycker du på ASIO-inställningsknappen för att öppna drivrutinsinställningspanelen. I Linux använder du Jack-konfigurationsverktyget för att ändra buffertstorleken. + + + + 64 samples setting radio button + 64 bitars buffertknapp + + + + 128 samples setting radio button + 128 bitars buffertknapp + + + + 256 samples setting radio button + 256 bitars buffertknapp + + + + ASIO setup push button + ASIO-inställningsknapp + + + + Skin + Skal + + + + Select the skin to be used for the main window. + Välj skal som ska användas för huvudfönstret. + + + + Skin combo box + Kombineringsknapp för skal + + + + Central server address combo box + Kombinationsruta för centralserveradress + + + Display Channel Levels + Visa kanalnivåer + + + If enabled, each client channel will display a pre-fader level bar. + Om detta är aktiverat kommer varje klientkanal att visa ett nivåfält före varje fader. + + + Display channel levels check box + Visa kryssrutan för visa kanalnivåer + + + + Audio Channels + Ljudkanaler + + + + Selects the number of audio channels to be used for communication between client and server. There are three modes available: + Väljer antalet ljudkanaler som ska användas för kommunikation mellan klient och server. Det finns tre lägen tillgängliga: + + + + + Mono + Mono + + + + and + och + + + + + + Stereo + Stereo + + + + These modes use one and two audio channels respectively. + Dessa lägen använder respektive en och två ljudkanaler. + + + + Mono in/Stereo-out + Mono in/Stereo-ut + + + + The audio signal sent to the server is mono but the return signal is stereo. This is useful if the sound card has the instrument on one input channel and the microphone on the other. In that case the two input signals can be mixed to one mono channel but the server mix is heard in stereo. + Ljudsignalen som skickas till servern är mono men retursignalen är stereo. Detta är användbart om ljudkortet har instrumentet på en ingångskanal och mikrofonen på den andra. I så fall kan de två insignalerna blandas till en monokanal men servermixen hörs i stereo. + + + + Enabling + Möjliggör + + + + mode will increase your stream's data rate. Make sure your upload rate does not exceed the available upload speed of your internet connection. + kommer att öka dataströmmen. Se till att din uppladdningshastighet inte överstiger den tillgängliga uppladdningshastigheten för din internetanslutning. + + + + In stereo streaming mode, no audio channel selection for the reverb effect will be available on the main window since the effect is applied to both channels in this case. + I stereo-strömningsläge kommer inget val av ljudkanal för reverb-effekten att finnas tillgängligt i huvudfönstret eftersom effekten tillämpas på båda kanalerna i detta fall. + + + + Audio channels combo box + Kombineringsknapp för ljudkanalerna + + + + Audio Quality + Ljudkvalitet + + + + The higher the audio quality, the higher your audio stream's data rate. Make sure your upload rate does not exceed the available bandwidth of your internet connection. + Ju högre ljudkvalitet, desto högre datahastighet krävs. Se till att din uppladdningshastighet inte överstiger den tillgängliga bandbredden för din internetanslutning. + + + + Audio quality combo box + Kombineringsknapp för ljudkvalitet + + + + New Client Level + Ny klientnivå + + + + This setting defines the fader level of a newly connected client in percent. If a new client connects to the current server, they will get the specified initial fader level if no other fader level from a previous connection of that client was already stored. + Denna inställning definierar fadernivån för en nyansluten klient i procent. Om en ny klient ansluter till den aktuella servern, kommer de att få den angivna initiala fader-nivån om ingen annan fader-nivå från en tidigare anslutning av den klienten redan lagrats. + + + + New client level edit box + Redigeringsruta för en ny klient + + + + Custom Central Server Address + Anpassad central serveradress + + + + Leave this blank unless you need to enter the address of a central server other than the default. + Lämna detta tomt om du inte behöver ange adressen till en annan central server än standard. + + + Central server address line edit + Ändra centralserveradress + + + + Current Connection Status Parameter + Parameter för aktuell anslutningsstatus + + + + The Ping Time is the time required for the audio stream to travel from the client to the server and back again. This delay is introduced by the network and should be about 20-30 ms. If this delay is higher than about 50 ms, your distance to the server is too large or your internet connection is not sufficient. + Ping-tiden är den tid som krävs för ljudströmmen att resa från klienten till servern och tillbaka igen. Denna fördröjning införs av nätverket och bör vara cirka 20-30 ms. Om denna fördröjning är högre än cirka 50 ms är ditt avstånd till servern för stort eller din internetanslutning är inte tillräcklig. + + + + Overall Delay is calculated from the current Ping Time and the delay introduced by the current buffer settings. + Övergripande fördröjning beräknas utifrån den aktuella Ping-tiden och den fördröjning som införts av de aktuella buffertinställningarna. + + + + Audio Upstream Rate depends on the current audio packet size and compression setting. Make sure that the upstream rate is not higher than your available internet upload speed (check this with a service such as speedtest.net). + Uppströmsfrekvensen för ljudet beror på den aktuella ljudpaketstorleken och komprimeringsinställningen. Se till att uppströmshastigheten inte är högre än din tillgängliga internetuppladdningshastighet (kolla detta med en tjänst som exempelvis speedtest.net). + + + + If this LED indicator turns red, you will not have much fun using the + Om den här LED-indikatorn blir röd kommer du inte ha så kul med att använda + + + + software. + applikationen. + + + + ASIO Setup + Inställningar för ASIO + + + + Mono-in/Stereo-out + Mono-in/Stereo-ut + + + + Low + Låg + + + + + Normal + Normal + + + + High + Hög + + + + Fancy + Fancy + + + + Compact + Kompakt + + + + preferred + föredraget + + + + + Size: + Storlek: + + + + Buffer Delay + Buffertfördröjning + + + + Buffer Delay: + Buffertfördröjning: + + + The selected audio device could not be used because of the following error: + Den valda ljudenheten kunde inte användas på grund av följande fel: + + + The previous driver will be selected. + Den föregående drivrutinen kommer att väljas. + + + Ok + Okej + + + + Custom + Eget + + + + All Genres + Alla genrer + + + + Genre Rock + Genre Rock + + + + Genre Jazz + Genre Jazz + + + + Genre Classical/Folk/Choral + Genre Klassiskt/Folkmusik/Kör + + + + Default + Standard + + + + CClientSettingsDlgBase + + + Settings + Inställningar + + + + Soundcard + Ljudkort + + + + Device + Enhet + + + + Input Channel Mapping + Kanalval för ingång + + + + + L + V + + + + + R + H + + + + Output Channel Mapping + Kanalval för utgång + + + + Enable Small Network Buffers + Aktivera små nätverksbuffertar + + + + Buffer Delay + Buffertfördröjning + + + + (preferred) + (förvald) + + + + (default) + (standard) + + + + (safe) + (säker) + + + + Driver Setup + Drivrutinsinställningar + + + + Jitter Buffer + Jitterbuffert + + + + Auto + Automatiskt + + + + Local + Lokalt + + + + Server + Server + + + + + Size + Nivå + + + + Misc + Blandade inställningar + + + + Audio Channels + Ljudkanaler + + + + Audio Quality + Ljudkvalitet + + + + New Client Level + Ny klientnivå + + + + Skin + Skal + + + + Language + Språk + + + + % + % + + + Display Channel Levels + Visa kanalnivåer + + + + Custom Central Server Address: + Egen serveradress: + + + + Audio Stream Rate + Ljudströmshastighet + + + + + + val + val + + + + Ping Time + Pingtid + + + + Overall Delay + Total fördröjning + + + + CConnectDlg + + + Server List + Serverlista + + + + The Connection Setup window shows a list of available servers. Server operators can optionally list their servers by music genre. Use the List dropdown to select a genre, click on the server you want to join and press the Connect button to connect to it. Alternatively, double click on on the server name. Permanent servers (those that have been listed for longer than 48 hours) are shown in bold. + Inställningsfönstret visar en lista över tillgängliga servrar. Serveroperatörer kan valfritt lista sina servrar efter musikgenre. Använd rullgardinsmenyn Lista för att välja en genre, klicka på servern du vill gå med i och tryck på Anslutsknappen för att ansluta till den. Alternativt kan du dubbelklicka på servernamnet. Permanenta servrar (de som har listats längre än 48 timmar) visas med fet stil. + + + + Server list view + Serverlista + + + + Server Address + Serveradress + + + + If you know the IP address or URL of a server, you can connect to it using the Server name/Address field. An optional port number can be added after the IP address or URL using a colon as a separator, e.g, example.org: + Om du känner till en servers IP-adress eller URL kan du ansluta till den i fältet Servernamn/adress. Ett valfritt portnummer kan läggas till efter IP-adressen eller URL:en med ett kolon som separator, t.ex. exempel.org: + + + + . The field will also show a list of the most recently used server addresses. + . Fältet visar också en lista över de senast använda serveradresserna. + + + + Server address edit box + Serveradressens redigeringsruta + + + + Holds the current server IP address or URL. It also stores old URLs in the combo box list. + Visar den aktuella serverns IP-adress eller URL. Det lagrar också gamla URL:er i listan med kombinationsrutor. + + + + Server List Selection + Val av server + + + + Selects the server list to be shown. + Väljer server som ska visas. + + + + Server list selection combo box + Kombobox för val av serverlista + + + + Filter + Filter + + + + The server list is filtered by the given text. Note that the filter is case insensitive. + Serverlistan filtreras av den givna texten. Observera att filtret är känsligt för stora och små bokstäver. + + + + Filter edit box + Redigeringsrutan för filtrering + + + + Show All Musicians + Visa alla musiker + + + + 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. + Om du markerar den här kryssrutan visas musikerna på alla servrar. Om du avmarkerar kryssrutan minimeras alla listvyer. + + + + Show all musicians check box + Klickruta för att visa alla musiker + + + + Type # for occupied servers + Skriv # för upptagna servrar + + + + CConnectDlgBase + + + Connection Setup + Anslutningsinställning + + + + List + Lista + + + + Filter + Filter + + + + Show All Musicians + Visa alla musiker + + + + Server Name + Servernamn + + + + Ping Time + Pingtid + + + + Musicians + Musiker + + + + Location + Plats + + + + Server Address + Serveradress + + + Server Name/Address + Servernamn/ipadress (:port) + + + + C&ancel + Av&bryt + + + + &Connect + &Anslut + + + + CHelpMenu + + + &Help + &Hjälp + + + + + Getting &Started... + Komma &igång... + + + + Software &Manual... + &Manual... + + + + What's &This + &Var är detta + + + + &About... + &Om... + + + + CLanguageComboBox + + + Restart Required + Omstart krävs + + + + Please restart the application for the language change to take effect. + Starta om applikationen för att språkändringen ska träda i kraft. + + + + CLicenceDlg + + I &agree to the above licence terms + Jag &accepterar ovanstående licensvillkor + + + + This server requires you accept conditions before you can join. Please read these in the chat window. + Denna server kräver att du accepterar villkoren innan du kan gå med. Läs dem i chattfönstret. + + + + I have read the conditions and &agree. + Jag har &läst villkoren och håller med. + + + + Accept + Acceptera + + + + Decline + Neka + + + By connecting to this server and agreeing to this notice, you agree to the following: + Genom att ansluta till denna server och godkänna detta meddelande, samtycker du till följande: + + + 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 + Du accepterar att all data, ljud eller andra verk som överförs till denna server ägs och skapas av dig eller dina licensgivare, och att du gör dessa data, ljud eller andra verk tillgängliga via följande Creative Commons-licens (för mer information om detta licens, se + + + You are free to: + Du är fri att: + + + Share + Dela + + + copy and redistribute the material in any medium or format + kopiera och omfördela materialet i vilket medium eller format som helst + + + Adapt + Anpssa + + + remix, transform, and build upon the material + remixa, transformera och bygga vidare på materialet + + + The licensor cannot revoke these freedoms as long as you follow the license terms. + Licensgivaren kan inte återkalla dessa friheter så länge du följer licensvillkoren. + + + Under the following terms: + Under följande villkor: + + + Attribution + Erkännande + + + You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. + Du måste ge lämplig kredit, ange en länk till licensen och ange om ändringar gjordes. Du kan göra det på något rimligt sätt, men inte på något sätt som antyder att licensgivaren godkänner dig eller din användning. + + + NonCommercial + Ickekommersiell + + + You may not use the material for commercial purposes. + Du får inte använda materialet för kommersiella ändamål. + + + ShareAlike + Dela lika + + + If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. + Om du remixar, omvandlar eller bygger på materialet måste du distribuera dina bidrag under samma licens som originalet. + + + No additional restrictions + Inga ytterligare begränsningar + + + You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. + Du får inte tillämpa juridiska villkor eller tekniska åtgärder som lagligen begränsar andra från att göra något som licensen tillåter. + + + + CMultiColorLED + + + Red + Röd + + + + Yellow + Gul + + + + Green + Grön + + + + CMusProfDlg + + + No Name + Inget namn + + + + + Musician Profile + Musikprofil + + + + Alias/Name + Alias/Namn + + + + Instrument + Instrument + + + + Country + Land + + + + City + Stad + + + + Skill + Färdighetsnivå + + + + &Close + &Stäng + + + + + + None + Ingen + + + + Beginner + Nybörjare + + + + Intermediate + Mellannivå + + + + Expert + Expert + + + + Write your name or an alias here so the other musicians you want to play with know who you are. You may also add a picture of the instrument you play and a flag of the country you are located in. Your city and skill level playing your instrument may also be added. + Skriv ditt namn eller ett alias här så att de andra musikerna du vill spela med vet vem du är. Du kan också lägga till en bild av instrumentet du spelar och en flagga för det land du befinner dig i. Din stad och din färdighetsnivå som spelar ditt instrument kan också läggas till. + + + + What you set here will appear at your fader on the mixer board when you are connected to a Jamulus server. This tag will also be shown at each client which is connected to the same server as you. + Det du ställer in här visas på din fader på mixerkortet när du är ansluten till en Jamulus-server. Den här taggen kommer också att visas vid varje klient som är ansluten till samma server som du. + + + + Alias or name edit box + Redigeringsruta för alias eller namn + + + + Instrument picture button + Knapp för instrumentbild + + + + Country flag button + Knapp för landsflagga + + + + City edit box + Redigeringsruta för stad + + + + Skill level combo box + Kombinationsruta för färdighetsnivå + + + + Drum Set + Trumset + + + + Djembe + Djembe + + + + Electric Guitar + Elgitarr + + + + Acoustic Guitar + Akustisk gitarr + + + + Bass Guitar + Basgitarr + + + + Keyboard + Klaviatur + + + + Synthesizer + Synthesizer + + + + Grand Piano + Flygel + + + + Accordion + Dragspel + + + + Vocal + Sång + + + + Microphone + Mikrofon + + + + Harmonica + Munspel + + + + Trumpet + Trumpet + + + + Trombone + Trombon + + + + French Horn + Valthorn + + + + Tuba + Tuba + + + + Saxophone + Saxofon + + + + Clarinet + Klarinett + + + + Flute + Flöjt + + + + Violin + Fiol + + + + Cello + Cello + + + + Double Bass + Kontrabas + + + + Recorder + Inspelningsapparat + + + + Streamer + Streamer + + + + Listener + Lyssnare + + + + Guitar+Vocal + Gitarr+Sång + + + + Keyboard+Vocal + Klaviatur+Sång + + + + Bodhran + Klaviatur+Sång + + + + Bassoon + Fagott + + + + Oboe + Oboe + + + + Harp + Harpa + + + + Viola + Altfiol + + + + Congas + Congas + + + + Bongo + Bongo + + + + Vocal Bass + Sång (bas) + + + + Vocal Tenor + Sång (tenor) + + + + Vocal Alto + Sång (alt) + + + + Vocal Soprano + Sång (sopran) + + + + Banjo + Banjo + + + + Mandolin + Mandolin + + + + Ukulele + Ukulele + + + + Bass Ukulele + Ukulele (bas) + + + + Vocal Baritone + Sång (baryton) + + + + Vocal Lead + Sång (Solist) + + + + Mountain Dulcimer + Mountain Dulcimer + + + + Scratching + Scratching + + + + Rapping + Rapp + + + + CServerDlg + + + Client List + Klientlista + + + + 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. + Klientlistan visar alla klienter som för närvarande är anslutna till den här servern. Viss information om klienterna som IP-adressen och namnet ges för varje ansluten klient. + + + + Connected clients list view + Lista över anslutna klienter + + + + Start Minimized on Operating System Start + Starta Minimerad vid operativsystemets start + + + + If the start minimized on operating system start check box is checked, the server will be started when the operating system starts up and is automatically minimized to a system task bar icon. + Om kryssrutan Starta minimerad på operativsystemets start är markerad kommer servern att startas när operativsystemet startar och minimeras automatiskt till en ikon för systemaktivitetsfält. + + + Show Creative Commons Licence Dialog + Visa Creative Commons licensedialog + + + If enabled, a Creative Commons BY-NC-SA 4.0 Licence dialog is shown each time a new user connects the server. + Om den är aktiverad visas en Creative Commons BY-NC-SA 4.0 licensdialog varje gång en ny användare ansluter till servern. + + + + Make My Server Public + Gör min server publik + + + + If the Make My Server Public check box is checked, this server registers itself at the central server so that all users of the application can see the server in the connect dialog server list and connect to it. The registration of the server is renewed periodically to make sure that all servers in the connect dialog server list are actually available. + Om kryssrutan Gör min server publik är markerad, registrerar den här servern sig själv på den centrala servern så att alla användare av applikationen kan se servern i anslutningsdialogens serverlista och ansluta till den. Registreringen av servern förnyas regelbundet för att se till att alla servrar i listan för anslutningsdialogserver är tillgängliga. + + + + Register Server Status + Serverstatus om servern är registrerad + + + + If the Make My Server Public check box is checked, this will show whether registration with the central server is successful. If the registration failed, please choose another server list. + Om kryssrutan Gör min server publik är markerad kommer detta att visa om registrering med den centrala servern är gjord. Om registreringen misslyckades, välj en annan serverlista. + + + + Custom Central Server Address + Anpassad centralserveradress + + + + The custom central server address is the IP address or URL of the central server at which the server list of the connection dialog is managed. + Den anpassade centralserveradressen är IP-adressen eller URL:en för den centrala servern där serverlistan hanteras. + + + + Central server address line edit + Ändra anpassad centralserveradress + + + + Server List Selection + Val av serverlista + + + + Selects the server list (i.e. central server address) in which your server will be added. + Väljer serverlistan (dvs. centralserveradress) där servern ska läggas till. + + + + Server list selection combo box + Kombineringsknapp för val av serverlista + + + + Server Name + Servernamn + + + + The server name identifies your server in the connect dialog server list at the clients. + Servernamnet identifierar din server i anslutningsdialogens serverlista hos klienterna. + + + + Server name line edit + Ändra servernamnet + + + + Location City + Stad + + + + 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. + Här kan man visa serverns plats. Om en stad anges kommer det att visas i listan för anslutningsdialogserver på klienterna. + + + + City where the server is located line edit + Ändra var servern befinner sig + + + + Location country + Land + + + + 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. + Landet där servern ligger kan skrivas in här. Om ett land anges kommer det att visas i listan för anslutningsdialogserver på klienterna. + + + + Country where the server is located combo box + Kombineringsknapp för val av serverplacering + + + + Display dialog to select recording directory button + Knapp för att visa dialogruta för att välja inspelningskatalog + + + + + Main Recording Directory + Huvudinspelningskatalog + + + + Click the button to open the dialog that allows the main recording directory to be selected.The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). + Klicka på knappen för att öppna dialogrutan som gör det möjligt att välja den huvudsakliga inspelningskatalogen. Den valda platsen måste finnas och vara skrivbar (tillåta skapandet av underkataloger av den användare som Jamulus körs via.). + + + + Main recording directory text box (read-only) + Textruta för huvudinspelningskatalog (skrivskyddad) + + + + The current value of the main recording directory. The chosen value must exist and be writeable (allow creation of sub-directories by the user Jamulus is running as). Click the button to open the dialog that allows the main recording directory to be selected. + Det aktuella värdet för huvudinspelningskatalogen. Det valda värdet måste existera och vara skrivbart (tillåta skapandet av underkataloger av användaren som Jamulus körs av). Klicka på knappen för att öppna dialogrutan som gör det möjligt att välja huvudinspelningskatalogen. + + + + Clear the recording directory button + Knappen för att rensa inspelningskatalogen + + + + Clear Recording Directory + Rensa inspelningskatalogen + + + + Click the button to clear the currently selected recording directory. This will prevent recording until a new value is selected. + Klicka på knappen för att rensa den valda inspelningskatalogen. Detta förhindrar inspelning tills ett ny plats valts. + + + + Checkbox to turn on or off server recording + Knapp för att slå på eller av serverinspelning + + + + Enable Recorder + Starta serverinspelning + + + + Checked when the recorder is enabled, otherwise unchecked. The recorder will run when a session is in progress, if (set up correctly and) enabled. + Markerad när inspelaren är aktiverad, annars avmarkerad. Inspelaren kommer att köras när en session pågår, om (inställt korrekt och) aktiverat. + + + + Current session directory text box (read-only) + Nuvarande inspelningskatalogtext (skrivskyddad) + + + + Current Session Directory + Aktuell inspelningskatalog + + + + Enabled during recording and holds the current recording session directory. Disabled after recording or when the recorder is not enabled. + Aktiverad under inspelning och har den aktuella inspelningskatalogen. Inaktiverad efter inspelning eller när inspelaren inte är aktiverad. + + + + Recorder status label + Inspelningsstatusetikett + + + + Recorder Status + Inspelningsstatus + + + + Displays the current status of the recorder. The following values are possible: + Visar inspelarens aktuella status. Dessa värden är möjliga: + + + + No recording directory has been set or the value is not useable + Ingen inspelningskatalog har ställts in eller värdet kan inte användas + + + + Recording has been switched off + Inspelningen har stängts av + + + + by the UI checkbox + genom kryssrutan + + + + , either by the UI checkbox or SIGUSR2 being received + , antingen genom kryssrutan eller SIGUSR2 som tas emot + + + + There is no one connected to the server to record + Det är ingen ansluten till servern att spela in + + + + The performers are being recorded to the specified session directory + Artisterna spelas in i den angivna sessionskatalogen + + + + NOTE + NOTERING + + + + If the recording directory is not useable, the problem will be displayed in place of the directory. + Om inspelningskatalogen inte kan användas visas problemet istället för katalogen. + + + + Server welcome message edit box + Serverns redigeringsruta för välkomstmeddelandet + + + + Server Welcome Message + Serverns välkomstmeddelande + + + + A server welcome message text is displayed in the chat window if a musician enters the server. If no message is set, the server welcome is disabled. + En välkomstmeddelandetext för servern visas i chattfönstret om en musiker kommer in på servern. Om inget meddelande är inställt är serverns välkomst inaktiverad. + + + + Type a message here. If no message is set, the server welcome is disabled. + Skriv ett meddelande här. Om inget meddelande är inställt är serverns välkomst inaktiverad. + + + + software upgrade available + mjukvaruuppdatering tillgänglig + + + + ERROR + FEL + + + Displays the current status of the recorder. + Visar inspelarens aktuella status. + + + + Request new recording button + Begär ny inspelningsknapp + + + + New Recording + Ny inspelning + + + + During a recording session, the button can be used to start a new recording. + Under en inspelningssession kan knappen användas för att starta en ny inspelning. + + + + + E&xit + &Avbryt + + + + &Hide + &Göm + + + + + + server + server + + + + &Open + &Öppna + + + server + server + + + + Server + Server + + + + &Window + &Fönster + + + + Select Main Recording Directory + Välj huvudinspelningskatalog + + + Predefined Address + Förvald adress + + + + Recording + Inspelning + + + + Not recording + Inspelning inte aktiv + + + + Not initialised + Inte initierad + + + + Not enabled + Inte aktiv + + + + Unregistered + Oregistrerad + + + + Bad address + Felaktig adress + + + + Registration requested + Registreringsförfrågan skickad + + + + Registration failed + Registrering misslyckades + + + + Check server version + Kontrollera serverversionen + + + + Registered + Regisrterad + + + + Central Server full + Centrala servern är full + + + + Your server version is too old + Din serverversion är för gammal + + + + Requirements not fulfilled + Kraven uppfylls inte + + + + Unknown value + Okänt värde + + + + CServerDlgBase + + + Client IP:Port + Klient IP:Port + + + + + Name + Namn + + + + Jitter Buffer Size + Jitterbufferstorlek + + + + Server Setup + Serverinställningar + + + + Enable Jam Recorder + Aktivera Jam-inspelning + + + + New Recording + Ny inspelning + + + + Chat Window Welcome (HTML/CSS Supported) + Välkomstmeddelande som visas i chattfönstret (HTML/CSS stöds) + + + + Options + Alternativ + + + + Language + Språk + + + + Recording Directory + Inspelningskatalog + + + + Start Minimized on Windows Start + Starta minimerad när Micosoft Windows startas + + + Show Creative Commons BY-NC-SA 4.0 Licence Dialog + Visa Creative Commons BY-NC-SA 4.0 licensdialog + + + + Update check + Uppdateringskontroll + + + + Make My Server Public (Register My Server in the Server List) + Gör min server publik (registrera min server i serverlistan) + + + + Genre + Genre + + + + + STATUS + STATUS + + + + Custom Central Server Address: + Anpassad centralserveradress: + + + + My Server Info + Min serverinformation + + + + Location: City + Plats: Stad + + + + Location: Country + Plats: Land + + + Enable jam recorder + Aktivera inspelning + + + New recording + Ny inspelning + + + Recordings folder + Inspelningsfolder + + + TextLabelNameVersion + TextLabelNameVersion + + + + CSound + + + Error closing stream: $s + Fel vid stängning av ström: $s + + + + The Jack server is not running. This software requires a Jack server to run. Normally if the Jack server is not running this software will automatically start the Jack server. It seems that this auto start has not worked. Try to start the Jack server manually. + Jack-servern körs inte. Denna programvara kräver att en Jack-server körs. Normalt om Jack-servern inte kör startar programvaran automatiskt Jack-servern. Det verkar som om denna automatiska start inte har fungerat. Försök starta Jack-servern manuellt. + + + + The Jack server sample rate is different from the required one. The required sample rate is: + Jackservers samplingsfrekvens skiljer sig från den önskade. Den nödvändiga bithastigheten är: + + + + You can use a tool like <i><a href=http://qjackctl.sourceforge.net>QJackCtl</a></i> to adjust the Jack server sample rate. + Du kan använda ett verktyg som <i><a href=http://qjackctl.sourceforge.net>QJackCtl</a></i> för att justera Jack-serverns samplingsfrekvens. + + + + Make sure to set the Frames/Period to a low value like + Se till att ställa in ramarna/perioden till ett så lågt värde som + + + + to achieve a low delay. + för att uppnå en låg fördröjning. + + + + + The Jack port registering failed. + Registreringen av Jack-porten misslyckades. + + + + Cannot activate the Jack client. + Kan inte aktivera Jack-klienten. + + + + The Jack server was shut down. This software requires a Jack server to run. Try to restart the software to solve the issue. + Jack-servern stängdes av. Denna programvara kräver att en Jack-server körs. Försök starta om programvaran för att lösa problemet. + + + + CoreAudio input AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. + CoreAudio-ingång AudioHardwareGetProperty misslyckades. Det verkar som om det inte finns något ljudkort i systemet. + + + + CoreAudio output AudioHardwareGetProperty call failed. It seems that no sound card is available in the system. + CoreAudio-utgång AudioHardwareGetProperty misslyckades. Det verkar som om det inte finns något ljudkort i systemet. + + + The previously selected audio device is no longer available. The system default audio device will be selected instead. + Den tidigare valda ljudenheten är inte längre tillgänglig. Systemets standardljudenhet har valts i stället. + + + Current audio input device is no longer available. + Nuvarande ljudingångsenhet är inte längre tillgänglig. + + + + Current system audio input device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. + Nuvarande systemljudingångsenhetens samplingsfrekvens för %1 Hz stöds inte. Öppna Audio-MIDI-installationen i Applications->Utilities och försök att ställa in en samplingsfrekvens på %2 Hz. + + + Current audio output device is no longer available. + Nuvarande ljudutgångsenhet är inte längre tillgänglig. + + + + + The current selected audio device is no longer present in the system. + + + + + The audio input device is no longer available. + + + + + The audio output device is no longer available. + + + + + Current system audio output device sample rate of %1 Hz is not supported. Please open the Audio-MIDI-Setup in Applications->Utilities and try to set a sample rate of %2 Hz. + Nuvarande systemljudutgångsenhetens samplingsfrekvens för %1 Hz stöds inte. Öppna Audio-MIDI-installationen i Applications->Utilities och försök att ställa in en samplingshastighet på %2 Hz. + + + + The audio input stream format for this audio device is not compatible with this software. + Ljudingångsströmformatet för denna ljudenhet är inte kompatibelt med den här programvaran. + + + + The audio output stream format for this audio device is not compatible with this software. + Ljudutmatningsströmformatet för denna ljudenhet är inte kompatibelt med den här programvaran. + + + + The buffer sizes of the current input and output audio device cannot be set to a common value. Please choose other input/output audio devices in your system settings. + Buffertstorlekarna för den aktuella ingångs- och utgångsljudenheten kan inte ställas in på ett gemensamt värde. Välj andra input/output-ljudenheter i systeminställningarna. + + + + The audio driver could not be initialized. + Ljuddrivrutinen kunde inte initialiseras. + + + + The audio device does not support the required sample rate. The required sample rate is: + Ljudenheten stöder inte den valda samplingsfrekvensen. Den valda provhastigheten är: + + + + The audio device does not support setting the required sampling rate. This error can happen if you have an audio interface like the Roland UA-25EX where you set the sample rate with a hardware switch on the audio device. If this is the case, please change the sample rate to + Ljudenheten stöder inte inställning av önskad samplingsfrekvens. Det här felet kan hända om du har ett ljudgränssnitt som Roland UA-25EX där du ställer in samtalstakten med en hårdvaruskontakt på ljudenheten. Om detta är fallet, ändra provhastigheten till + + + + Hz on the device and restart the + Hz på enheten och starta om + + + + software. + applikationen. + + + + The audio device does not support the required number of channels. The required number of channels for input and output is: + Ljudenheten stöder inte det valda antalet kanaler. Det valda antalet kanaler för in- och utmatning är: + + + + + Required audio sample format not available. + Nödvändigt ljudformat är inte tillgängligt. + + + + No ASIO audio device (driver) found. + Ingen ASIO-ljudenhet (drivrutin) hittades. + + + + The + Programvaran + + + + software requires the low latency audio interface ASIO to work properly. This is not a 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 the ASIO4All driver. + kräver det låga latentljudgränssnittet ASIO för att fungera korrekt. Detta är inte ett vanligt Windows ljudgränssnitt och därför krävs en speciell ljuddrivrutin. Antingen har ditt ljudkort en inbyggd ASIO-drivrutin (som rekommenderas) eller så kanske du vill använda alternativa drivrutiner som ASIO4All-drivrutinen. + + + + CSoundBase + + Invalid device selection. + Ogiltigt enhetsval. + + + The audio driver properties have changed to a state which is incompatible with this software. The selected audio device could not be used because of the following error: + Ljuddrivrutinens egenskaper har ändrats till ett tillstånd som inte är kompatibelt med den här programvaran. Den valda ljudenheten kunde inte användas på grund av följande fel: + + + Please restart the software. + Starta om programvaran. + + + Close + Stäng + + + + The selected audio device could not be used because of the following error: + Den valda ljudenheten kunde inte användas på grund av följande fel: + + + + The previous driver will be selected. + Den föregående drivrutinen kommer att väljas. + + + + The previously selected audio device is no longer available or the audio driver properties have changed to a state which is incompatible with this software. We now try to find a valid audio device. This new audio device might cause audio feedback. So, before connecting to a server, please check the audio device setting. + + + + + No usable + Ingen användbar + + + + audio device (driver) found. + ljudenhet (drivrutin) hittades. + + + + In the following there is a list of all available drivers with the associated error message: + I det följande finns en lista över alla tillgängliga drivrutiner med tillhörande felmeddelande: + + + + Do you want to open the ASIO driver setups? + Vill du öppna ASIO-drivrutinens inställningar? + + + + could not be started because of audio interface issues. + kunde inte startas på grund av problem med ljudgränssnittet. + + + + QCoreApplication + + + , Version + , Version + + + + Internet Jam Session Software + Internet Jam Session Software + + + + Released under the GNU General Public License (GPL) + Släppt under GNU General Public License (GPL) + + + + global + + + For more information use the What's This help (help menu, right mouse button or Shift+F1) + För mer information använd hjälpen (hjälpmeny, höger musknapp eller Shift + F1) + + + diff --git a/src/resources.qrc b/src/resources.qrc index 1e4b78c2d9..f94cebb6eb 100755 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -1,22 +1,35 @@ - - res/translation/translation_de_DE.qm + + res/translation/translation_de_DE.qm - - res/translation/translation_fr_FR.qm + + res/translation/translation_fr_FR.qm - - res/translation/translation_pt_PT.qm + + res/translation/translation_pt_PT.qm - - res/translation/translation_es_ES.qm + + res/translation/translation_pt_BR.qm - - res/translation/translation_nl_NL.qm + + res/translation/translation_es_ES.qm - - res/translation/translation_it_IT.qm + + res/translation/translation_nl_NL.qm + + res/translation/translation_it_IT.qm + + + res/translation/translation_pl_PL.qm + + + res/translation/translation_sk_SK.qm + + + res/translation/translation_sv_SE.qm + + res/CLEDDisabledSmall.png res/CLEDGreenArrow.png @@ -25,8 +38,11 @@ res/CLEDGreySmall.png res/CLEDRedSmall.png res/CLEDYellowSmall.png + res/IndicatorGreen.png + res/IndicatorYellow.png + res/IndicatorRed.png res/HLEDGreenSmall.png - res/HLEDGreySmall.png + res/HLEDBlackSmall.png res/HLEDRedSmall.png res/HLEDYellowSmall.png @@ -45,8 +61,11 @@ res/instruments/drumset.png res/instruments/eguitar.png res/instruments/saxophone.png + res/instruments/scratching.png + res/instruments/rapping.png res/instruments/trumpet.png res/instruments/microphone.png + res/instruments/mountaindulcimer.png res/instruments/keyboard.png res/instruments/violin.png res/instruments/aguitar.png @@ -75,15 +94,19 @@ res/instruments/congas.png res/instruments/bongo.png res/instruments/vocalbass.png + res/instruments/vocalbaritone.png + res/instruments/vocallead.png res/instruments/vocaltenor.png res/instruments/vocalalto.png res/instruments/vocalsoprano.png res/instruments/banjo.png res/instruments/mandolin.png + res/instruments/ukulele.png + res/instruments/bassukulele.png res/fronticon.png - res/mainicon.png + res/fronticonserver.png res/flags/flagnone.png diff --git a/src/server.cpp b/src/server.cpp index 11ad954f35..6c5d5bd244 100755 --- a/src/server.cpp +++ b/src/server.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -59,8 +59,8 @@ CHighPrecisionTimer::CHighPrecisionTimer ( const bool bNewUseDoubleSystemFrameSi veciTimeOutIntervals[2] = 0; // connect timer timeout signal - QObject::connect ( &Timer, SIGNAL ( timeout() ), - this, SLOT ( OnTimer() ) ); + QObject::connect ( &Timer, &QTimer::timeout, + this, &CHighPrecisionTimer::OnTimer ); } void CHighPrecisionTimer::Start() @@ -220,36 +220,36 @@ void CHighPrecisionTimer::run() // CServer implementation ****************************************************** CServer::CServer ( const int iNewMaxNumChan, - const int iMaxDaysHistory, const QString& strLoggingFileName, const quint16 iPortNumber, const QString& strHTMLStatusFileName, - const QString& strHistoryFileName, - const QString& strServerNameForHTMLStatusFile, const QString& strCentralServer, const QString& strServerInfo, + const QString& strServerListFilter, const QString& strNewWelcomeMessage, const QString& strRecordingDirName, - const bool bNCentServPingServerInList, const bool bNDisconnectAllClientsOnQuit, const bool bNUseDoubleSystemFrameSize, + const bool bNUseMultithreading, + const bool bDisableRecording, const ELicenceType eNLicenceType ) : bUseDoubleSystemFrameSize ( bNUseDoubleSystemFrameSize ), + bUseMultithreading ( bNUseMultithreading ), iMaxNumChannels ( iNewMaxNumChan ), Socket ( this, iPortNumber ), - Logging ( iMaxDaysHistory ), - JamRecorder ( strRecordingDirName ), - bEnableRecording ( !strRecordingDirName.isEmpty() ), + Logging ( ), + iFrameCount ( 0 ), bWriteStatusHTMLFile ( false ), + strServerHTMLFileListName ( strHTMLStatusFileName ), HighPrecisionTimer ( bNUseDoubleSystemFrameSize ), ServerListManager ( iPortNumber, strCentralServer, strServerInfo, + strServerListFilter, iNewMaxNumChan, - bNCentServPingServerInList, &ConnLessProtocol ), + bDisableRecording ( bDisableRecording ), bAutoRunMinimized ( false ), - strWelcomeMessage ( strNewWelcomeMessage ), eLicenceType ( eNLicenceType ), bDisconnectAllClientsOnQuit ( bNDisconnectAllClientsOnQuit ), pSignalHandler ( CSignalHandler::getSingletonP() ) @@ -334,14 +334,14 @@ CServer::CServer ( const int iNewMaxNumChan, // do not know the required sizes for the vectors, we allocate memory for // the worst case here: - // we always use stereo audio buffers (which is the worst case) - vecsSendData.Init ( 2 /* stereo */ * DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES /* worst case buffer size */ ); - // allocate worst case memory for the temporary vectors vecChanIDsCurConChan.Init ( iMaxNumChannels ); - vecvecdGains.Init ( iMaxNumChannels ); - vecvecdPannings.Init ( iMaxNumChannels ); + vecvecfGains.Init ( iMaxNumChannels ); + vecvecfPannings.Init ( iMaxNumChannels ); vecvecsData.Init ( iMaxNumChannels ); + vecvecsSendData.Init ( iMaxNumChannels ); + vecvecfIntermediateProcBuf.Init ( iMaxNumChannels ); + vecvecbyCodedData.Init ( iMaxNumChannels ); vecNumAudioChannels.Init ( iMaxNumChannels ); vecNumFrameSizeConvBlocks.Init ( iMaxNumChannels ); vecUseDoubleSysFraSizeConvBuf.Init ( iMaxNumChannels ); @@ -350,62 +350,59 @@ CServer::CServer ( const int iNewMaxNumChan, for ( i = 0; i < iMaxNumChannels; i++ ) { // init vectors storing information of all channels - vecvecdGains[i].Init ( iMaxNumChannels ); - vecvecdPannings[i].Init ( iMaxNumChannels ); + vecvecfGains[i].Init ( iMaxNumChannels ); + vecvecfPannings[i].Init ( iMaxNumChannels ); - // we always use stereo audio buffers (see "vecsSendData") + // we always use stereo audio buffers (which is the worst case) vecvecsData[i].Init ( 2 /* stereo */ * DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES /* worst case buffer size */ ); - } - // allocate worst case memory for the coded data - vecbyCodedData.Init ( MAX_SIZE_BYTES_NETW_BUF ); + // (note that we only allocate iMaxNumChannels buffers for the send + // and coded data because of the OMP implementation) + vecvecsSendData[i].Init ( 2 /* stereo */ * DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES /* worst case buffer size */ ); - // allocate worst case memory for the channel levels - vecChannelLevels.Init ( iMaxNumChannels ); + // allocate worst case memory for intermediate processing buffers in float precision + vecvecfIntermediateProcBuf[i].Init ( 2 /* stereo */ * DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES /* worst case buffer size */ ); - // enable history graph (if requested) - if ( !strHistoryFileName.isEmpty() ) - { - Logging.EnableHistory ( strHistoryFileName ); + // allocate worst case memory for the coded data + vecvecbyCodedData[i].Init ( MAX_SIZE_BYTES_NETW_BUF ); } + // allocate worst case memory for the channel levels + vecChannelLevels.Init ( iMaxNumChannels ); + // enable logging (if requested) if ( !strLoggingFileName.isEmpty() ) { - // in case the history is enabled and a logging file name is - // given, parse the logging file for old entries which are then - // added in the history on software startup - if ( !strHistoryFileName.isEmpty() ) - { - Logging.ParseLogFile ( strLoggingFileName ); - } - Logging.Start ( strLoggingFileName ); } // HTML status file writing - if ( !strHTMLStatusFileName.isEmpty() ) + if ( !strServerHTMLFileListName.isEmpty() ) + { + // activate HTML file writing and write initial file + bWriteStatusHTMLFile = true; + WriteHTMLChannelList(); + } + + // manage welcome message: if the welcome message is a valid link to a local + // file, the content of that file is used as the welcome message (#361) + SetWelcomeMessage ( strNewWelcomeMessage ); // first use given text, may be overwritten + + if ( QFileInfo ( strNewWelcomeMessage ).exists() ) { - QString strCurServerNameForHTMLStatusFile = strServerNameForHTMLStatusFile; + QFile file ( strNewWelcomeMessage ); - // if server name is empty, substitude a default name - if ( strCurServerNameForHTMLStatusFile.isEmpty() ) + if ( file.open ( QIODevice::ReadOnly | QIODevice::Text ) ) { - strCurServerNameForHTMLStatusFile = "[server address]"; + // use entire file content for the welcome message + SetWelcomeMessage ( file.readAll() ); } - - // (the static cast to integer of the port number is required so that it - // works correctly under Linux) - StartStatusHTMLFileWriting ( strHTMLStatusFileName, - strCurServerNameForHTMLStatusFile + ":" + - QString().number( static_cast ( iPortNumber ) ) ); } - // Enable jam recording (if requested) - kicks off the thread - if ( bEnableRecording ) - { - JamRecorder.Init ( this, iServerFrameSizeSamples ); - } + // enable jam recording (if requested) - kicks off the thread (note + // that jam recorder needs the frame size which is given to the jam + // recorder in the SetRecordingDir() function) + SetRecordingDir ( strRecordingDirName ); // enable all channels (for the server all channel must be enabled the // entire life time of the software) @@ -414,71 +411,87 @@ CServer::CServer ( const int iNewMaxNumChan, vecChannels[i].SetEnable ( true ); } + // introduced by kraney (#653): "increased the size of the thread pool" + if ( bUseMultithreading ) + { + QThreadPool::globalInstance()->setMaxThreadCount ( QThread::idealThreadCount() * 4 ); + } + // Connections ------------------------------------------------------------- // connect timer timeout signal - QObject::connect ( &HighPrecisionTimer, SIGNAL ( timeout() ), - this, SLOT ( OnTimer() ) ); + QObject::connect ( &HighPrecisionTimer, &CHighPrecisionTimer::timeout, + this, &CServer::OnTimer ); + + QObject::connect ( &ConnLessProtocol, &CProtocol::CLMessReadyForSending, + this, &CServer::OnSendCLProtMessage ); + + QObject::connect ( &ConnLessProtocol, &CProtocol::CLPingReceived, + this, &CServer::OnCLPingReceived ); + + QObject::connect ( &ConnLessProtocol, &CProtocol::CLPingWithNumClientsReceived, + this, &CServer::OnCLPingWithNumClientsReceived ); + + QObject::connect ( &ConnLessProtocol, &CProtocol::CLRegisterServerReceived, + this, &CServer::OnCLRegisterServerReceived ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLMessReadyForSending ( CHostAddress, CVector ) ), - this, SLOT ( OnSendCLProtMessage ( CHostAddress, CVector ) ) ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLRegisterServerExReceived, + this, &CServer::OnCLRegisterServerExReceived ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLPingReceived ( CHostAddress, int ) ), - this, SLOT ( OnCLPingReceived ( CHostAddress, int ) ) ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLUnregisterServerReceived, + this, &CServer::OnCLUnregisterServerReceived ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLPingWithNumClientsReceived ( CHostAddress, int, int ) ), - this, SLOT ( OnCLPingWithNumClientsReceived ( CHostAddress, int, int ) ) ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLReqServerList, + this, &CServer::OnCLReqServerList ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLRegisterServerReceived ( CHostAddress, CHostAddress, CServerCoreInfo ) ), - this, SLOT ( OnCLRegisterServerReceived ( CHostAddress, CHostAddress, CServerCoreInfo ) ) ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLRegisterServerResp, + this, &CServer::OnCLRegisterServerResp ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLUnregisterServerReceived ( CHostAddress ) ), - this, SLOT ( OnCLUnregisterServerReceived ( CHostAddress ) ) ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLSendEmptyMes, + this, &CServer::OnCLSendEmptyMes ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLReqServerList ( CHostAddress ) ), - this, SLOT ( OnCLReqServerList ( CHostAddress ) ) ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLDisconnection, + this, &CServer::OnCLDisconnection ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLRegisterServerResp ( CHostAddress, ESvrRegResult ) ), - this, SLOT ( OnCLRegisterServerResp ( CHostAddress, ESvrRegResult ) ) ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLReqVersionAndOS, + this, &CServer::OnCLReqVersionAndOS ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLSendEmptyMes ( CHostAddress ) ), - this, SLOT ( OnCLSendEmptyMes ( CHostAddress ) ) ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLVersionAndOSReceived, + this, &CServer::CLVersionAndOSReceived ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLDisconnection ( CHostAddress ) ), - this, SLOT ( OnCLDisconnection ( CHostAddress ) ) ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLReqConnClientsList, + this, &CServer::OnCLReqConnClientsList ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLReqVersionAndOS ( CHostAddress ) ), - this, SLOT ( OnCLReqVersionAndOS ( CHostAddress ) ) ); + QObject::connect ( &ServerListManager, &CServerListManager::SvrRegStatusChanged, + this, &CServer::SvrRegStatusChanged ); - QObject::connect ( &ConnLessProtocol, - SIGNAL ( CLReqConnClientsList ( CHostAddress ) ), - this, SLOT ( OnCLReqConnClientsList ( CHostAddress ) ) ); + QObject::connect ( &JamController, &recorder::CJamController::RestartRecorder, + this, &CServer::RestartRecorder ); - QObject::connect ( &ServerListManager, - SIGNAL ( SvrRegStatusChanged() ), - SIGNAL ( SvrRegStatusChanged() ) ); + QObject::connect ( &JamController, &recorder::CJamController::StopRecorder, + this, &CServer::StopRecorder ); - QObject::connect( &JamRecorder, - SIGNAL ( RecordingSessionStarted ( QString ) ), - SIGNAL ( RecordingSessionStarted ( QString ) ) ); + QObject::connect ( &JamController, &recorder::CJamController::RecordingSessionStarted, + this, &CServer::RecordingSessionStarted ); - QObject::connect ( QCoreApplication::instance(), - SIGNAL ( aboutToQuit() ), - this, SLOT ( OnAboutToQuit() ) ); + QObject::connect ( &JamController, &recorder::CJamController::EndRecorderThread, + this, &CServer::EndRecorderThread ); - QObject::connect ( pSignalHandler, - SIGNAL ( HandledSignal ( int ) ), - this, SLOT ( OnHandledSignal ( int ) ) ); + QObject::connect ( this, &CServer::Stopped, + &JamController, &recorder::CJamController::Stopped ); + + QObject::connect ( this, &CServer::ClientDisconnected, + &JamController, &recorder::CJamController::ClientDisconnected ); + + qRegisterMetaType> ( "CVector" ); + QObject::connect ( this, &CServer::AudioFrame, + &JamController, &recorder::CJamController::AudioFrame ); + + QObject::connect ( QCoreApplication::instance(), &QCoreApplication::aboutToQuit, + this, &CServer::OnAboutToQuit ); + + QObject::connect ( pSignalHandler, &CSignalHandler::HandledSignal, + this, &CServer::OnHandledSignal ); connectChannelSignalsToServerSlots(); @@ -532,10 +545,10 @@ inline void CServer::connectChannelSignalsToServerSlots() this, pOnServerAutoSockBufSizeChangeCh ); connectChannelSignalsToServerSlots(); -}; +} template<> -inline void CServer::connectChannelSignalsToServerSlots<0>() {}; +inline void CServer::connectChannelSignalsToServerSlots<0>() {} void CServer::CreateAndSendJitBufMessage ( const int iCurChanID, const int iNNumFra ) @@ -543,6 +556,26 @@ void CServer::CreateAndSendJitBufMessage ( const int iCurChanID, vecChannels[iCurChanID].CreateJitBufMes ( iNNumFra ); } +CServer::~CServer() +{ + for ( int i = 0; i < iMaxNumChannels; i++ ) + { + // free audio encoders and decoders + opus_custom_encoder_destroy ( OpusEncoderMono[i] ); + opus_custom_decoder_destroy ( OpusDecoderMono[i] ); + opus_custom_encoder_destroy ( OpusEncoderStereo[i] ); + opus_custom_decoder_destroy ( OpusDecoderStereo[i] ); + opus_custom_encoder_destroy ( Opus64EncoderMono[i] ); + opus_custom_decoder_destroy ( Opus64DecoderMono[i] ); + opus_custom_encoder_destroy ( Opus64EncoderStereo[i] ); + opus_custom_decoder_destroy ( Opus64DecoderStereo[i] ); + + // free audio modes + opus_custom_mode_destroy ( OpusMode[i] ); + opus_custom_mode_destroy ( Opus64Mode[i] ); + } +} + void CServer::SendProtMessage ( int iChID, CVector vecMessage ) { // the protocol queries me to call the function to send the message @@ -553,10 +586,15 @@ void CServer::SendProtMessage ( int iChID, CVector vecMessage ) void CServer::OnNewConnection ( int iChID, CHostAddress RecHostAddr ) { + QMutexLocker locker ( &Mutex ); + // inform the client about its own ID at the server (note that this // must be the first message to be sent for a new connection) vecChannels[iChID].CreateClientIDMes ( iChID ); + // query support for split messages in the client + vecChannels[iChID].CreateReqSplitMessSupportMes(); + // 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.) @@ -574,7 +612,7 @@ void CServer::OnNewConnection ( int iChID, // a channel name request to the client which causes a channel // name message to be transmitted to the server. If the server // receives this message, the channel list will be automatically - // updated (implicitely). + // updated (implicitly). // // Usually it is not required to send the channel list to the // client currently connecting since it automatically requests @@ -587,15 +625,19 @@ void CServer::OnNewConnection ( int iChID, vecChannels[iChID].CreateReqChanInfoMes(); // send welcome message (if enabled) - if ( !strWelcomeMessage.isEmpty() ) + MutexWelcomeMessage.lock(); { - // create formated server welcome message and send it just to - // the client which just connected to the server - const QString strWelcomeMessageFormated = - "Server Welcome Message: " + strWelcomeMessage; + if ( !strWelcomeMessage.isEmpty() ) + { + // create formatted server welcome message and send it just to + // the client which just connected to the server + const QString strWelcomeMessageFormated = + WELCOME_MESSAGE_PREFIX + strWelcomeMessage; - vecChannels[iChID].CreateChatTextMes ( strWelcomeMessageFormated ); + vecChannels[iChID].CreateChatTextMes ( strWelcomeMessageFormated ); + } } + MutexWelcomeMessage.unlock(); // send licence request message (if enabled) if ( eLicenceType != LT_NO_LICENCE ) @@ -606,16 +648,21 @@ void CServer::OnNewConnection ( int iChID, // send version info (for, e.g., feature activation in the client) vecChannels[iChID].CreateVersionAndOSMes(); + // send recording state message on connection + vecChannels[iChID].CreateRecorderStateMes ( JamController.GetRecorderState() ); + // reset the conversion buffers DoubleFrameSizeConvBufIn[iChID].Reset(); DoubleFrameSizeConvBufOut[iChID].Reset(); // logging of new connected channel - Logging.AddNewConnection ( RecHostAddr.InetAddr ); + Logging.AddNewConnection ( RecHostAddr.InetAddr, GetNumberOfConnectedClients() ); } void CServer::OnServerFull ( CHostAddress RecHostAddr ) { + // note: no mutex required here + // inform the calling client that no channel is free ConnLessProtocol.CreateCLServerFullMes ( RecHostAddr ); } @@ -628,16 +675,6 @@ void CServer::OnSendCLProtMessage ( CHostAddress InetAddr, Socket.SendPacket ( vecMessage, InetAddr ); } -void CServer::OnProtcolCLMessageReceived ( int iRecID, - CVector vecbyMesBodyData, - CHostAddress RecHostAddr ) -{ - // connection less messages are always processed - ConnLessProtocol.ParseConnectionLessMessageBody ( vecbyMesBodyData, - iRecID, - RecHostAddr ); -} - void CServer::OnCLDisconnection ( CHostAddress InetAddr ) { // check if the given address is actually a client which is connected to @@ -679,6 +716,10 @@ void CServer::OnAboutToQuit() void CServer::OnHandledSignal ( int sigNum ) { + // show the signal number on the command line (note that this does not work for the Windows command line) +// TODO we should use the ConsoleWriterFactory() instead of qDebug() + qDebug() << "OnHandledSignal: " << sigNum; + #ifdef _WIN32 // Windows does not actually get OnHandledSignal triggered QCoreApplication::instance()->exit(); @@ -686,11 +727,14 @@ void CServer::OnHandledSignal ( int sigNum ) #else switch ( sigNum ) { - case SIGUSR1: RequestNewRecording(); break; + case SIGUSR2: + SetEnableRecording ( !JamController.GetRecordingEnabled() ); + break; + case SIGINT: case SIGTERM: // This should trigger OnAboutToQuit @@ -703,14 +747,6 @@ void CServer::OnHandledSignal ( int sigNum ) #endif } -void CServer::RequestNewRecording() -{ - if ( bEnableRecording ) - { - emit RestartRecorder(); - } -} - void CServer::Start() { // only start if not already running @@ -745,30 +781,20 @@ void CServer::Stop() void CServer::OnTimer() { - int i, j, iUnused; - int iClientFrameSizeSamples = 0; // initialize to avoid a compiler warning - OpusCustomDecoder* CurOpusDecoder; - OpusCustomEncoder* CurOpusEncoder; - unsigned char* pCurCodedData; - /* -// TEST do a timer jitter measurement -static CTimingMeas JitterMeas ( 1000, "test2.dat" ); -JitterMeas.Measure(); +static CTimingMeas JitterMeas ( 1000, "test2.dat" ); JitterMeas.Measure(); // TEST do a timer jitter measurement */ - // Get data from all connected clients ------------------------------------- // some inits - int iNumClients = 0; // init connected client counter - bool bChannelIsNowDisconnected = false; - bool bSendChannelLevels = false; + int iNumClients = 0; // init connected client counter + bChannelIsNowDisconnected = false; // note that the flag must be a member function since QtConcurrent::run can only take 5 params // Make put and get calls thread safe. Do not forget to unlock mutex // afterwards! Mutex.lock(); { // first, get number and IDs of connected channels - for ( i = 0; i < iMaxNumChannels; i++ ) + for ( int i = 0; i < iMaxNumChannels; i++ ) { if ( vecChannels[i].IsConnected() ) { @@ -781,342 +807,346 @@ JitterMeas.Measure(); } } - // process connected channels - for ( i = 0; i < iNumClients; i++ ) + // prepare and decode connected channels + if ( !bUseMultithreading ) { - // get actual ID of current channel - const int iCurChanID = vecChanIDsCurConChan[i]; - - // get and store number of audio channels and compression type - vecNumAudioChannels[i] = vecChannels[iCurChanID].GetNumAudioChannels(); - vecAudioComprType[i] = vecChannels[iCurChanID].GetAudioCompressionType(); - - // get info about required frame size conversion properties - vecUseDoubleSysFraSizeConvBuf[i] = ( !bUseDoubleSystemFrameSize && ( vecAudioComprType[i] == CT_OPUS ) ); - - if ( bUseDoubleSystemFrameSize && ( vecAudioComprType[i] == CT_OPUS64 ) ) + for ( int iChanCnt = 0; iChanCnt < iNumClients; iChanCnt++ ) { - vecNumFrameSizeConvBlocks[i] = 2; - } - else - { - vecNumFrameSizeConvBlocks[i] = 1; + DecodeReceiveData ( iChanCnt, iNumClients ); } + } + else + { + // processing with multithreading +// TODO optimization of the MTBlockSize value + const int iMTBlockSize = 10; // every 10 users a new thread is created + const int iNumBlocks = ( iNumClients - 1 ) / iMTBlockSize + 1; - // update conversion buffer size (nothing will happen if the size stays the same) - if ( vecUseDoubleSysFraSizeConvBuf[i] ) + for ( int iBlockCnt = 0; iBlockCnt < iNumBlocks; iBlockCnt++ ) { - DoubleFrameSizeConvBufIn[iCurChanID].SetBufferSize ( DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[i] ); - DoubleFrameSizeConvBufOut[iCurChanID].SetBufferSize ( DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[i] ); + // The work for OPUS decoding is distributed over all available processor cores. + // By using the future synchronizer we make sure that all + // threads are done when we leave the timer callback function. + const int iStartChanCnt = iBlockCnt * iMTBlockSize; + const int iStopChanCnt = std::min ( ( iBlockCnt + 1 ) * iMTBlockSize - 1, iNumClients - 1 ); + + FutureSynchronizer.addFuture ( QtConcurrent::run ( this, + &CServer::DecodeReceiveDataBlocks, + iStartChanCnt, + iStopChanCnt, + iNumClients ) ); } - // select the opus decoder and raw audio frame length - if ( vecAudioComprType[i] == CT_OPUS ) - { - iClientFrameSizeSamples = DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES; + // make sure all concurrent run threads have finished when we leave this function + FutureSynchronizer.waitForFinished(); + FutureSynchronizer.clearFutures(); + } - if ( vecNumAudioChannels[i] == 1 ) - { - CurOpusDecoder = OpusDecoderMono[iCurChanID]; - } - else - { - CurOpusDecoder = OpusDecoderStereo[iCurChanID]; - } - } - else if ( vecAudioComprType[i] == CT_OPUS64 ) - { - iClientFrameSizeSamples = SYSTEM_FRAME_SIZE_SAMPLES; + // a channel is now disconnected, take action on it + if ( bChannelIsNowDisconnected ) + { + // update channel list for all currently connected clients + CreateAndSendChanListForAllConChannels(); + } + } + Mutex.unlock(); // release mutex - if ( vecNumAudioChannels[i] == 1 ) - { - CurOpusDecoder = Opus64DecoderMono[iCurChanID]; - } - else - { - CurOpusDecoder = Opus64DecoderStereo[iCurChanID]; - } + + // Process data ------------------------------------------------------------ + // Check if at least one client is connected. If not, stop server until + // one client is connected. + if ( iNumClients > 0 ) + { + // calculate levels for all connected clients + const bool bSendChannelLevels = CreateLevelsForAllConChannels ( iNumClients, + vecNumAudioChannels, + vecvecsData, + vecChannelLevels ); + + for ( int iChanCnt = 0; iChanCnt < iNumClients; iChanCnt++ ) + { + // get actual ID of current channel + const int iCurChanID = vecChanIDsCurConChan[iChanCnt]; + + // update socket buffer size + vecChannels[iCurChanID].UpdateSocketBufferSize(); + + // send channel levels if they are ready + if ( bSendChannelLevels ) + { + ConnLessProtocol.CreateCLChannelLevelListMes ( vecChannels[iCurChanID].GetAddress(), + vecChannelLevels, + iNumClients ); } - else + + // export the audio data for recording purpose + if ( JamController.GetRecordingEnabled() ) { - CurOpusDecoder = nullptr; + emit AudioFrame ( iCurChanID, + vecChannels[iCurChanID].GetName(), + vecChannels[iCurChanID].GetAddress(), + vecNumAudioChannels[iChanCnt], + vecvecsData[iChanCnt] ); } - // get gains of all connected channels - for ( j = 0; j < iNumClients; j++ ) + // processing without multithreading + if ( !bUseMultithreading ) { - // The second index of "vecvecdGains" does not represent - // the channel ID! Therefore we have to use - // "vecChanIDsCurConChan" to query the IDs of the currently - // connected channels - vecvecdGains[i][j] = vecChannels[iCurChanID].GetGain ( vecChanIDsCurConChan[j] ); + // generate a separate mix for each channel, OPUS encode the + // audio data and transmit the network packet + MixEncodeTransmitData ( iChanCnt, iNumClients ); + } + } - // consider audio fade-in - vecvecdGains[i][j] *= vecChannels[vecChanIDsCurConChan[j]].GetFadeInGain(); + // processing with multithreading + if ( bUseMultithreading ) + { + // introduced by kraney (#653): each thread must complete within the 1 or 2ms time budget for the timer +// TODO determine at startup by running a small benchmark + const int iMaximumMixOpsInTimeBudget = 500; // approximate limit as observed on GCP e2-standard instance + const int iMTBlockSize = iMaximumMixOpsInTimeBudget / iNumClients; // number of ops = block size * total number of clients + const int iNumBlocks = ( iNumClients - 1 ) / iMTBlockSize + 1; - // panning - vecvecdPannings[i][j] = vecChannels[iCurChanID].GetPan ( vecChanIDsCurConChan[j] ); + for ( int iBlockCnt = 0; iBlockCnt < iNumBlocks; iBlockCnt++ ) + { + // Generate a separate mix for each channel, OPUS encode the + // audio data and transmit the network packet. The work is + // distributed over all available processor cores. + // By using the future synchronizer we make sure that all + // threads are done when we leave the timer callback function. + const int iStartChanCnt = iBlockCnt * iMTBlockSize; + const int iStopChanCnt = std::min ( ( iBlockCnt + 1 ) * iMTBlockSize - 1, iNumClients - 1 ); + + FutureSynchronizer.addFuture ( QtConcurrent::run ( this, + &CServer::MixEncodeTransmitDataBlocks, + iStartChanCnt, + iStopChanCnt, + iNumClients ) ); } - // If the server frame size is smaller than the received OPUS frame size, we need a conversion - // buffer which stores the large buffer. - // Note that we have a shortcut here. If the conversion buffer is not needed, the boolean flag - // is false and the Get() function is not called at all. Therefore if the buffer is not needed - // we do not spend any time in the function but go directly inside the if condition. - if ( ( vecUseDoubleSysFraSizeConvBuf[i] == 0 ) || - !DoubleFrameSizeConvBufIn[iCurChanID].Get ( vecvecsData[i], SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[i] ) ) - { - // get current number of OPUS coded bytes - const int iCeltNumCodedBytes = vecChannels[iCurChanID].GetNetwFrameSize(); + // make sure all concurrent run threads have finished when we leave this function + FutureSynchronizer.waitForFinished(); + FutureSynchronizer.clearFutures(); + } + } + else + { + // Disable server if no clients are connected. In this case the server + // does not consume any significant CPU when no client is connected. + Stop(); + } +} - for ( int iB = 0; iB < vecNumFrameSizeConvBlocks[i]; iB++ ) - { - // get data - const EGetDataStat eGetStat = vecChannels[iCurChanID].GetData ( vecbyCodedData, iCeltNumCodedBytes ); +void CServer::DecodeReceiveDataBlocks ( const int iStartChanCnt, + const int iStopChanCnt, + const int iNumClients ) +{ + // loop over all channels in the current block, needed for multithreading support + for ( int iChanCnt = iStartChanCnt; iChanCnt <= iStopChanCnt; iChanCnt++ ) + { + DecodeReceiveData ( iChanCnt, iNumClients ); + } +} - // if channel was just disconnected, set flag that connected - // client list is sent to all other clients - // and emit the client disconnected signal - if ( eGetStat == GS_CHAN_NOW_DISCONNECTED ) - { - if ( bEnableRecording ) - { - emit ClientDisconnected ( iCurChanID ); // TODO do this outside the mutex lock? - } +void CServer::MixEncodeTransmitDataBlocks ( const int iStartChanCnt, + const int iStopChanCnt, + const int iNumClients ) +{ + // loop over all channels in the current block, needed for multithreading support + for ( int iChanCnt = iStartChanCnt; iChanCnt <= iStopChanCnt; iChanCnt++ ) + { + MixEncodeTransmitData ( iChanCnt, iNumClients ); + } +} - bChannelIsNowDisconnected = true; - } +void CServer::DecodeReceiveData ( const int iChanCnt, + const int iNumClients ) +{ + int iUnused; + int iClientFrameSizeSamples = 0; // initialize to avoid a compiler warning + OpusCustomDecoder* CurOpusDecoder; + unsigned char* pCurCodedData; - // get pointer to coded data - if ( eGetStat == GS_BUFFER_OK ) - { - pCurCodedData = &vecbyCodedData[0]; - } - else - { - // for lost packets use null pointer as coded input data - pCurCodedData = nullptr; - } + // get actual ID of current channel + const int iCurChanID = vecChanIDsCurConChan[iChanCnt]; - // OPUS decode received data stream - if ( CurOpusDecoder != nullptr ) - { - iUnused = opus_custom_decode ( CurOpusDecoder, - pCurCodedData, - iCeltNumCodedBytes, - &vecvecsData[i][iB * SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[i]], - iClientFrameSizeSamples ); - } - } + // get and store number of audio channels and compression type + vecNumAudioChannels[iChanCnt] = vecChannels[iCurChanID].GetNumAudioChannels(); + vecAudioComprType[iChanCnt] = vecChannels[iCurChanID].GetAudioCompressionType(); - // a new large frame is ready, if the conversion buffer is required, put it in the buffer - // and read out the small frame size immediately for further processing - if ( vecUseDoubleSysFraSizeConvBuf[i] != 0 ) - { - DoubleFrameSizeConvBufIn[iCurChanID].PutAll ( vecvecsData[i] ); - DoubleFrameSizeConvBufIn[iCurChanID].Get ( vecvecsData[i], SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[i] ); - } - } - } + // get info about required frame size conversion properties + vecUseDoubleSysFraSizeConvBuf[iChanCnt] = ( !bUseDoubleSystemFrameSize && ( vecAudioComprType[iChanCnt] == CT_OPUS ) ); - // a channel is now disconnected, take action on it - if ( bChannelIsNowDisconnected ) - { - // update channel list for all currently connected clients - CreateAndSendChanListForAllConChannels(); - } + if ( bUseDoubleSystemFrameSize && ( vecAudioComprType[iChanCnt] == CT_OPUS64 ) ) + { + vecNumFrameSizeConvBlocks[iChanCnt] = 2; + } + else + { + vecNumFrameSizeConvBlocks[iChanCnt] = 1; } - Mutex.unlock(); // release mutex + // update conversion buffer size (nothing will happen if the size stays the same) + if ( vecUseDoubleSysFraSizeConvBuf[iChanCnt] ) + { + DoubleFrameSizeConvBufIn[iCurChanID].SetBufferSize ( DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt] ); + DoubleFrameSizeConvBufOut[iCurChanID].SetBufferSize ( DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt] ); + } - // Process data ------------------------------------------------------------ - // Check if at least one client is connected. If not, stop server until - // one client is connected. - if ( iNumClients > 0 ) + // select the opus decoder and raw audio frame length + if ( vecAudioComprType[iChanCnt] == CT_OPUS ) { - // low frequency updates - if ( iFrameCount > CHANNEL_LEVEL_UPDATE_INTERVAL ) - { - iFrameCount = 0; + iClientFrameSizeSamples = DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES; - // Calculate channel levels if any client has requested them - for ( int i = 0; i < iNumClients; i++ ) - { - if ( vecChannels[vecChanIDsCurConChan[i]].ChannelLevelsRequired() ) - { - bSendChannelLevels = true; + if ( vecNumAudioChannels[iChanCnt] == 1 ) + { + CurOpusDecoder = OpusDecoderMono[iCurChanID]; + } + else + { + CurOpusDecoder = OpusDecoderStereo[iCurChanID]; + } + } + else if ( vecAudioComprType[iChanCnt] == CT_OPUS64 ) + { + iClientFrameSizeSamples = SYSTEM_FRAME_SIZE_SAMPLES; - CreateLevelsForAllConChannels ( iNumClients, - vecNumAudioChannels, - vecvecsData, - vecChannelLevels ); - break; - } - } + if ( vecNumAudioChannels[iChanCnt] == 1 ) + { + CurOpusDecoder = Opus64DecoderMono[iCurChanID]; } - iFrameCount++; - if ( bUseDoubleSystemFrameSize ) + else { - // additional increment needed for double frame size to get to the same time interval - iFrameCount++; + CurOpusDecoder = Opus64DecoderStereo[iCurChanID]; } + } + else + { + CurOpusDecoder = nullptr; + } - for ( int i = 0; i < iNumClients; i++ ) + // get gains of all connected channels + for ( int j = 0; j < iNumClients; j++ ) + { + // The second index of "vecvecdGains" does not represent + // the channel ID! Therefore we have to use + // "vecChanIDsCurConChan" to query the IDs of the currently + // connected channels + vecvecfGains[iChanCnt][j] = vecChannels[iCurChanID].GetGain ( vecChanIDsCurConChan[j] ); + + // consider audio fade-in + vecvecfGains[iChanCnt][j] *= vecChannels[vecChanIDsCurConChan[j]].GetFadeInGain(); + + // use the fade in of the current channel for all other connected clients + // as well to avoid the client volumes are at 100% when joining a server (#628) + if ( j != iChanCnt ) { - // get actual ID of current channel - const int iCurChanID = vecChanIDsCurConChan[i]; + vecvecfGains[iChanCnt][j] *= vecChannels[iCurChanID].GetFadeInGain(); + } - // get number of audio channels of current channel - const int iCurNumAudChan = vecNumAudioChannels[i]; + // panning + vecvecfPannings[iChanCnt][j] = vecChannels[iCurChanID].GetPan ( vecChanIDsCurConChan[j] ); + } - // export the audio data for recording purpose - if ( bEnableRecording ) - { - emit AudioFrame ( iCurChanID, - vecChannels[iCurChanID].GetName(), - vecChannels[iCurChanID].GetAddress(), - iCurNumAudChan, - vecvecsData[i] ); - } + // If the server frame size is smaller than the received OPUS frame size, we need a conversion + // buffer which stores the large buffer. + // Note that we have a shortcut here. If the conversion buffer is not needed, the boolean flag + // is false and the Get() function is not called at all. Therefore if the buffer is not needed + // we do not spend any time in the function but go directly inside the if condition. + if ( ( vecUseDoubleSysFraSizeConvBuf[iChanCnt] == 0 ) || + !DoubleFrameSizeConvBufIn[iCurChanID].Get ( vecvecsData[iChanCnt], SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt] ) ) + { + // get current number of OPUS coded bytes + const int iCeltNumCodedBytes = vecChannels[iCurChanID].GetCeltNumCodedBytes(); - // generate a sparate mix for each channel - // actual processing of audio data -> mix - ProcessData ( vecvecsData, - vecvecdGains[i], - vecvecdPannings[i], - vecNumAudioChannels, - vecsSendData, - iCurNumAudChan, - iNumClients ); - - // get current number of CELT coded bytes - const int iCeltNumCodedBytes = vecChannels[iCurChanID].GetNetwFrameSize(); - - // select the opus encoder and raw audio frame length - if ( vecAudioComprType[i] == CT_OPUS ) - { - iClientFrameSizeSamples = DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES; + for ( int iB = 0; iB < vecNumFrameSizeConvBlocks[iChanCnt]; iB++ ) + { + // get data + const EGetDataStat eGetStat = vecChannels[iCurChanID].GetData ( vecvecbyCodedData[iChanCnt], iCeltNumCodedBytes ); - if ( vecNumAudioChannels[i] == 1 ) - { - CurOpusEncoder = OpusEncoderMono[iCurChanID]; - } - else + // if channel was just disconnected, set flag that connected + // client list is sent to all other clients + // and emit the client disconnected signal + if ( eGetStat == GS_CHAN_NOW_DISCONNECTED ) + { + if ( JamController.GetRecordingEnabled() ) { - CurOpusEncoder = OpusEncoderStereo[iCurChanID]; + emit ClientDisconnected ( iCurChanID ); // TODO do this outside the mutex lock? } + + // note that no mutex is needed for this shared resource since it is not a + // read-modify-write operation but an atomic write and also each thread can + // only set it to true and never to false + bChannelIsNowDisconnected = true; } - else if ( vecAudioComprType[i] == CT_OPUS64 ) - { - iClientFrameSizeSamples = SYSTEM_FRAME_SIZE_SAMPLES; - if ( vecNumAudioChannels[i] == 1 ) - { - CurOpusEncoder = Opus64EncoderMono[iCurChanID]; - } - else - { - CurOpusEncoder = Opus64EncoderStereo[iCurChanID]; - } + // get pointer to coded data + if ( eGetStat == GS_BUFFER_OK ) + { + pCurCodedData = &vecvecbyCodedData[iChanCnt][0]; } else { - CurOpusEncoder = nullptr; + // for lost packets use null pointer as coded input data + pCurCodedData = nullptr; } - // If the server frame size is smaller than the received OPUS frame size, we need a conversion - // buffer which stores the large buffer. - // Note that we have a shortcut here. If the conversion buffer is not needed, the boolean flag - // is false and the Get() function is not called at all. Therefore if the buffer is not needed - // we do not spend any time in the function but go directly inside the if condition. - if ( ( vecUseDoubleSysFraSizeConvBuf[i] == 0 ) || - DoubleFrameSizeConvBufOut[iCurChanID].Put ( vecsSendData, SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[i] ) ) + // OPUS decode received data stream + if ( CurOpusDecoder != nullptr ) { - if ( vecUseDoubleSysFraSizeConvBuf[i] != 0 ) - { - // get the large frame from the conversion buffer - DoubleFrameSizeConvBufOut[iCurChanID].GetAll ( vecsSendData, DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[i] ); - } - - for ( int iB = 0; iB < vecNumFrameSizeConvBlocks[i]; iB++ ) - { - // OPUS encoding - if ( CurOpusEncoder != nullptr ) - { -// TODO find a better place than this: the setting does not change all the time -// so for speed optimization it would be better to set it only if the network -// frame size is changed -opus_custom_encoder_ctl ( CurOpusEncoder, - OPUS_SET_BITRATE ( CalcBitRateBitsPerSecFromCodedBytes ( iCeltNumCodedBytes, iClientFrameSizeSamples ) ) ); - - iUnused = opus_custom_encode ( CurOpusEncoder, - &vecsSendData[iB * SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[i]], - iClientFrameSizeSamples, - &vecbyCodedData[0], - iCeltNumCodedBytes ); - } - - // send separate mix to current clients - vecChannels[iCurChanID].PrepAndSendPacket ( &Socket, - vecbyCodedData, - iCeltNumCodedBytes ); - } - - // update socket buffer size - vecChannels[iCurChanID].UpdateSocketBufferSize(); - - // send channel levels - if ( bSendChannelLevels && vecChannels[iCurChanID].ChannelLevelsRequired() ) - { - ConnLessProtocol.CreateCLChannelLevelListMes ( vecChannels[iCurChanID].GetAddress(), vecChannelLevels, iNumClients ); - } + iUnused = opus_custom_decode ( CurOpusDecoder, + pCurCodedData, + iCeltNumCodedBytes, + &vecvecsData[iChanCnt][iB * SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt]], + iClientFrameSizeSamples ); } } - } - else - { - // Disable server if no clients are connected. In this case the server - // does not consume any significant CPU when no client is connected. - Stop(); + + // a new large frame is ready, if the conversion buffer is required, put it in the buffer + // and read out the small frame size immediately for further processing + if ( vecUseDoubleSysFraSizeConvBuf[iChanCnt] != 0 ) + { + DoubleFrameSizeConvBufIn[iCurChanID].PutAll ( vecvecsData[iChanCnt] ); + DoubleFrameSizeConvBufIn[iCurChanID].Get ( vecvecsData[iChanCnt], SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt] ); + } } Q_UNUSED ( iUnused ) } -/// @brief Mix all audio data from all clients together. -void CServer::ProcessData ( const CVector >& vecvecsData, - const CVector& vecdGains, - const CVector& vecdPannings, - const CVector& vecNumAudioChannels, - CVector& vecsOutData, - const int iCurNumAudChan, - const int iNumClients ) +/// @brief Mix all audio data from all clients together, encode and transmit +void CServer::MixEncodeTransmitData ( const int iChanCnt, + const int iNumClients ) { - int i, j, k; + int i, j, k, iUnused; + CVector& vecfIntermProcBuf = vecvecfIntermediateProcBuf[iChanCnt]; // use reference for faster access + CVector& vecsSendData = vecvecsSendData[iChanCnt]; // use reference for faster access + + // get actual ID of current channel + const int iCurChanID = vecChanIDsCurConChan[iChanCnt]; - // init return vector with zeros since we mix all channels on that vector - vecsOutData.Reset ( 0 ); + // init intermediate processing vector with zeros since we mix all channels on that vector + vecfIntermProcBuf.Reset ( 0 ); // distinguish between stereo and mono mode - if ( iCurNumAudChan == 1 ) + if ( vecNumAudioChannels[iChanCnt] == 1 ) { // Mono target channel ------------------------------------------------- for ( j = 0; j < iNumClients; j++ ) { // get a reference to the audio data and gain of the current client const CVector& vecsData = vecvecsData[j]; - const double dGain = vecdGains[j]; + const float fGain = vecvecfGains[iChanCnt][j]; // if channel gain is 1, avoid multiplication for speed optimization - if ( dGain == static_cast ( 1.0 ) ) + if ( fGain == 1.0f ) { if ( vecNumAudioChannels[j] == 1 ) { // mono for ( i = 0; i < iServerFrameSizeSamples; i++ ) { - vecsOutData[i] = Double2Short ( - static_cast ( vecsOutData[i] ) + vecsData[i] ); + vecfIntermProcBuf[i] += vecsData[i]; } } else @@ -1124,9 +1154,8 @@ void CServer::ProcessData ( const CVector >& vecvecsData, // stereo: apply stereo-to-mono attenuation for ( i = 0, k = 0; i < iServerFrameSizeSamples; i++, k += 2 ) { - vecsOutData[i] = - Double2Short ( vecsOutData[i] + - ( static_cast ( vecsData[k] ) + vecsData[k + 1] ) / 2 ); + vecfIntermProcBuf[i] += + ( static_cast ( vecsData[k] ) + vecsData[k + 1] ) / 2; } } } @@ -1137,8 +1166,7 @@ void CServer::ProcessData ( const CVector >& vecvecsData, // mono for ( i = 0; i < iServerFrameSizeSamples; i++ ) { - vecsOutData[i] = Double2Short ( - vecsOutData[i] + vecsData[i] * dGain ); + vecfIntermProcBuf[i] += vecsData[i] * fGain; } } else @@ -1146,13 +1174,18 @@ void CServer::ProcessData ( const CVector >& vecvecsData, // stereo: apply stereo-to-mono attenuation for ( i = 0, k = 0; i < iServerFrameSizeSamples; i++, k += 2 ) { - vecsOutData[i] = - Double2Short ( vecsOutData[i] + dGain * - ( static_cast ( vecsData[k] ) + vecsData[k + 1] ) / 2 ); + vecfIntermProcBuf[i] += fGain * + ( static_cast ( vecsData[k] ) + vecsData[k + 1] ) / 2; } } } } + + // convert from double to short with clipping + for ( i = 0; i < iServerFrameSizeSamples; i++ ) + { + vecsSendData[i] = Float2Short ( vecfIntermProcBuf[i] ); + } } else { @@ -1161,29 +1194,25 @@ void CServer::ProcessData ( const CVector >& vecvecsData, { // get a reference to the audio data and gain/pan of the current client const CVector& vecsData = vecvecsData[j]; - const double dGain = vecdGains[j]; - const double dPan = vecdPannings[j]; + const float fGain = vecvecfGains[iChanCnt][j]; + const float fPan = vecvecfPannings[iChanCnt][j]; // calculate combined gain/pan for each stereo channel where we define // the panning that center equals full gain for both channels - const double dGainL = std::min ( 0.5, 1 - dPan ) * 2 * dGain; - const double dGainR = std::min ( 0.5, dPan ) * 2 * dGain; + const float fGainL = MathUtils::GetLeftPan ( fPan, false ) * fGain; + const float fGainR = MathUtils::GetRightPan ( fPan, false ) * fGain; // if channel gain is 1, avoid multiplication for speed optimization - if ( ( dGainL == static_cast ( 1.0 ) ) && ( dGainR == static_cast ( 1.0 ) ) ) + if ( ( fGainL == 1.0f ) && ( fGainR == 1.0f ) ) { if ( vecNumAudioChannels[j] == 1 ) { // mono: copy same mono data in both out stereo audio channels for ( i = 0, k = 0; i < iServerFrameSizeSamples; i++, k += 2 ) { - // left channel - vecsOutData[k] = Double2Short ( - static_cast ( vecsOutData[k] ) + vecsData[i] ); - - // right channel - vecsOutData[k + 1] = Double2Short ( - static_cast ( vecsOutData[k + 1] ) + vecsData[i] ); + // left/right channel + vecfIntermProcBuf[k] += vecsData[i]; + vecfIntermProcBuf[k + 1] += vecsData[i]; } } else @@ -1191,8 +1220,7 @@ void CServer::ProcessData ( const CVector >& vecvecsData, // stereo for ( i = 0; i < ( 2 * iServerFrameSizeSamples ); i++ ) { - vecsOutData[i] = Double2Short ( - static_cast ( vecsOutData[i] ) + vecsData[i] ); + vecfIntermProcBuf[i] += vecsData[i]; } } } @@ -1204,8 +1232,8 @@ void CServer::ProcessData ( const CVector >& vecvecsData, for ( i = 0, k = 0; i < iServerFrameSizeSamples; i++, k += 2 ) { // left/right channel - vecsOutData[k] = Double2Short ( vecsOutData[k] + vecsData[i] * dGainL ); - vecsOutData[k + 1] = Double2Short ( vecsOutData[k + 1] + vecsData[i] * dGainR ); + vecfIntermProcBuf[k] += vecsData[i] * fGainL; + vecfIntermProcBuf[k + 1] += vecsData[i] * fGainR; } } else @@ -1214,13 +1242,92 @@ void CServer::ProcessData ( const CVector >& vecvecsData, for ( i = 0; i < ( 2 * iServerFrameSizeSamples ); i += 2 ) { // left/right channel - vecsOutData[i] = Double2Short ( vecsOutData[i] + vecsData[i] * dGainL ); - vecsOutData[i + 1] = Double2Short ( vecsOutData[i + 1] + vecsData[i + 1] * dGainR ); + vecfIntermProcBuf[i] += vecsData[i] * fGainL; + vecfIntermProcBuf[i + 1] += vecsData[i + 1] * fGainR; } } } } + + // convert from double to short with clipping + for ( i = 0; i < ( 2 * iServerFrameSizeSamples ); i++ ) + { + vecsSendData[i] = Float2Short ( vecfIntermProcBuf[i] ); + } } + + int iClientFrameSizeSamples = 0; // initialize to avoid a compiler warning + OpusCustomEncoder* pCurOpusEncoder = nullptr; + + // get current number of CELT coded bytes + const int iCeltNumCodedBytes = vecChannels[iCurChanID].GetCeltNumCodedBytes(); + + // select the opus encoder and raw audio frame length + if ( vecAudioComprType[iChanCnt] == CT_OPUS ) + { + iClientFrameSizeSamples = DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES; + + if ( vecNumAudioChannels[iChanCnt] == 1 ) + { + pCurOpusEncoder = OpusEncoderMono[iCurChanID]; + } + else + { + pCurOpusEncoder = OpusEncoderStereo[iCurChanID]; + } + } + else if ( vecAudioComprType[iChanCnt] == CT_OPUS64 ) + { + iClientFrameSizeSamples = SYSTEM_FRAME_SIZE_SAMPLES; + + if ( vecNumAudioChannels[iChanCnt] == 1 ) + { + pCurOpusEncoder = Opus64EncoderMono[iCurChanID]; + } + else + { + pCurOpusEncoder = Opus64EncoderStereo[iCurChanID]; + } + } + + // If the server frame size is smaller than the received OPUS frame size, we need a conversion + // buffer which stores the large buffer. + // Note that we have a shortcut here. If the conversion buffer is not needed, the boolean flag + // is false and the Get() function is not called at all. Therefore if the buffer is not needed + // we do not spend any time in the function but go directly inside the if condition. + if ( ( vecUseDoubleSysFraSizeConvBuf[iChanCnt] == 0 ) || + DoubleFrameSizeConvBufOut[iCurChanID].Put ( vecsSendData, SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt] ) ) + { + if ( vecUseDoubleSysFraSizeConvBuf[iChanCnt] != 0 ) + { + // get the large frame from the conversion buffer + DoubleFrameSizeConvBufOut[iCurChanID].GetAll ( vecsSendData, DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt] ); + } + + for ( int iB = 0; iB < vecNumFrameSizeConvBlocks[iChanCnt]; iB++ ) + { + // OPUS encoding + if ( pCurOpusEncoder != nullptr ) + { +// TODO find a better place than this: the setting does not change all the time so for speed +// optimization it would be better to set it only if the network frame size is changed +opus_custom_encoder_ctl ( pCurOpusEncoder, OPUS_SET_BITRATE ( CalcBitRateBitsPerSecFromCodedBytes ( iCeltNumCodedBytes, iClientFrameSizeSamples ) ) ); + + iUnused = opus_custom_encode ( pCurOpusEncoder, + &vecsSendData[iB * SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt]], + iClientFrameSizeSamples, + &vecvecbyCodedData[iChanCnt][0], + iCeltNumCodedBytes ); + } + + // send separate mix to current clients + vecChannels[iCurChanID].PrepAndSendPacket ( &Socket, + vecvecbyCodedData[iChanCnt], + iCeltNumCodedBytes ); + } + } + + Q_UNUSED ( iUnused ) } CVector CServer::CreateChannelList() @@ -1235,7 +1342,7 @@ CVector CServer::CreateChannelList() // append channel ID, IP address and channel name to storing vectors vecChanInfo.Add ( CChannelInfo ( i, // ID - vecChannels[i].GetAddress().InetAddr.toIPv4Address(), // IP address + QHostAddress ( QHostAddress::Null ).toIPv4Address(), // use invalid IP address (for privacy reason, #316) vecChannels[i].GetChanInfo() ) ); } } @@ -1278,23 +1385,17 @@ void CServer::CreateAndSendChatTextForAllConChannels ( const int iCurChanID const QString& strChatText ) { // Create message which is sent to all connected clients ------------------- - // get client name, if name is empty, use IP address instead + // get client name QString ChanName = vecChannels[iCurChanID].GetName(); - if ( ChanName.isEmpty() ) - { - // convert IP address to text and show it - ChanName = vecChannels[iCurChanID].GetAddress(). - toString ( CHostAddress::SM_IP_NO_LAST_BYTE ); - } - // add time and name of the client at the beginning of the message text and // use different colors QString sCurColor = vstrChatColors[iCurChanID % vstrChatColors.Size()]; const QString strActualMessageText = "(" + - QTime::currentTime().toString ( "hh:mm:ss AP" ) + ") " + ChanName + + QTime::currentTime().toString ( "hh:mm:ss AP" ) + ") " + + ChanName.toHtmlEscaped() + " " + strChatText; @@ -1309,6 +1410,22 @@ void CServer::CreateAndSendChatTextForAllConChannels ( const int iCurChanID } } +void CServer::CreateAndSendRecorderStateForAllConChannels() +{ + // get recorder state + ERecorderState eRecorderState = JamController.GetRecorderState(); + + // now send recorder state to all connected clients + for ( int i = 0; i < iMaxNumChannels; i++ ) + { + if ( vecChannels[i].IsConnected() ) + { + // send message + vecChannels[i].CreateRecorderStateMes ( eRecorderState ); + } + } +} + void CServer::CreateOtherMuteStateChanged ( const int iCurChanID, const int iOtherChanID, const bool bIsMuted ) @@ -1375,26 +1492,36 @@ int CServer::FindChannel ( const CHostAddress& CheckAddr ) return INVALID_CHANNEL_ID; } +void CServer::OnProtcolCLMessageReceived ( int iRecID, + CVector vecbyMesBodyData, + CHostAddress RecHostAddr ) +{ + QMutexLocker locker ( &Mutex ); + + // connection less messages are always processed + ConnLessProtocol.ParseConnectionLessMessageBody ( vecbyMesBodyData, + iRecID, + RecHostAddr ); +} + void CServer::OnProtcolMessageReceived ( int iRecCounter, int iRecID, CVector vecbyMesBodyData, CHostAddress RecHostAddr ) { - Mutex.lock(); - { - // find the channel with the received address - const int iCurChanID = FindChannel ( RecHostAddr ); + QMutexLocker locker ( &Mutex ); - // if the channel exists, apply the protocol message to the channel - if ( iCurChanID != INVALID_CHANNEL_ID ) - { - vecChannels[iCurChanID].PutProtcolData ( iRecCounter, - iRecID, - vecbyMesBodyData, - RecHostAddr ); - } + // find the channel with the received address + const int iCurChanID = FindChannel ( RecHostAddr ); + + // if the channel exists, apply the protocol message to the channel + if ( iCurChanID != INVALID_CHANNEL_ID ) + { + vecChannels[iCurChanID].PutProtcolData ( iRecCounter, + iRecID, + vecbyMesBodyData, + RecHostAddr ); } - Mutex.unlock(); } bool CServer::PutAudioData ( const CVector& vecbyRecBuf, @@ -1402,62 +1529,62 @@ bool CServer::PutAudioData ( const CVector& vecbyRecBuf, const CHostAddress& HostAdr, int& iCurChanID ) { + QMutexLocker locker ( &Mutex ); + bool bNewConnection = false; // init return value - bool bChanOK = true; // init with ok, might be overwritten + bool bChanOK = true; // init with ok, might be overwritten - Mutex.lock(); + // Get channel ID ------------------------------------------------------ + // check address + iCurChanID = FindChannel ( HostAdr ); + + if ( iCurChanID == INVALID_CHANNEL_ID ) { - // Get channel ID ------------------------------------------------------ - // check address - iCurChanID = FindChannel ( HostAdr ); + // a new client is calling, look for free channel + iCurChanID = GetFreeChan(); - if ( iCurChanID == INVALID_CHANNEL_ID ) + if ( iCurChanID != INVALID_CHANNEL_ID ) { - // a new client is calling, look for free channel - iCurChanID = GetFreeChan(); + // initialize current channel by storing the calling host + // address + vecChannels[iCurChanID].SetAddress ( HostAdr ); - if ( iCurChanID != INVALID_CHANNEL_ID ) - { - // initialize current channel by storing the calling host - // address - vecChannels[iCurChanID].SetAddress ( HostAdr ); - - // reset channel info - vecChannels[iCurChanID].ResetInfo(); - - // reset the channel gains of current channel, at the same - // time reset gains of this channel ID for all other channels - for ( int i = 0; i < iMaxNumChannels; i++ ) - { - vecChannels[iCurChanID].SetGain ( i, 1.0 ); + // reset channel info + vecChannels[iCurChanID].ResetInfo(); - // other channels (we do not distinguish the case if - // i == iCurChanID for simplicity) - vecChannels[i].SetGain ( iCurChanID, 1.0 ); - } - } - else + // reset the channel gains/pans of current channel, at the same + // time reset gains/pans of this channel ID for all other channels + for ( int i = 0; i < iMaxNumChannels; i++ ) { - // no free channel available - bChanOK = false; + vecChannels[iCurChanID].SetGain ( i, 1.0 ); + vecChannels[iCurChanID].SetPan ( i, 0.5 ); + + // other channels (we do not distinguish the case if + // i == iCurChanID for simplicity) + vecChannels[i].SetGain ( iCurChanID, 1.0 ); + vecChannels[i].SetPan ( iCurChanID, 0.5 ); } } + else + { + // no free channel available + bChanOK = false; + } + } - // Put received audio data in jitter buffer ---------------------------- - if ( bChanOK ) + // Put received audio data in jitter buffer ---------------------------- + if ( bChanOK ) + { + // put packet in socket buffer + if ( vecChannels[iCurChanID].PutAudioData ( vecbyRecBuf, + iNumBytesRead, + HostAdr ) == PS_NEW_CONNECTION ) { - // put packet in socket buffer - if ( vecChannels[iCurChanID].PutAudioData ( vecbyRecBuf, - iNumBytesRead, - HostAdr ) == PS_NEW_CONNECTION ) - { - // in case we have a new connection return this information - bNewConnection = true; - } + // in case we have a new connection return this information + bNewConnection = true; } } - Mutex.unlock(); // return the state if a new connection was happening return bNewConnection; @@ -1490,18 +1617,25 @@ void CServer::GetConCliParam ( CVector& vecHostAddresses, } } -void CServer::StartStatusHTMLFileWriting ( const QString& strNewFileName, - const QString& strNewServerNameWithPort ) +void CServer::SetEnableRecording ( bool bNewEnableRecording ) { - // set important parameters - strServerHTMLFileListName = strNewFileName; - strServerNameWithPort = strNewServerNameWithPort; + JamController.SetEnableRecording ( bNewEnableRecording, IsRunning() ); + + // not dependent upon JamController state + bDisableRecording = !bNewEnableRecording; - // set flag - bWriteStatusHTMLFile = true; + // the recording state may have changed, send recording state message + CreateAndSendRecorderStateForAllConChannels(); +} + +void CServer::SetWelcomeMessage ( const QString& strNWelcMess ) +{ + // we need a mutex to secure access + QMutexLocker locker ( &MutexWelcomeMessage ); + strWelcomeMessage = strNWelcMess; - // write initial file - WriteHTMLChannelList(); + // restrict welcome message to maximum allowed length + strWelcomeMessage = strWelcomeMessage.left ( MAX_LEN_CHAT_TEXT ); } void CServer::WriteHTMLChannelList() @@ -1509,45 +1643,32 @@ void CServer::WriteHTMLChannelList() // prepare file and stream QFile serverFileListFile ( strServerHTMLFileListName ); - if ( !serverFileListFile.open ( QIODevice::WriteOnly | QIODevice::Text ) ) + if ( serverFileListFile.open ( QIODevice::WriteOnly | QIODevice::Text ) ) { - return; - } - - QTextStream streamFileOut ( &serverFileListFile ); - streamFileOut << strServerNameWithPort << endl << "
    " << endl; + QTextStream streamFileOut ( &serverFileListFile ); - // depending on number of connected clients write list - if ( GetNumberOfConnectedClients() == 0 ) - { - // no clients are connected -> empty server - streamFileOut << " No client connected" << endl; - } - else - { - // write entry for each connected client - for ( int i = 0; i < iMaxNumChannels; i++ ) + // depending on number of connected clients write list + if ( GetNumberOfConnectedClients() == 0 ) { - if ( vecChannels[i].IsConnected() ) - { - QString strCurChanName = vecChannels[i].GetName(); + // no clients are connected -> empty server + streamFileOut << " No client connected\n"; + } + else + { + streamFileOut << "
      \n"; - // if text is empty, show IP address instead - if ( strCurChanName.isEmpty() ) + // write entry for each connected client + for ( int i = 0; i < iMaxNumChannels; i++ ) + { + if ( vecChannels[i].IsConnected() ) { - // convert IP address to text and show it, remove last - // digits - strCurChanName = vecChannels[i].GetAddress(). - toString ( CHostAddress::SM_IP_NO_LAST_BYTE ); + streamFileOut << "
    • " << vecChannels[i].GetName().toHtmlEscaped() << "
    • \n"; } - - streamFileOut << "
    • " << strCurChanName << "
    • " << endl; } + + streamFileOut << "
    \n"; } } - - // finish list - streamFileOut << "
" << endl; } void CServer::customEvent ( QEvent* pEvent ) @@ -1569,58 +1690,40 @@ void CServer::customEvent ( QEvent* pEvent ) } /// @brief Compute frame peak level for each client -void CServer::CreateLevelsForAllConChannels ( const int iNumClients, +bool CServer::CreateLevelsForAllConChannels ( const int iNumClients, const CVector& vecNumAudioChannels, const CVector > vecvecsData, CVector& vecLevelsOut ) { - int i, j, k; - - // init return vector with zeros since we mix all channels on that vector - vecLevelsOut.Reset ( 0 ); + bool bLevelsWereUpdated = false; - for ( j = 0; j < iNumClients; j++ ) + // low frequency updates + if ( iFrameCount > CHANNEL_LEVEL_UPDATE_INTERVAL ) { - // get a reference to the audio data - const CVector& vecsData = vecvecsData[j]; - - double dCurLevel = 0.0; + iFrameCount = 0; + bLevelsWereUpdated = true; - if ( vecNumAudioChannels[j] == 1 ) + for ( int j = 0; j < iNumClients; j++ ) { - // mono - for ( i = 0; i < iServerFrameSizeSamples; i += 3 ) - { - dCurLevel = std::max ( dCurLevel, fabs ( static_cast ( vecsData[i] ) ) ); - } + // update and get signal level for meter in dB for each channel + const double dCurSigLevelForMeterdB = vecChannels[vecChanIDsCurConChan[j]]. + UpdateAndGetLevelForMeterdB ( vecvecsData[j], + iServerFrameSizeSamples, + vecNumAudioChannels[j] > 1 ); + + // map value to integer for transmission via the protocol (4 bit available) + vecLevelsOut[j] = static_cast ( std::ceil ( dCurSigLevelForMeterdB ) ); } - else - { - // stereo: apply stereo-to-mono attenuation - for ( i = 0, k = 0; i < iServerFrameSizeSamples; i += 3, k += 6 ) - { - double sMix = ( static_cast ( vecsData[k] ) + vecsData[k + 1] ) / 2; - dCurLevel = std::max ( dCurLevel, fabs ( sMix ) ); - } - } - - // smoothing - const int iChId = vecChanIDsCurConChan[j]; - dCurLevel = std::max ( dCurLevel, vecChannels[iChId].GetPrevLevel() * 0.5 ); - vecChannels[iChId].SetPrevLevel ( dCurLevel ); - - // logarithmic measure - double dCurSigLevel = CStereoSignalLevelMeter::CalcLogResult ( dCurLevel ); + } - // map to signal level meter - dCurSigLevel -= LOW_BOUND_SIG_METER; - dCurSigLevel *= NUM_STEPS_LED_BAR / ( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ); + // increment the frame counter needed for low frequency update trigger + iFrameCount++; - if ( dCurSigLevel < 0 ) - { - dCurSigLevel = 0; - } - - vecLevelsOut[j] = static_cast ( ceil ( dCurSigLevel ) ); + if ( bUseDoubleSystemFrameSize ) + { + // additional increment needed for double frame size to get to the same time interval + iFrameCount++; } + + return bLevelsWereUpdated; } diff --git a/src/server.h b/src/server.h index 8c4013045b..119ef0e94f 100755 --- a/src/server.h +++ b/src/server.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include #include #ifdef USE_OPUS_SHARED_LIB # include "opus/opus_custom.h" @@ -42,9 +45,7 @@ #include "util.h" #include "serverlogging.h" #include "serverlist.h" -#include "multicolorledbar.h" -#include "recorder/jamrecorder.h" - +#include "recorder/jamcontroller.h" /* Definitions ****************************************************************/ // no valid channel number @@ -160,6 +161,7 @@ class CServerSlots : public CServerSlots template<> class CServerSlots<0> {}; + class CServer : public QObject, public CServerSlots @@ -168,21 +170,22 @@ class CServer : public: CServer ( const int iNewMaxNumChan, - const int iMaxDaysHistory, const QString& strLoggingFileName, const quint16 iPortNumber, const QString& strHTMLStatusFileName, - const QString& strHistoryFileName, - const QString& strServerNameForHTMLStatusFile, const QString& strCentralServer, const QString& strServerInfo, + const QString& strServerListFilter, const QString& strNewWelcomeMessage, const QString& strRecordingDirName, - const bool bNCentServPingServerInList, const bool bNDisconnectAllClientsOnQuit, const bool bNUseDoubleSystemFrameSize, + const bool bNUseMultithreading, + const bool bDisableRecording, const ELicenceType eNLicenceType ); + virtual ~CServer(); + void Start(); void Stop(); bool IsRunning() { return HighPrecisionTimer.isActive(); } @@ -197,7 +200,26 @@ class CServer : CVector& veciJitBufNumFrames, CVector& veciNetwFrameSizeFact ); - bool GetRecordingEnabled() { return bEnableRecording; } + void CreateCLServerListReqVerAndOSMes ( const CHostAddress& InetAddr ) + { ConnLessProtocol.CreateCLReqVersionAndOSMes ( InetAddr ); } + + + // Jam recorder ------------------------------------------------------------ + bool GetRecorderInitialised() { return JamController.GetRecorderInitialised(); } + QString GetRecorderErrMsg() { return JamController.GetRecorderErrMsg(); } + bool GetRecordingEnabled() { return JamController.GetRecordingEnabled(); } + bool GetDisableRecording() { return bDisableRecording; } + void RequestNewRecording() { JamController.RequestNewRecording(); } + + void SetEnableRecording ( bool bNewEnableRecording ); + + QString GetRecordingDir() { return JamController.GetRecordingDir(); } + + void SetRecordingDir( QString newRecordingDir ) + { JamController.SetRecordingDir ( newRecordingDir, iServerFrameSizeSamples, bDisableRecording ); } + + void CreateAndSendRecorderStateForAllConChannels(); + // Server list management -------------------------------------------------- void UpdateServerList() { ServerListManager.Update(); } @@ -237,6 +259,9 @@ class CServer : QLocale::Country GetServerCountry() { return ServerListManager.GetServerCountry(); } + void SetWelcomeMessage ( const QString& strNWelcMess ); + QString GetWelcomeMessage() { return strWelcomeMessage; } + ESvrRegStatus GetSvrRegStatus() { return ServerListManager.GetSvrRegStatus(); } @@ -244,16 +269,9 @@ class CServer : void SetAutoRunMinimized ( const bool NAuRuMin ) { bAutoRunMinimized = NAuRuMin; } bool GetAutoRunMinimized() { return bAutoRunMinimized; } - void SetLicenceType ( const ELicenceType NLiType ) { eLicenceType = NLiType; } - ELicenceType GetLicenceType() { return eLicenceType; } - protected: // access functions for actual channels - bool IsConnected ( const int iChanNum ) - { return vecChannels[iChanNum].IsConnected(); } - - void StartStatusHTMLFileWriting ( const QString& strNewFileName, - const QString& strNewServerNameWithPort ); + bool IsConnected ( const int iChanNum ) { return vecChannels[iChanNum].IsConnected(); } int GetFreeChan(); int FindChannel ( const CHostAddress& CheckAddr ); @@ -281,13 +299,19 @@ class CServer : void WriteHTMLChannelList(); - void ProcessData ( const CVector >& vecvecsData, - const CVector& vecdGains, - const CVector& vecdPannings, - const CVector& vecNumAudioChannels, - CVector& vecsOutData, - const int iCurNumAudChan, - const int iNumClients ); + void DecodeReceiveDataBlocks ( const int iStartChanCnt, + const int iStopChanCnt, + const int iNumClients ); + + void MixEncodeTransmitDataBlocks ( const int iStartChanCnt, + const int iStopChanCnt, + const int iNumClients ); + + void DecodeReceiveData ( const int iChanCnt, + const int iNumClients ); + + void MixEncodeTransmitData ( const int iChanCnt, + const int iNumClients ); virtual void customEvent ( QEvent* pEvent ); @@ -295,19 +319,23 @@ class CServer : bool bUseDoubleSystemFrameSize; int iServerFrameSizeSamples; - void CreateLevelsForAllConChannels ( const int iNumClients, + // variables needed for multithreading support + bool bUseMultithreading; + QFutureSynchronizer FutureSynchronizer; + + bool CreateLevelsForAllConChannels ( const int iNumClients, const CVector& vecNumAudioChannels, const CVector > vecvecsData, CVector& vecLevelsOut ); - void RequestNewRecording(); - // do not use the vector class since CChannel does not have appropriate // copy constructor/operator CChannel vecChannels[MAX_NUM_CHANNELS]; int iMaxNumChannels; CProtocol ConnLessProtocol; QMutex Mutex; + QMutex MutexWelcomeMessage; + bool bChannelIsNowDisconnected; // audio encoder/decoder OpusCustomMode* Opus64Mode[MAX_NUM_CHANNELS]; @@ -326,15 +354,16 @@ class CServer : CVector vstrChatColors; CVector vecChanIDsCurConChan; - CVector > vecvecdGains; - CVector > vecvecdPannings; + CVector > vecvecfGains; + CVector > vecvecfPannings; CVector > vecvecsData; CVector vecNumAudioChannels; CVector vecNumFrameSizeConvBlocks; CVector vecUseDoubleSysFraSizeConvBuf; CVector vecAudioComprType; - CVector vecsSendData; - CVector vecbyCodedData; + CVector > vecvecsSendData; + CVector > vecvecfIntermediateProcBuf; + CVector > vecvecbyCodedData; // Channel levels CVector vecChannelLevels; @@ -346,22 +375,21 @@ class CServer : CServerLogging Logging; // channel level update frame interval counter - uint16_t iFrameCount; - - // recording thread - recorder::CJamRecorder JamRecorder; - bool bEnableRecording; + int iFrameCount; // HTML file server status bool bWriteStatusHTMLFile; QString strServerHTMLFileListName; - QString strServerNameWithPort; CHighPrecisionTimer HighPrecisionTimer; // server list CServerListManager ServerListManager; + // jam recorder + recorder::CJamController JamController; + bool bDisableRecording; + // GUI settings bool bAutoRunMinimized; @@ -382,8 +410,16 @@ class CServer : const CHostAddress RecHostAddr, const int iNumAudChan, const CVector vecsData ); + + void CLVersionAndOSReceived ( CHostAddress InetAddr, + COSUtil::EOpSystemType eOSType, + QString strVersion ); + + // pass through from jam controller void RestartRecorder(); + void StopRecorder(); void RecordingSessionStarted ( QString sessionDir ); + void EndRecorderThread(); public slots: void OnTimer(); @@ -444,6 +480,15 @@ public slots: ServerListManager.CentralServerRegisterServer ( InetAddr, LInetAddr, ServerInfo ); } + void OnCLRegisterServerExReceived ( CHostAddress InetAddr, + CHostAddress LInetAddr, + CServerCoreInfo ServerInfo, + COSUtil::EOpSystemType , + QString strVersion ) + { + ServerListManager.CentralServerRegisterServer ( InetAddr, LInetAddr, ServerInfo, strVersion ); + } + void OnCLRegisterServerResp ( CHostAddress /* unused */, ESvrRegResult eResult ) { @@ -461,3 +506,5 @@ public slots: void OnHandledSignal ( int sigNum ); }; + +Q_DECLARE_METATYPE(CVector) diff --git a/src/serverdlg.cpp b/src/serverdlg.cpp index ca8753061a..22d86e0a06 100755 --- a/src/serverdlg.cpp +++ b/src/serverdlg.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -26,17 +26,19 @@ /* Implementation *************************************************************/ -CServerDlg::CServerDlg ( CServer* pNServP, - CSettings* pNSetP, - const bool bStartMinimized, - QWidget* parent, - Qt::WindowFlags f ) - : QDialog ( parent, f ), +CServerDlg::CServerDlg ( CServer* pNServP, + CServerSettings* pNSetP, + const bool bStartMinimized, + QWidget* parent ) + : QDialog ( parent, Qt::Window ), // use Qt::Window to get min/max window buttons pServer ( pNServP ), pSettings ( pNSetP ), BitmapSystemTrayInactive ( QString::fromUtf8 ( ":/png/LEDs/res/CLEDGreyArrow.png" ) ), BitmapSystemTrayActive ( QString::fromUtf8 ( ":/png/LEDs/res/CLEDGreenArrow.png" ) ) { + // check if system tray icon can be used + bSystemTrayIconAvaialbe = SystemTrayIcon.isSystemTrayAvailable(); + setupUi ( this ); @@ -52,20 +54,15 @@ CServerDlg::CServerDlg ( CServer* pNServP, // start minimized on operating system start chbStartOnOSStart->setWhatsThis ( "" + tr ( "Start Minimized on Operating " "System Start" ) + ": " + tr ( "If the start minimized on operating system start " - "check box is checked, the " ) + APP_NAME + tr ( " server will be " + "check box is checked, the 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 ( "Show Creative Commons Licence " - "Dialog" ) + ": " + 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 ( "Make My Server Public" ) + ": " + 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 " + "itself at the central server so that all users of the application " + "can see the server in the connect dialog server list and " "connect to it. The registration of the server is renewed periodically " "to make sure that all servers in the connect dialog server list are " "actually available." ) ); @@ -91,8 +88,7 @@ CServerDlg::CServerDlg ( CServer* pNServP, // server name QString strServName = "" + tr ( "Server Name" ) + ": " + 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." ); + "your server in the connect dialog server list at the clients." ); lblServerName->setWhatsThis ( strServName ); edtServerName->setWhatsThis ( strServName ); @@ -121,9 +117,70 @@ CServerDlg::CServerDlg ( CServer* pNServP, cbxLocationCountry->setAccessibleName ( tr ( "Country where the server is located combo box" ) ); - - // check if system tray icon can be used - bSystemTrayIconAvaialbe = SystemTrayIcon.isSystemTrayAvailable(); + // recording directory + pbtRecordingDir->setAccessibleName ( tr ( "Display dialog to select recording directory button" ) ); + pbtRecordingDir->setWhatsThis ( "" + tr ( "Main Recording Directory" ) + ": " + + tr ( "Click the button to open the dialog that allows the main recording directory to be selected." + "The chosen value must exist and be writeable (allow creation of sub-directories " + "by the user Jamulus is running as). " ) ); + + edtRecordingDir->setAccessibleName ( tr ( "Main recording directory text box (read-only)" ) ); + edtRecordingDir->setWhatsThis ( "" + tr ( "Main Recording Directory" ) + ": " + + tr ( "The current value of the main recording directory. " + "The chosen value must exist and be writeable (allow creation of sub-directories " + "by the user Jamulus is running as). " + "Click the button to open the dialog that allows the main recording directory to be selected." ) ); + + tbtClearRecordingDir->setAccessibleName ( tr ( "Clear the recording directory button" ) ); + tbtClearRecordingDir->setWhatsThis ( "" + tr ( "Clear Recording Directory" ) + ": " + + tr ( "Click the button to clear the currently selected recording directory. " + "This will prevent recording until a new value is selected.") ); + + // enable recorder + chbEnableRecorder->setAccessibleName( tr ( "Checkbox to turn on or off server recording" ) ); + chbEnableRecorder->setWhatsThis( "" + tr ( "Enable Recorder" ) + ": " + + tr ( "Checked when the recorder is enabled, otherwise unchecked. " + "The recorder will run when a session is in progress, if (set up correctly and) enabled." ) ); + + // current session directory + edtCurrentSessionDir->setAccessibleName( tr ( "Current session directory text box (read-only)" ) ); + edtCurrentSessionDir->setWhatsThis( "" + tr ( "Current Session Directory" ) + ": " + + tr ( "Enabled during recording and holds the current recording session directory. " + "Disabled after recording or when the recorder is not enabled." ) ); + + // recorder status + lblRecorderStatus->setAccessibleName ( tr ( "Recorder status label" ) ); + lblRecorderStatus->setWhatsThis ( "" + tr ( "Recorder Status" ) + ": " + + tr ( "Displays the current status of the recorder. The following values are possible:" ) + + "
" + + "
" + SREC_NOT_INITIALISED + "
" + + "
" + tr ( "No recording directory has been set or the value is not useable" ) + "
" + + "
" + SREC_NOT_ENABLED + "
" + + "
" + tr ( "Recording has been switched off" ) +#ifdef _WIN32 + + tr ( " by the UI checkbox" ) +#else + + tr ( ", either by the UI checkbox or SIGUSR2 being received" ) +#endif + + "
" + + "
" + SREC_NOT_RECORDING + "
" + + "
" + tr ( "There is no one connected to the server to record" ) + "
" + + "
" + SREC_RECORDING + "
" + + "
" + tr ( "The performers are being recorded to the specified session directory" ) + "
" + + "
" + + "
" + tr ( "NOTE" ) + ": " + + tr ( "If the recording directory is not useable, the problem will be displayed in place of the directory." ) ); + + // new recording + pbtNewRecording->setAccessibleName ( tr ( "Request new recording button" ) ); + pbtNewRecording->setWhatsThis ( "" + tr ( "New Recording" ) + ": " + + tr ( "During a recording session, the button can be used to start a new recording." ) ); + + // welcome message + tedWelcomeMessage->setAccessibleName ( tr ( "Server welcome message edit box" ) ); + tedWelcomeMessage->setWhatsThis ( "" + tr ( "Server Welcome Message" ) + ": " + + tr ( "A server welcome message text is displayed in the chat window if a " + "musician enters the server. If no message is set, the server welcome is disabled." ) ); // init system tray icon if ( bSystemTrayIconAvaialbe ) @@ -161,10 +218,6 @@ CServerDlg::CServerDlg ( CServer* pNServP, showMinimized(); } - // set text for version and application name - lblNameVersion->setText ( QString ( APP_NAME ) + - tr ( " server " ) + QString ( VERSION ) ); - // set up list view for connected clients lvwClients->setColumnWidth ( 0, 170 ); lvwClients->setColumnWidth ( 1, 200 ); @@ -196,6 +249,9 @@ lvwClients->setMinimumHeight ( 140 ); cbxCentServAddrType->addItem ( csCentServAddrTypeToString ( AT_CUSTOM ) ); cbxCentServAddrType->setCurrentIndex ( static_cast ( pServer->GetCentralServerAddressType() ) ); + // custom central server address + edtCentralServerAddress->setText ( pServer->GetServerListCentralServerAddress() ); + // update server name line edit edtServerName->setText ( pServer->GetServerName() ); @@ -237,16 +293,6 @@ lvwClients->setMinimumHeight ( 140 ); chbRegisterServer->setCheckState ( Qt::Unchecked ); } - // update show Creative Commons licence check box - if ( pServer->GetLicenceType() == LT_CREATIVECOMMONS ) - { - chbUseCCLicence->setCheckState ( Qt::Checked ); - } - else - { - chbUseCCLicence->setCheckState ( Qt::Unchecked ); - } - // update start minimized check box (only available for Windows) #ifndef _WIN32 chbStartOnOSStart->setVisible ( false ); @@ -268,20 +314,28 @@ lvwClients->setMinimumHeight ( 140 ); #endif // Recorder controls + chbEnableRecorder->setChecked ( pServer->GetRecordingEnabled() ); + edtCurrentSessionDir->setText ( "" ); pbtNewRecording->setAutoDefault ( false ); + pbtRecordingDir->setAutoDefault ( false ); + edtRecordingDir->setText ( pServer->GetRecordingDir() ); + tbtClearRecordingDir->setText ( u8"\u232B" ); - if ( !pServer->GetRecordingEnabled() ) - { - // The recorder was not enabled from the command line - // TODO: Once enabling from the GUI is implemented, remove - lblRecorderStatus->setVisible ( false ); - pbtNewRecording->setVisible ( false ); - } + UpdateRecorderStatus ( QString::null ); + + // language combo box (corrects the setting if language not found) + cbxLanguage->Init ( pSettings->strLanguage ); + + // setup welcome message GUI control + tedWelcomeMessage->setPlaceholderText ( tr ( + "Type a message here. If no message is set, the server welcome is disabled." ) ); - // TODO: Not yet implemented, so hide them! - chbEnableRecorder->setVisible ( false ); - pbtRecordingDir->setVisible ( false ); - edtRecordingsDir->setVisible ( false ); + tedWelcomeMessage->setText ( pServer->GetWelcomeMessage() ); + + // prepare update check info label (invisible by default) + lblUpdateCheck->setText ( "" + QString ( APP_NAME ) + " " + + tr ( "software upgrade available" ) + "" ); + lblUpdateCheck->hide(); // update GUI dependencies UpdateGUIDependencies(); @@ -307,65 +361,109 @@ lvwClients->setMinimumHeight ( 140 ); layout()->setMenuBar ( pMenu ); + // Window positions -------------------------------------------------------- + // main window + if ( !pSettings->vecWindowPosMain.isEmpty() && !pSettings->vecWindowPosMain.isNull() ) + { + restoreGeometry ( pSettings->vecWindowPosMain ); + } + + // Connections ------------------------------------------------------------- // check boxes - QObject::connect ( chbRegisterServer, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnRegisterServerStateChanged ( int ) ) ); + QObject::connect ( chbRegisterServer, &QCheckBox::stateChanged, + this, &CServerDlg::OnRegisterServerStateChanged ); - QObject::connect ( chbStartOnOSStart, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnStartOnOSStartStateChanged ( int ) ) ); + QObject::connect ( chbStartOnOSStart, &QCheckBox::stateChanged, + this, &CServerDlg::OnStartOnOSStartStateChanged ); - QObject::connect ( chbUseCCLicence, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnUseCCLicenceStateChanged ( int ) ) ); + QObject::connect ( chbEnableRecorder, &QCheckBox::stateChanged, + this, &CServerDlg::OnEnableRecorderStateChanged ); // line edits - QObject::connect ( edtCentralServerAddress, SIGNAL ( editingFinished() ), - this, SLOT ( OnCentralServerAddressEditingFinished() ) ); + QObject::connect ( edtCentralServerAddress, &QLineEdit::editingFinished, + this, &CServerDlg::OnCentralServerAddressEditingFinished ); - QObject::connect ( edtServerName, SIGNAL ( textChanged ( const QString& ) ), - this, SLOT ( OnServerNameTextChanged ( const QString& ) ) ); + QObject::connect ( edtServerName, &QLineEdit::textChanged, + this, &CServerDlg::OnServerNameTextChanged ); - QObject::connect ( edtLocationCity, SIGNAL ( textChanged ( const QString& ) ), - this, SLOT ( OnLocationCityTextChanged ( const QString& ) ) ); + QObject::connect ( edtLocationCity, &QLineEdit::textChanged, + this, &CServerDlg::OnLocationCityTextChanged ); // combo boxes - QObject::connect ( cbxLocationCountry, SIGNAL ( activated ( int ) ), - this, SLOT ( OnLocationCountryActivated ( int ) ) ); + QObject::connect ( cbxLocationCountry, static_cast ( &QComboBox::activated ), + this, &CServerDlg::OnLocationCountryActivated ); - QObject::connect ( cbxCentServAddrType, SIGNAL ( activated ( int ) ), - this, SLOT ( OnCentServAddrTypeActivated ( int ) ) ); + QObject::connect ( cbxCentServAddrType, static_cast ( &QComboBox::activated ), + this, &CServerDlg::OnCentServAddrTypeActivated ); + + QObject::connect ( cbxLanguage, &CLanguageComboBox::LanguageChanged, + this, &CServerDlg::OnLanguageChanged ); // push buttons - QObject::connect ( pbtNewRecording, SIGNAL ( released() ), - this, SLOT ( OnNewRecordingClicked() ) ); + QObject::connect ( pbtRecordingDir, &QPushButton::released, + this, &CServerDlg::OnRecordingDirClicked ); + + QObject::connect ( pbtNewRecording, &QPushButton::released, + this, &CServerDlg::OnNewRecordingClicked ); + + // tool buttons + QObject::connect ( tbtClearRecordingDir, &QToolButton::released, + this, &CServerDlg::OnClearRecordingDirClicked ); // timers - QObject::connect ( &Timer, SIGNAL ( timeout() ), this, SLOT ( OnTimer() ) ); + QObject::connect ( &Timer, &QTimer::timeout, + this, &CServerDlg::OnTimer ); // other - QObject::connect ( pServer, SIGNAL ( Started() ), - this, SLOT ( OnServerStarted() ) ); + QObject::connect ( tedWelcomeMessage, &QTextEdit::textChanged, + this, &CServerDlg::OnWelcomeMessageChanged ); + + QObject::connect ( pServer, &CServer::Started, + this, &CServerDlg::OnServerStarted ); + + QObject::connect ( pServer, &CServer::Stopped, + this, &CServerDlg::OnServerStopped ); - QObject::connect ( pServer, SIGNAL ( Stopped() ), - this, SLOT ( OnServerStopped() ) ); + QObject::connect ( pServer, &CServer::SvrRegStatusChanged, + this, &CServerDlg::OnSvrRegStatusChanged ); - QObject::connect ( pServer, SIGNAL ( SvrRegStatusChanged() ), - this, SLOT ( OnSvrRegStatusChanged() ) ); + QObject::connect ( pServer, &CServer::RecordingSessionStarted, + this, &CServerDlg::OnRecordingSessionStarted ); - QObject::connect ( pServer, SIGNAL ( RecordingSessionStarted ( QString ) ), - this, SLOT ( OnRecordingSessionStarted ( QString ) ) ); + QObject::connect ( pServer, &CServer::StopRecorder, + this, &CServerDlg::OnStopRecorder ); - QObject::connect ( QCoreApplication::instance(), SIGNAL ( aboutToQuit() ), - this, SLOT ( OnAboutToQuit() ) ); + QObject::connect ( pServer, &CServer::CLVersionAndOSReceived, + this, &CServerDlg::OnCLVersionAndOSReceived ); - QObject::connect ( &SystemTrayIcon, - SIGNAL ( activated ( QSystemTrayIcon::ActivationReason ) ), - this, SLOT ( OnSysTrayActivated ( QSystemTrayIcon::ActivationReason ) ) ); + QObject::connect ( &SystemTrayIcon, &QSystemTrayIcon::activated, + this, &CServerDlg::OnSysTrayActivated ); - // Timers ------------------------------------------------------------------ + // Initializations which have to be done after the signals are connected --- // start timer for GUI controls Timer.start ( GUI_CONTRL_UPDATE_TIME ); + + // query the central server version number needed for update check (note + // that the connection less message respond may not make it back but that + // is not critical since the next time Jamulus is started we have another + // chance and the update check is not time-critical at all) + CHostAddress CentServerHostAddress; + + if ( NetworkUtil().ParseNetworkAddress ( DEFAULT_SERVER_ADDRESS, CentServerHostAddress ) ) + { + pServer->CreateCLServerListReqVerAndOSMes ( CentServerHostAddress ); + } +} + +void CServerDlg::closeEvent ( QCloseEvent* Event ) +{ + // store window positions + pSettings->vecWindowPosMain = saveGeometry(); + + // default implementation of this event handler routine + Event->accept(); } void CServerDlg::OnStartOnOSStartStateChanged ( int value ) @@ -377,18 +475,6 @@ void CServerDlg::OnStartOnOSStartStateChanged ( int value ) ModifyAutoStartEntry ( bCurAutoStartMinState ); } -void CServerDlg::OnUseCCLicenceStateChanged ( int value ) -{ - if ( value == Qt::Checked ) - { - pServer->SetLicenceType ( LT_CREATIVECOMMONS ); - } - else - { - pServer->SetLicenceType ( LT_NO_LICENCE ); - } -} - void CServerDlg::OnRegisterServerStateChanged ( int value ) { const bool bRegState = ( value == Qt::Checked ); @@ -428,7 +514,7 @@ void CServerDlg::OnServerNameTextChanged ( const QString& strNewName ) } else { - // text is too long, update control with shortend text + // text is too long, update control with shortened text edtServerName->setText ( strNewName.left ( MAX_LEN_SERVER_NAME ) ); } } @@ -444,7 +530,7 @@ void CServerDlg::OnLocationCityTextChanged ( const QString& strNewCity ) } else { - // text is too long, update control with shortend text + // text is too long, update control with shortened text edtLocationCity->setText ( strNewCity.left ( MAX_LEN_SERVER_CITY ) ); } } @@ -474,12 +560,48 @@ void CServerDlg::OnCentServAddrTypeActivated ( int iTypeIdx ) UpdateGUIDependencies(); } +void CServerDlg::OnServerStarted() +{ + UpdateSystemTrayIcon ( true ); + UpdateRecorderStatus ( QString::null ); +} + void CServerDlg::OnServerStopped() { UpdateSystemTrayIcon ( false ); UpdateRecorderStatus ( QString::null ); } +void CServerDlg::OnStopRecorder() +{ + UpdateRecorderStatus ( QString::null ); +} + +void CServerDlg::OnRecordingDirClicked() +{ + // get the current value from pServer + QString currentValue = pServer->GetRecordingDir(); + QString newRecordingDir = QFileDialog::getExistingDirectory ( this, + tr ( "Select Main Recording Directory" ), + currentValue, + QFileDialog::ShowDirsOnly | QFileDialog::DontUseNativeDialog ); + + if ( newRecordingDir != currentValue ) + { + pServer->SetRecordingDir ( newRecordingDir ); + UpdateRecorderStatus ( QString::null ); + } +} + +void CServerDlg::OnClearRecordingDirClicked() +{ + if ( pServer->GetRecorderErrMsg() != QString::null || pServer->GetRecordingDir() != "" ) + { + pServer->SetRecordingDir ( "" ); + UpdateRecorderStatus ( QString::null ); + } +} + void CServerDlg::OnSysTrayActivated ( QSystemTrayIcon::ActivationReason ActReason ) { // on double click on the icon, show window in fore ground @@ -489,6 +611,19 @@ void CServerDlg::OnSysTrayActivated ( QSystemTrayIcon::ActivationReason ActReaso } } +void CServerDlg::OnCLVersionAndOSReceived ( CHostAddress , + COSUtil::EOpSystemType , + QString strVersion ) +{ + // update check +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) + if ( QVersionNumber::compare ( QVersionNumber::fromString ( strVersion ), QVersionNumber::fromString ( VERSION ) ) > 0 ) + { + lblUpdateCheck->show(); + } +#endif +} + void CServerDlg::OnTimer() { CVector vecHostAddresses; @@ -536,40 +671,14 @@ void CServerDlg::OnTimer() void CServerDlg::UpdateGUIDependencies() { // get the states which define the GUI dependencies from the server - const bool bCurSerListEnabled = pServer->GetServerListEnabled(); - - const bool bCurUseDefCentServAddr = ( pServer->GetCentralServerAddressType() != AT_CUSTOM ); - - const ESvrRegStatus eSvrRegStatus = pServer->GetSvrRegStatus(); + const bool bCurSerListEnabled = pServer->GetServerListEnabled(); + const ESvrRegStatus eSvrRegStatus = pServer->GetSvrRegStatus(); // if register server is not enabled, we disable all the configuration // controls for the server list cbxCentServAddrType->setEnabled ( bCurSerListEnabled ); grbServerInfo->setEnabled ( bCurSerListEnabled ); - // make sure the line edit does not fire signals when we update the text - edtCentralServerAddress->blockSignals ( true ); - { - if ( bCurUseDefCentServAddr ) - { - // if the default central server is used, just show a text of the - // server name - edtCentralServerAddress->setText ( tr ( "Predefined Address" ) ); - } - else - { - // show the current user defined server address - edtCentralServerAddress->setText ( - pServer->GetServerListCentralServerAddress() ); - } - } - edtCentralServerAddress->blockSignals ( false ); - - // the line edit of the central server address is only enabled, if the - // server list is enabled and not the default address is used - edtCentralServerAddress->setEnabled ( - !bCurUseDefCentServAddr && bCurSerListEnabled ); - QString strStatus = svrRegStatusToString ( eSvrRegStatus ); switch ( eSvrRegStatus ) @@ -577,6 +686,8 @@ void CServerDlg::UpdateGUIDependencies() case SRS_BAD_ADDRESS: case SRS_TIME_OUT: case SRS_CENTRAL_SVR_FULL: + case SRS_VERSION_TOO_OLD: + case SRS_NOT_FULFILL_REQUIREMENTS: strStatus = "" + strStatus + ""; break; @@ -589,9 +700,6 @@ void CServerDlg::UpdateGUIDependencies() } lblRegSvrStatus->setText ( strStatus ); - - edtCurrentSessionDir->setText ( "" ); - UpdateRecorderStatus ( QString::null ); } void CServerDlg::UpdateSystemTrayIcon ( const bool bIsActive ) @@ -653,33 +761,55 @@ void CServerDlg::ModifyAutoStartEntry ( const bool bDoAutoStart ) void CServerDlg::UpdateRecorderStatus ( QString sessionDir ) { QString currentSessionDir = edtCurrentSessionDir->text(); + QString errMsg = pServer->GetRecorderErrMsg(); bool bIsRecording = false; QString strRecorderStatus; + QString strRecordingDir; - if ( pServer->GetRecordingEnabled() ) + if ( pServer->GetRecorderInitialised() ) { - if ( pServer->IsRunning() ) + strRecordingDir = pServer->GetRecordingDir(); + chbEnableRecorder->setEnabled ( true ); + + if ( pServer->GetRecordingEnabled() ) { - currentSessionDir = sessionDir != QString::null ? sessionDir : ""; - strRecorderStatus = tr ( "Recording" ); - bIsRecording = true; + if ( pServer->IsRunning() ) + { + edtCurrentSessionDir->setText ( sessionDir != QString::null ? sessionDir : "" ); + + strRecorderStatus = SREC_RECORDING; + bIsRecording = true; + } + else + { + strRecorderStatus = SREC_NOT_RECORDING; + } } else { - strRecorderStatus = tr ( "Not recording" ); + strRecorderStatus = SREC_NOT_ENABLED; } } else { - strRecorderStatus = tr ( "Not enabled" ); + strRecordingDir = pServer->GetRecorderErrMsg(); + + if ( strRecordingDir == QString::null ) + { + strRecordingDir = pServer->GetRecordingDir(); + } + else + { + strRecordingDir = tr ( "ERROR" ) + ": " + strRecordingDir; + } + + chbEnableRecorder->setEnabled ( false ); + strRecorderStatus = SREC_NOT_INITIALISED; } - edtCurrentSessionDir->setVisible ( pServer->GetRecordingEnabled() ); + edtRecordingDir->setText ( strRecordingDir ); edtCurrentSessionDir->setEnabled ( bIsRecording ); - edtCurrentSessionDir->setText ( currentSessionDir ); - lblRecorderStatus->setText ( strRecorderStatus ); - pbtNewRecording->setEnabled ( bIsRecording ); } @@ -696,5 +826,11 @@ void CServerDlg::changeEvent ( QEvent* pEvent ) // the timer for this purpose QTimer::singleShot ( 0, this, SLOT ( hide() ) ); } + else + { + // we have to call the show function from another thread -> use + // the timer for this purpose + QTimer::singleShot ( 0, this, SLOT ( show() ) ); + } } } diff --git a/src/serverdlg.h b/src/serverdlg.h index 8227032a45..399b4b98e9 100755 --- a/src/serverdlg.h +++ b/src/serverdlg.h @@ -18,10 +18,12 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ +#pragma once + #include #include #include @@ -33,6 +35,10 @@ #include #include #include +#include +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) +# include +#endif #include "global.h" #include "server.h" #include "settings.h" @@ -43,6 +49,12 @@ // update time for GUI controls #define GUI_CONTRL_UPDATE_TIME 1000 // ms +// Strings used in multiple places +#define SREC_NOT_INITIALISED CServerDlg::tr ( "Not initialised" ) +#define SREC_NOT_ENABLED CServerDlg::tr ( "Not enabled" ) +#define SREC_NOT_RECORDING CServerDlg::tr ( "Not recording" ) +#define SREC_RECORDING CServerDlg::tr ( "Recording" ) + /* Classes ********************************************************************/ class CServerDlg : public QDialog, private Ui_CServerDlgBase @@ -50,14 +62,14 @@ class CServerDlg : public QDialog, private Ui_CServerDlgBase Q_OBJECT public: - CServerDlg ( CServer* pNServP, - CSettings* pNSetP, - const bool bStartMinimized, - QWidget* parent = nullptr, - Qt::WindowFlags f = nullptr ); + CServerDlg ( CServer* pNServP, + CServerSettings* pNSetP, + const bool bStartMinimized, + QWidget* parent = nullptr ); protected: virtual void changeEvent ( QEvent* pEvent ); + virtual void closeEvent ( QCloseEvent* Event ); void UpdateGUIDependencies(); void UpdateSystemTrayIcon ( const bool bIsActive ); @@ -65,45 +77,54 @@ class CServerDlg : public QDialog, private Ui_CServerDlgBase void ModifyAutoStartEntry ( const bool bDoAutoStart ); void UpdateRecorderStatus( QString sessionDir ); - QTimer Timer; - CServer* pServer; - CSettings* pSettings; + QTimer Timer; + CServer* pServer; + CServerSettings* pSettings; - CVector vecpListViewItems; - QMutex ListViewMutex; + CVector vecpListViewItems; + QMutex ListViewMutex; - QMenuBar* pMenu; + QMenuBar* pMenu; - bool bSystemTrayIconAvaialbe; - QSystemTrayIcon SystemTrayIcon; - QPixmap BitmapSystemTrayInactive; - QPixmap BitmapSystemTrayActive; - QMenu* pSystemTrayIconMenu; + bool bSystemTrayIconAvaialbe; + QSystemTrayIcon SystemTrayIcon; + QPixmap BitmapSystemTrayInactive; + QPixmap BitmapSystemTrayActive; + QMenu* pSystemTrayIconMenu; public slots: - void OnAboutToQuit() { pSettings->Save(); } - void OnRegisterServerStateChanged ( int value ); void OnStartOnOSStartStateChanged ( int value ); - void OnUseCCLicenceStateChanged ( int value ); + void OnEnableRecorderStateChanged ( int value ) + { pServer->SetEnableRecording ( Qt::CheckState::Checked == value ); } + void OnCentralServerAddressEditingFinished(); void OnServerNameTextChanged ( const QString& strNewName ); void OnLocationCityTextChanged ( const QString& strNewCity ); void OnLocationCountryActivated ( int iCntryListItem ); void OnCentServAddrTypeActivated ( int iTypeIdx ); void OnTimer(); - void OnServerStarted() { UpdateSystemTrayIcon ( true ); } + void OnServerStarted(); void OnServerStopped(); void OnSvrRegStatusChanged() { UpdateGUIDependencies(); } + void OnStopRecorder(); void OnSysTrayMenuOpen() { ShowWindowInForeground(); } void OnSysTrayMenuHide() { hide(); } void OnSysTrayMenuExit() { close(); } void OnSysTrayActivated ( QSystemTrayIcon::ActivationReason ActReason ); + void OnWelcomeMessageChanged() { pServer->SetWelcomeMessage ( tedWelcomeMessage->toPlainText() ); } void keyPressEvent ( QKeyEvent *e ) // block escape key { if ( e->key() != Qt::Key_Escape ) QDialog::keyPressEvent ( e ); } - void OnNewRecordingClicked() { pServer->RestartRecorder(); } + void OnLanguageChanged ( QString strLanguage ) { pSettings->strLanguage = strLanguage; } + void OnNewRecordingClicked() { pServer->RequestNewRecording(); } + void OnRecordingDirClicked(); + void OnClearRecordingDirClicked(); void OnRecordingSessionStarted ( QString sessionDir ) { UpdateRecorderStatus ( sessionDir ); } + + void OnCLVersionAndOSReceived ( CHostAddress , + COSUtil::EOpSystemType , + QString strVersion ); }; diff --git a/src/serverdlgbase.ui b/src/serverdlgbase.ui index 6e55a0ed89..3600f07d5d 100755 --- a/src/serverdlgbase.ui +++ b/src/serverdlgbase.ui @@ -7,7 +7,7 @@ 0 0 588 - 415 + 560
@@ -15,7 +15,7 @@ - :/png/main/res/fronticon.png:/png/main/res/fronticon.png + :/png/main/res/fronticonserver.png:/png/main/res/fronticonserver.png true @@ -47,245 +47,275 @@
- - - Start Minimized on Windows Start + + + + 0 + 0 + - - - - - - Show Creative Commons BY-NC-SA 4.0 Licence Dialog + + 0 + + + Server Setup + + + + + + Make My Server Public (Register My Server in the Server List) + + + + + + + + + Genre + + + + + + + + + + STATUS + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 20 + 20 + + + + + + + + + + My Server Info + + + + + + + + Name + + + + + + + Location: City + + + + + + + Location: Country + + + + + + + + + + + + + + + + + + + + + + + + + + + Enable Jam Recorder + + + + + + + true + + + + + + + STATUS + + + + + + + New Recording + + + + + + + + + Chat Window Welcome (HTML/CSS Supported) + + + + + + + + 0 + 0 + + + + + + + + + Options + + + + + + + + Language + + + + + + + + + + + + + + Recording Directory + + + + + + + true + + + + + + + + + + + + + + Custom Central Server Address: + + + + + + + + + + + + Start Minimized on Windows Start + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + - - - - - Make My Server Public (Register My Server in the Server List) - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 20 - 20 - - - - - - - - STATUS - - - - - - - - - - - Custom Central Server Address: - - - - - - - - - - - - My Server Info + + + Update check - - - - - - - Name - - - - - - - Location: City - - - - - - - Location: Country - - - - - - - - - - - - - - - - - - - - - - - - - Enable jam recorder - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 20 - 20 - - - - - - - - true - - - - - - - STATUS - - - - - - - New recording - - - - - - - - - - - Recordings folder - - - - - - - true - - - - - - - - - 6 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - TextLabelNameVersion - - - false - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 20 - 20 - - - - - - + + + CLanguageComboBox + QComboBox +
util.h
+
+
lvwClients - chbStartOnOSStart - chbUseCCLicence + tabWidget chbRegisterServer cbxCentServAddrType - edtCentralServerAddress edtServerName edtLocationCity cbxLocationCountry chbEnableRecorder edtCurrentSessionDir pbtNewRecording + tedWelcomeMessage + cbxLanguage pbtRecordingDir - edtRecordingsDir + edtRecordingDir + tbtClearRecordingDir + edtCentralServerAddress + chbStartOnOSStart diff --git a/src/serverlist.cpp b/src/serverlist.cpp index 1d8e1a482b..cb059d9388 100755 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -28,13 +28,12 @@ CServerListManager::CServerListManager ( const quint16 iNPortNum, const QString& sNCentServAddr, const QString& strServerInfo, + const QString& strServerListFilter, const int iNumChannels, - const bool bNCentServPingServerInList, CProtocol* pNConLProt ) : tsConsoleStream ( *( ( new ConsoleWriterFactory() )->get() ) ), - iNumPredefinedServers ( 0 ), eCentralServerAddressType ( AT_CUSTOM ), // must be AT_CUSTOM for the "no GUI" case - bCentServPingServerInList ( bNCentServPingServerInList ), + strMinServerVersion ( "" ), // disable version check with empty version pConnLessProtocol ( pNConLProt ), eSvrRegStatus ( SRS_UNREGISTERED ), iSvrRegRetries ( 0 ) @@ -63,7 +62,7 @@ CServerListManager::CServerListManager ( const quint16 iNPortNum, ServerList.clear(); // Init server list entry (server info for this server) with defaults. Per - // definition the client substitudes the IP address of the central server + // definition the client substitutes the IP address of the central server // itself for his server list. If we are the central server, we assume that // we have a permanent server. CServerListEntry ThisServerListEntry ( CHostAddress(), @@ -91,71 +90,35 @@ CServerListManager::CServerListManager ( const quint16 iNPortNum, if ( !slServInfoSeparateParams[2].isEmpty() && ( iCountry >= 0 ) && ( iCountry <= QLocale::LastCountry ) ) { - ThisServerListEntry.eCountry = static_cast ( - iCountry ); + ThisServerListEntry.eCountry = static_cast ( iCountry ); } } // per definition, the first entry in the server list is the own server ServerList.append ( ThisServerListEntry ); - // parse the predefined server infos (if any) according to definition: - // [server1 address];[server1 name];[server1 city]; ... - // [server1 country as QLocale ID]; ... - // [server2 address];[server2 name];[server2 city]; ... - // [server2 country as QLocale ID]; ... - // ... - int iCurUsedServInfoSplitItems = 3; // three items are used for this server - - // we always expect four items per new server, also check for maximum - // allowed number of servers in the server list - while ( ( iServInfoNumSplitItems - iCurUsedServInfoSplitItems >= 4 ) && - ( iNumPredefinedServers <= MAX_NUM_SERVERS_IN_SERVER_LIST ) ) + // whitelist parsing + if ( !strServerListFilter.isEmpty() ) { - // create a new server list entry, we assume that servers which are - // registered via the command line are permanent servers - CServerListEntry NewServerListEntry ( CHostAddress(), - CHostAddress(), - "", - QLocale::AnyCountry, - "", - iNumChannels, - true ); - - // [server n address] - NetworkUtil().ParseNetworkAddress ( - slServInfoSeparateParams[iCurUsedServInfoSplitItems], - NewServerListEntry.HostAddr ); - - // [server n server internal address] - // Not included in the static server info, so use external address - NewServerListEntry.LHostAddr = NewServerListEntry.HostAddr; - - // [server n name] - NewServerListEntry.strName = - slServInfoSeparateParams[iCurUsedServInfoSplitItems + 1].left ( MAX_LEN_SERVER_NAME ); - - // [server n city] - NewServerListEntry.strCity = - slServInfoSeparateParams[iCurUsedServInfoSplitItems + 2].left ( MAX_LEN_SERVER_CITY ); - - // [server n country as QLocale ID] - const int iCountry = - slServInfoSeparateParams[iCurUsedServInfoSplitItems + 3].toInt(); - - if ( ( iCountry >= 0 ) && ( iCountry <= QLocale::LastCountry ) ) + // split the different parameter strings + QStringList slWhitelistAddresses = strServerListFilter.split ( ";" ); + QHostAddress CurWhiteListAddress; + + for ( int iIdx = 0; iIdx < slWhitelistAddresses.size(); iIdx++ ) { - NewServerListEntry.eCountry = static_cast ( - iCountry ); + // check for special case: [version] + if ( ( slWhitelistAddresses.at ( iIdx ).length() > 2 ) && + ( slWhitelistAddresses.at ( iIdx ).left ( 1 ) == "[" ) && + ( slWhitelistAddresses.at ( iIdx ).right ( 1 ) == "]" ) ) + { + strMinServerVersion = slWhitelistAddresses.at ( iIdx ).mid ( 1, slWhitelistAddresses.at ( iIdx ).length() - 2 ); + } + else if ( CurWhiteListAddress.setAddress ( slWhitelistAddresses.at ( iIdx ) ) ) + { + vWhiteList << CurWhiteListAddress; + tsConsoleStream << "Whitelist entry added: " << CurWhiteListAddress.toString() << endl; + } } - - // add the new server to the server list - ServerList.append ( NewServerListEntry ); - - // we have used four items and have created one predefined server - // (adjust counters) - iCurUsedServInfoSplitItems += 4; - iNumPredefinedServers++; } // for slave servers start the one shot timer for determining if it is a @@ -173,20 +136,20 @@ CServerListManager::CServerListManager ( const quint16 iNPortNum, // Connections ------------------------------------------------------------- - QObject::connect ( &TimerPollList, SIGNAL ( timeout() ), - this, SLOT ( OnTimerPollList() ) ); + QObject::connect ( &TimerPollList, &QTimer::timeout, + this, &CServerListManager::OnTimerPollList ); - QObject::connect ( &TimerPingServerInList, SIGNAL ( timeout() ), - this, SLOT ( OnTimerPingServerInList() ) ); + QObject::connect ( &TimerPingServerInList, &QTimer::timeout, + this, &CServerListManager::OnTimerPingServerInList ); - QObject::connect ( &TimerPingCentralServer, SIGNAL ( timeout() ), - this, SLOT ( OnTimerPingCentralServer() ) ); + QObject::connect ( &TimerPingCentralServer, &QTimer::timeout, + this, &CServerListManager::OnTimerPingCentralServer ); - QObject::connect ( &TimerRegistering, SIGNAL ( timeout() ), - this, SLOT ( OnTimerRegistering() ) ); + QObject::connect ( &TimerRegistering, &QTimer::timeout, + this, &CServerListManager::OnTimerRegistering ); - QObject::connect ( &TimerCLRegisterServerResp, SIGNAL ( timeout() ), - this, SLOT ( OnTimerCLRegisterServerResp() ) ); + QObject::connect ( &TimerCLRegisterServerResp, &QTimer::timeout, + this, &CServerListManager::OnTimerCLRegisterServerResp ); } void CServerListManager::SetCentralServerAddress ( const QString sNCentServAddr ) @@ -230,11 +193,8 @@ void CServerListManager::Update() // 1 minute = 60 * 1000 ms TimerPollList.start ( SERVLIST_POLL_TIME_MINUTES * 60000 ); - if ( bCentServPingServerInList ) - { - // start timer for sending ping messages to servers in the list - TimerPingServerInList.start ( SERVLIST_UPDATE_PING_SERVERS_MS ); - } + // start timer for sending ping messages to servers in the list + TimerPingServerInList.start ( SERVLIST_UPDATE_PING_SERVERS_MS ); } else { @@ -274,11 +234,7 @@ void CServerListManager::Update() if ( bIsCentralServer ) { TimerPollList.stop(); - - if ( bCentServPingServerInList ) - { - TimerPingServerInList.stop(); - } + TimerPingServerInList.stop(); } else { @@ -298,8 +254,8 @@ void CServerListManager::OnTimerPingServerInList() const int iCurServerListSize = ServerList.size(); // send ping to list entries except of the very first one (which is the central - // server entry) and the predefined servers - for ( int iIdx = 1 + iNumPredefinedServers; iIdx < iCurServerListSize; iIdx++ ) + // server entry) + for ( int iIdx = 1; iIdx < iCurServerListSize; iIdx++ ) { // send empty message to keep NAT port open at slave server pConnLessProtocol->CreateCLEmptyMes ( ServerList[iIdx].HostAddr ); @@ -313,10 +269,10 @@ void CServerListManager::OnTimerPollList() QMutexLocker locker ( &Mutex ); // Check all list entries except of the very first one (which is the central - // server entry) and the predefined servers if they are still valid. + // server entry) if they are still valid. // Note that we have to use "ServerList.size()" function in the for loop // since we may remove elements from the server list inside the for loop. - for ( int iIdx = 1 + iNumPredefinedServers; iIdx < ServerList.size(); ) + for ( int iIdx = 1; iIdx < ServerList.size(); ) { // 1 minute = 60 * 1000 ms if ( ServerList[iIdx].RegisterTime.elapsed() > ( SERVLIST_TIME_OUT_MINUTES * 60000 ) ) @@ -342,7 +298,8 @@ void CServerListManager::OnTimerPollList() void CServerListManager::CentralServerRegisterServer ( const CHostAddress& InetAddr, const CHostAddress& LInetAddr, - const CServerCoreInfo& ServerInfo ) + const CServerCoreInfo& ServerInfo, + const QString strVersion ) { if ( bIsCentralServer && bEnabled ) { @@ -350,6 +307,31 @@ void CServerListManager::CentralServerRegisterServer ( const CHostAddress& In << InetAddr.toString() << " (" << LInetAddr.toString() << ")" << ": " << ServerInfo.strName << endl; + // check for minimum server version + if ( !strMinServerVersion.isEmpty() ) + { +#if ( QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) ) + if ( strVersion.isEmpty() || + QVersionNumber::compare ( QVersionNumber::fromString ( strMinServerVersion ), QVersionNumber::fromString ( strVersion ) ) > 0 ) + { + pConnLessProtocol->CreateCLRegisterServerResp ( InetAddr, SRR_NOT_FULFILL_REQIREMENTS ); + return; // leave function early, i.e., we do not register this server + } +#endif + } + + // check for whitelist (it is enabled if it is not empty per definition) + if ( !vWhiteList.empty() ) + { + // if the server is not listed, refuse registration and send registration response + if ( !vWhiteList.contains ( InetAddr.InetAddr ) ) + { + pConnLessProtocol->CreateCLRegisterServerResp ( InetAddr, SRR_NOT_FULFILL_REQIREMENTS ); + return; // leave function early, i.e., we do not register this server + } + } + + // access/modifications to the server list needs to be mutexed QMutexLocker locker ( &Mutex ); const int iCurServerListSize = ServerList.size(); @@ -358,6 +340,7 @@ void CServerListManager::CentralServerRegisterServer ( const CHostAddress& In // The very first list entry must not be checked since // this is per definition the central server (i.e., this server) int iSelIdx = INVALID_INDEX; // initialize with an illegal value + for ( int iIdx = 1; iIdx < iCurServerListSize; iIdx++ ) { if ( ServerList[iIdx].HostAddr == InetAddr ) @@ -383,19 +366,15 @@ void CServerListManager::CentralServerRegisterServer ( const CHostAddress& In } else { - // do not update the information in the predefined servers - if ( iSelIdx > iNumPredefinedServers ) - { - // update all data and call update registration function - ServerList[iSelIdx].LHostAddr = LInetAddr; - ServerList[iSelIdx].strName = ServerInfo.strName; - ServerList[iSelIdx].eCountry = ServerInfo.eCountry; - ServerList[iSelIdx].strCity = ServerInfo.strCity; - ServerList[iSelIdx].iMaxNumClients = ServerInfo.iMaxNumClients; - ServerList[iSelIdx].bPermanentOnline = ServerInfo.bPermanentOnline; - - ServerList[iSelIdx].UpdateRegistration(); - } + // update all data and call update registration function + ServerList[iSelIdx].LHostAddr = LInetAddr; + ServerList[iSelIdx].strName = ServerInfo.strName; + ServerList[iSelIdx].eCountry = ServerInfo.eCountry; + ServerList[iSelIdx].strCity = ServerInfo.strCity; + ServerList[iSelIdx].iMaxNumClients = ServerInfo.iMaxNumClients; + ServerList[iSelIdx].bPermanentOnline = ServerInfo.bPermanentOnline; + + ServerList[iSelIdx].UpdateRegistration(); } pConnLessProtocol->CreateCLRegisterServerResp ( InetAddr, iSelIdx == INVALID_INDEX @@ -417,8 +396,8 @@ void CServerListManager::CentralServerUnregisterServer ( const CHostAddress& Ine // Find the server to unregister in the list. The very first list entry // must not be checked since this is per definition the central server - // (i.e., this server), also the predefined servers must not be checked. - for ( int iIdx = 1 + iNumPredefinedServers; iIdx < iCurServerListSize; iIdx++ ) + // (i.e., this server). + for ( int iIdx = 1; iIdx < iCurServerListSize; iIdx++ ) { if ( ServerList[iIdx].HostAddr == InetAddr ) { @@ -462,14 +441,7 @@ void CServerListManager::CentralServerQueryServerList ( const CHostAddress& Inet // to allow for NAT. if ( vecServerInfo[iIdx].HostAddr.InetAddr == InetAddr.InetAddr ) { - // for a predefined server: - // - LHostAddr and HostAddr are the same - // - no local port number is supplied - // otherwise, use the supplied details - if ( iIdx > iNumPredefinedServers ) - { - vecServerInfo[iIdx].HostAddr = ServerList[iIdx].LHostAddr; - } + vecServerInfo[iIdx].HostAddr = ServerList[iIdx].LHostAddr; } else { @@ -485,7 +457,10 @@ void CServerListManager::CentralServerQueryServerList ( const CHostAddress& Inet } } - // send the server list to the client + // send the server list to the client, since we do not know that the client + // has a UDP fragmentation issue, we send both lists, the reduced and the + // normal list after each other + pConnLessProtocol->CreateCLRedServerListMes ( InetAddr, vecServerInfo ); pConnLessProtocol->CreateCLServerListMes ( InetAddr, vecServerInfo ); } } @@ -511,6 +486,14 @@ void CServerListManager::StoreRegistrationResult ( ESvrRegResult eResult ) SetSvrRegStatus ( ESvrRegStatus::SRS_CENTRAL_SVR_FULL ); break; + case ESvrRegResult::SRR_VERSION_TOO_OLD: + SetSvrRegStatus ( ESvrRegStatus::SRS_VERSION_TOO_OLD ); + break; + + case ESvrRegResult::SRR_NOT_FULFILL_REQIREMENTS: + SetSvrRegStatus ( ESvrRegStatus::SRS_NOT_FULFILL_REQUIREMENTS ); + break; + default: SetSvrRegStatus ( ESvrRegStatus::SRS_UNKNOWN_RESP ); break; @@ -581,9 +564,9 @@ void CServerListManager::SlaveServerRegisterServer ( const bool bIsRegister ) // register server SetSvrRegStatus ( SRS_REQUESTED ); - pConnLessProtocol->CreateCLRegisterServerMes ( SlaveCurCentServerHostAddress, - SlaveCurLocalHostAddress, - ServerList[0] ); + pConnLessProtocol->CreateCLRegisterServerExMes ( SlaveCurCentServerHostAddress, + SlaveCurLocalHostAddress, + ServerList[0] ); } else { diff --git a/src/serverlist.h b/src/serverlist.h index decf9d72ae..405f1a14e4 100755 --- a/src/serverlist.h +++ b/src/serverlist.h @@ -53,7 +53,7 @@ private network. * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -64,6 +64,9 @@ private network. #include #include #include +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) +# include +#endif #include "global.h" #include "util.h" #include "protocol.h" @@ -124,8 +127,8 @@ class CServerListManager : public QObject CServerListManager ( const quint16 iNPortNum, const QString& sNCentServAddr, const QString& strServerInfo, + const QString& strServerListFilter, const int iNumChannels, - const bool bNCentServPingServerInList, CProtocol* pNConLProt ); // the update has to be called if any change to the server list @@ -145,7 +148,8 @@ class CServerListManager : public QObject void CentralServerRegisterServer ( const CHostAddress& InetAddr, const CHostAddress& LInetAddr, - const CServerCoreInfo& ServerInfo ); + const CServerCoreInfo& ServerInfo, + const QString strVersion = "" ); void CentralServerUnregisterServer ( const CHostAddress& InetAddr ); @@ -191,15 +195,16 @@ class CServerListManager : public QObject QList ServerList; QString strCentralServerAddress; - int iNumPredefinedServers; bool bEnabled; bool bIsCentralServer; ECSAddType eCentralServerAddressType; - bool bCentServPingServerInList; CHostAddress SlaveCurCentServerHostAddress; CHostAddress SlaveCurLocalHostAddress; + QList vWhiteList; + QString strMinServerVersion; + CProtocol* pConnLessProtocol; // server registration status diff --git a/src/serverlogging.cpp b/src/serverlogging.cpp index f1233362f0..6ed905b1e6 100755 --- a/src/serverlogging.cpp +++ b/src/serverlogging.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -45,48 +45,26 @@ void CServerLogging::Start ( const QString& strLoggingFileName ) } } -void CServerLogging::EnableHistory ( const QString& strHistoryFileName ) -{ - if ( strHistoryFileName.right ( 4 ).compare ( ".svg", Qt::CaseInsensitive ) == 0 ) - { - SvgHistoryGraph.Start ( strHistoryFileName ); - } - else - { - JpegHistoryGraph.Start ( strHistoryFileName ); - } -} - -void CServerLogging::AddNewConnection ( const QHostAddress& ClientInetAddr ) +void CServerLogging::AddNewConnection ( const QHostAddress& ClientInetAddr, + const int iNumberOfConnectedClients ) { // logging of new connected channel const QString strLogStr = CurTimeDatetoLogString() + ", " + - ClientInetAddr.toString() + ", connected"; + ClientInetAddr.toString() + ", connected (" + QString::number ( iNumberOfConnectedClients ) + ")"; QTextStream& tsConsoleStream = *( ( new ConsoleWriterFactory() )->get() ); tsConsoleStream << strLogStr << endl; // on console *this << strLogStr; // in log file - - // add element to history - JpegHistoryGraph.Add ( QDateTime::currentDateTime(), ClientInetAddr ); - SvgHistoryGraph.Add ( QDateTime::currentDateTime(), ClientInetAddr ); } void CServerLogging::AddServerStopped() { - const QString strLogStr = CurTimeDatetoLogString() + ",, server stopped " + const QString strLogStr = CurTimeDatetoLogString() + ",, server idling " "-------------------------------------"; QTextStream& tsConsoleStream = *( ( new ConsoleWriterFactory() )->get() ); tsConsoleStream << strLogStr << endl; // on console *this << strLogStr; // in log file - - // add element to history and update on server stop - JpegHistoryGraph.Add ( QDateTime::currentDateTime(), AHistoryGraph::HIT_SERVER_STOP ); - SvgHistoryGraph.Add ( QDateTime::currentDateTime(), AHistoryGraph::HIT_SERVER_STOP ); - - JpegHistoryGraph.Update(); - SvgHistoryGraph.Update(); } void CServerLogging::operator<< ( const QString& sNewStr ) @@ -100,62 +78,6 @@ void CServerLogging::operator<< ( const QString& sNewStr ) } } -void CServerLogging::ParseLogFile ( const QString& strFileName ) -{ - // open file for reading - QFile LogFile ( strFileName ); - LogFile.open ( QIODevice::ReadOnly | QIODevice::Text ); - - QTextStream inStream ( &LogFile ); - - // read all content from file - while ( !inStream.atEnd() ) - { - // get a new line from log file - QString strCurLine = inStream.readLine(); - - // parse log file line - QStringList strlistCurLine = strCurLine.split ( "," ); - - // check number of separated strings condition - if ( strlistCurLine.size() == 3 ) - { - // first entry - QDateTime curDateTime = - QDateTime::fromString ( strlistCurLine.at ( 0 ).trimmed(), - "yyyy-MM-dd HH:mm:ss" ); - - if ( curDateTime.isValid() ) - { - // check if server stop or new client connection - QString strAddress = strlistCurLine.at ( 1 ).trimmed(); - - if ( strAddress.isEmpty() ) - { - // server stop - JpegHistoryGraph.Add ( curDateTime, AHistoryGraph::HIT_SERVER_STOP ); - SvgHistoryGraph.Add ( curDateTime, CSvgHistoryGraph::HIT_SERVER_STOP ); - } - else - { - QHostAddress curAddress; - - // second entry is IP address - if ( curAddress.setAddress ( strlistCurLine.at ( 1 ).trimmed() ) ) - { - // new client connection - JpegHistoryGraph.Add ( curDateTime, curAddress ); - SvgHistoryGraph.Add ( curDateTime, curAddress ); - } - } - } - } - } - - JpegHistoryGraph.Update(); - SvgHistoryGraph.Update(); -} - QString CServerLogging::CurTimeDatetoLogString() { // time and date to string conversion diff --git a/src/serverlogging.h b/src/serverlogging.h index 5813db278e..749d0e848a 100755 --- a/src/serverlogging.h +++ b/src/serverlogging.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -32,32 +32,27 @@ #include "global.h" #include "util.h" -#include "historygraph.h" /* Classes ********************************************************************/ class CServerLogging { public: - CServerLogging ( const int iMaxDaysHistory ) : - JpegHistoryGraph ( iMaxDaysHistory ), - SvgHistoryGraph ( iMaxDaysHistory ), + CServerLogging() : bDoLogging ( false ), File ( DEFAULT_LOG_FILE_NAME ) {} virtual ~CServerLogging(); void Start ( const QString& strLoggingFileName ); - void EnableHistory ( const QString& strHistoryFileName ); - void AddNewConnection ( const QHostAddress& ClientInetAddr ); void AddServerStopped(); - void ParseLogFile ( const QString& strFileName ); + + void AddNewConnection ( const QHostAddress& ClientInetAddr, + const int iNumberOfConnectedClients ); protected: void operator<< ( const QString& sNewStr ); QString CurTimeDatetoLogString(); - CJpegHistoryGraph JpegHistoryGraph; - CSvgHistoryGraph SvgHistoryGraph; - bool bDoLogging; - QFile File; + bool bDoLogging; + QFile File; }; diff --git a/src/settings.cpp b/src/settings.cpp index b115db77bd..903032d2ce 100755 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -26,824 +26,929 @@ /* Implementation *************************************************************/ -void CSettings::Load() +void CSettings::Load ( const QList CommandLineOptions ) { - int iIdx; - int iValue; - bool bValue; - QDomDocument IniXMLDocument; - // prepare file name for loading initialization data from XML file and read // data from file if possible - QFile file ( strFileName ); + QDomDocument IniXMLDocument; + ReadFromFile ( strFileName, IniXMLDocument ); + + // read the settings from the given XML file + ReadSettingsFromXML ( IniXMLDocument, CommandLineOptions ); +} + +void CSettings::Save() +{ + // create XML document for storing initialization parameters + QDomDocument IniXMLDocument; + + // write the settings in the XML file + WriteSettingsToXML ( IniXMLDocument ); + + // prepare file name for storing initialization data in XML file and store + // XML data in file + WriteToFile ( strFileName, IniXMLDocument ); +} + +void CSettings::ReadFromFile ( const QString& strCurFileName, + QDomDocument& XMLDocument ) +{ + QFile file ( strCurFileName ); + if ( file.open ( QIODevice::ReadOnly ) ) { - QTextStream in ( &file ); - IniXMLDocument.setContent ( in.readAll(), false ); + XMLDocument.setContent ( QTextStream ( &file ).readAll(), false ); + file.close(); + } +} +void CSettings::WriteToFile ( const QString& strCurFileName, + const QDomDocument& XMLDocument ) +{ + QFile file ( strCurFileName ); + + if ( file.open ( QIODevice::WriteOnly ) ) + { + QTextStream ( &file ) << XMLDocument.toString(); file.close(); } +} +void CSettings::SetFileName ( const QString& sNFiName, + const QString& sDefaultFileName ) +{ + // return the file name with complete path, take care if given file name is empty + strFileName = sNFiName; - // Actual settings data --------------------------------------------------- - if ( bIsClient ) + if ( strFileName.isEmpty() ) { - // client: + // we use the Qt default setting file paths for the different OSs by + // utilizing the QSettings class + const QString sConfigDir = QFileInfo ( QSettings ( QSettings::IniFormat, + QSettings::UserScope, + APP_NAME, + APP_NAME ).fileName() ).absolutePath(); - // IP addresses - for ( iIdx = 0; iIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIdx++ ) + // make sure the directory exists + if ( !QFile::exists ( sConfigDir ) ) { - pClient->vstrIPAddress[iIdx] = - GetIniSetting ( IniXMLDocument, "client", - QString ( "ipaddress%1" ).arg ( iIdx ), "" ); + QDir().mkpath ( sConfigDir ); } - // stored fader tags - for ( iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ ) - { - pClient->vecStoredFaderTags[iIdx] = FromBase64ToString ( - GetIniSetting ( IniXMLDocument, "client", - QString ( "storedfadertag%1_base64" ).arg ( iIdx ), "" ) ); - } + // append the actual file name + strFileName = sConfigDir + "/" + sDefaultFileName; + } +} - // stored fader levels - for ( iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ ) - { - if ( GetNumericIniSet ( IniXMLDocument, "client", - QString ( "storedfaderlevel%1" ).arg ( iIdx ), - 0, AUD_MIX_FADER_MAX, iValue ) ) - { - pClient->vecStoredFaderLevels[iIdx] = iValue; - } - } +void CSettings::SetNumericIniSet ( QDomDocument& xmlFile, + const QString& strSection, + const QString& strKey, + const int iValue ) +{ + // convert input parameter which is an integer to string and store + PutIniSetting ( xmlFile, strSection, strKey, QString::number ( iValue ) ); +} - // stored pan values - for ( iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ ) - { - if ( GetNumericIniSet ( IniXMLDocument, "client", - QString ( "storedpanvalue%1" ).arg ( iIdx ), - 0, AUD_MIX_PAN_MAX, iValue ) ) - { - pClient->vecStoredPanValues[iIdx] = iValue; - } - } +bool CSettings::GetNumericIniSet ( const QDomDocument& xmlFile, + const QString& strSection, + const QString& strKey, + const int iRangeStart, + const int iRangeStop, + int& iValue ) +{ + // init return value + bool bReturn = false; - // stored fader solo state - for ( iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ ) - { - if ( GetFlagIniSet ( IniXMLDocument, "client", - QString ( "storedfaderissolo%1" ).arg ( iIdx ), - bValue ) ) - { - pClient->vecStoredFaderIsSolo[iIdx] = bValue; - } - } + const QString strGetIni = GetIniSetting ( xmlFile, strSection, strKey ); - // stored fader muted state - for ( iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ ) - { - if ( GetFlagIniSet ( IniXMLDocument, "client", - QString ( "storedfaderismute%1" ).arg ( iIdx ), - bValue ) ) - { - pClient->vecStoredFaderIsMute[iIdx] = bValue; - } - } + // check if it is a valid parameter + if ( !strGetIni.isEmpty() ) + { + // convert string from init file to integer + iValue = strGetIni.toInt(); - // new client level - if ( GetNumericIniSet ( IniXMLDocument, "client", "newclientlevel", - 0, 100, iValue ) ) + // check range + if ( ( iValue >= iRangeStart ) && ( iValue <= iRangeStop ) ) { - pClient->iNewClientFaderLevel = iValue; + bReturn = true; } + } - // connect dialog show all musicians - if ( GetFlagIniSet ( IniXMLDocument, "client", "connectdlgshowallmusicians", bValue ) ) - { - pClient->bConnectDlgShowAllMusicians = bValue; - } + return bReturn; +} - // name - pClient->ChannelInfo.strName = FromBase64ToString ( - GetIniSetting ( IniXMLDocument, "client", "name_base64" ) ); +void CSettings::SetFlagIniSet ( QDomDocument& xmlFile, + const QString& strSection, + const QString& strKey, + const bool bValue ) +{ + // we encode true -> "1" and false -> "0" + PutIniSetting ( xmlFile, strSection, strKey, bValue ? "1" : "0" ); +} - // instrument - if ( GetNumericIniSet ( IniXMLDocument, "client", "instrument", - 0, CInstPictures::GetNumAvailableInst() - 1, iValue ) ) - { - pClient->ChannelInfo.iInstrument = iValue; - } +bool CSettings::GetFlagIniSet ( const QDomDocument& xmlFile, + const QString& strSection, + const QString& strKey, + bool& bValue ) +{ + // init return value + bool bReturn = false; - // country - if ( GetNumericIniSet ( IniXMLDocument, "client", "country", - 0, static_cast ( QLocale::LastCountry ), iValue ) ) - { - pClient->ChannelInfo.eCountry = static_cast ( iValue ); - } - else - { - // if no country is given, use the one from the operating system - pClient->ChannelInfo.eCountry = QLocale::system().country(); - } + const QString strGetIni = GetIniSetting ( xmlFile, strSection, strKey ); - // city - pClient->ChannelInfo.strCity = FromBase64ToString ( - GetIniSetting ( IniXMLDocument, "client", "city_base64" ) ); + if ( !strGetIni.isEmpty() ) + { + bValue = ( strGetIni.toInt() != 0 ); + bReturn = true; + } - // skill level - if ( GetNumericIniSet ( IniXMLDocument, "client", "skill", - 0, 3 /* SL_PROFESSIONAL */, iValue ) ) - { - pClient->ChannelInfo.eSkillLevel = static_cast ( iValue ); - } + return bReturn; +} - // audio fader - if ( GetNumericIniSet ( IniXMLDocument, "client", "audfad", - AUD_FADER_IN_MIN, AUD_FADER_IN_MAX, iValue ) ) - { - pClient->SetAudioInFader ( iValue ); - } - // reverberation level - if ( GetNumericIniSet ( IniXMLDocument, "client", "revlev", - 0, AUD_REVERB_MAX, iValue ) ) - { - pClient->SetReverbLevel ( iValue ); - } +// Init-file routines using XML *********************************************** +QString CSettings::GetIniSetting ( const QDomDocument& xmlFile, + const QString& sSection, + const QString& sKey, + const QString& sDefaultVal ) +{ + // init return parameter with default value + QString sResult ( sDefaultVal ); - // reverberation channel assignment - if ( GetFlagIniSet ( IniXMLDocument, "client", "reverblchan", bValue ) ) - { - pClient->SetReverbOnLeftChan ( bValue ); - } + // get section + QDomElement xmlSection = xmlFile.firstChildElement ( sSection ); - // sound card selection - // special case with this setting: the sound card initialization depends - // on this setting call, therefore, if no setting file parameter could - // be retrieved, the sound card is initialized with a default setting - // defined here - if ( GetNumericIniSet ( IniXMLDocument, "client", "auddevidx", - 1, MAX_NUMBER_SOUND_CARDS, iValue ) ) - { - pClient->SetSndCrdDev ( iValue ); - } - else - { - // use "INVALID_INDEX" to tell the sound card driver that - // no device selection was done previously - pClient->SetSndCrdDev ( INVALID_INDEX ); - } + if ( !xmlSection.isNull() ) + { + // get key + QDomElement xmlKey = xmlSection.firstChildElement ( sKey ); - // sound card channel mapping settings: make sure these settings are - // set AFTER the sound card device is set, otherwise the settings are - // overwritten by the defaults - // - // sound card left input channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinlch", - 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + if ( !xmlKey.isNull() ) { - pClient->SetSndCrdLeftInputChannel ( iValue ); + // get value + sResult = xmlKey.text(); } + } - // sound card right input channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinrch", - 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) - { - pClient->SetSndCrdRightInputChannel ( iValue ); - } + return sResult; +} - // sound card left output channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutlch", - 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) - { - pClient->SetSndCrdLeftOutputChannel ( iValue ); - } +void CSettings::PutIniSetting ( QDomDocument& xmlFile, + const QString& sSection, + const QString& sKey, + const QString& sValue ) +{ + // check if section is already there, if not then create it + QDomElement xmlSection = xmlFile.firstChildElement ( sSection ); - // sound card right output channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutrch", - 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) - { - pClient->SetSndCrdRightOutputChannel ( iValue ); - } + if ( xmlSection.isNull() ) + { + // create new root element and add to document + xmlSection = xmlFile.createElement ( sSection ); + xmlFile.appendChild ( xmlSection ); + } - // sound card preferred buffer size index - if ( GetNumericIniSet ( IniXMLDocument, "client", "prefsndcrdbufidx", - FRAME_SIZE_FACTOR_PREFERRED, FRAME_SIZE_FACTOR_SAFE, iValue ) ) - { - // additional check required since only a subset of factors are - // defined - if ( ( iValue == FRAME_SIZE_FACTOR_PREFERRED ) || - ( iValue == FRAME_SIZE_FACTOR_DEFAULT ) || - ( iValue == FRAME_SIZE_FACTOR_SAFE ) ) - { - pClient->SetSndCrdPrefFrameSizeFactor ( iValue ); - } - } + // check if key is already there, if not then create it + QDomElement xmlKey = xmlSection.firstChildElement ( sKey ); - // automatic network jitter buffer size setting - if ( GetFlagIniSet ( IniXMLDocument, "client", "autojitbuf", bValue ) ) - { - pClient->SetDoAutoSockBufSize ( bValue ); - } + if ( xmlKey.isNull() ) + { + xmlKey = xmlFile.createElement ( sKey ); + xmlSection.appendChild ( xmlKey ); + } - // network jitter buffer size - if ( GetNumericIniSet ( IniXMLDocument, "client", "jitbuf", - MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL, iValue ) ) - { - pClient->SetSockBufNumFrames ( iValue ); - } + // add actual data to the key + QDomText currentValue = xmlFile.createTextNode ( sValue ); + xmlKey.appendChild ( currentValue ); +} - // network jitter buffer size for server - if ( GetNumericIniSet ( IniXMLDocument, "client", "jitbufserver", - MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL, iValue ) ) - { - pClient->SetServerSockBufNumFrames ( iValue ); - } - // enable OPUS64 setting - if ( GetFlagIniSet ( IniXMLDocument, "client", "enableopussmall", bValue ) ) - { - pClient->SetEnableOPUS64 ( bValue ); - } +// Client settings ------------------------------------------------------------- +void CClientSettings::LoadFaderSettings ( const QString& strCurFileName ) +{ + // prepare file name for loading initialization data from XML file and read + // data from file if possible + QDomDocument IniXMLDocument; + ReadFromFile ( strCurFileName, IniXMLDocument ); - // GUI design - if ( GetNumericIniSet ( IniXMLDocument, "client", "guidesign", - 0, 1 /* GD_ORIGINAL */, iValue ) ) - { - pClient->SetGUIDesign ( static_cast ( iValue ) ); - } + // read the settings from the given XML file + ReadFaderSettingsFromXML ( IniXMLDocument ); +} - // display channel levels preference - if ( GetFlagIniSet ( IniXMLDocument, "client", "displaychannellevels", bValue ) ) - { - pClient->SetDisplayChannelLevels ( bValue ); - } +void CClientSettings::SaveFaderSettings ( const QString& strCurFileName ) +{ + // create XML document for storing initialization parameters + QDomDocument IniXMLDocument; - // audio channels - if ( GetNumericIniSet ( IniXMLDocument, "client", "audiochannels", - 0, 2 /* CC_STEREO */, iValue ) ) - { - pClient->SetAudioChannels ( static_cast ( iValue ) ); - } + // write the settings in the XML file + WriteFaderSettingsToXML ( IniXMLDocument ); - // audio quality - if ( GetNumericIniSet ( IniXMLDocument, "client", "audioquality", - 0, 2 /* AQ_HIGH */, iValue ) ) - { - pClient->SetAudioQuality ( static_cast ( iValue ) ); - } + // prepare file name for storing initialization data in XML file and store + // XML data in file + WriteToFile ( strCurFileName, IniXMLDocument ); +} - // central server address - pClient->SetServerListCentralServerAddress ( - GetIniSetting ( IniXMLDocument, "client", "centralservaddr" ) ); +void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, + const QList& ) +{ + int iIdx; + int iValue; + bool bValue; - // central server address type - if ( GetNumericIniSet ( IniXMLDocument, "client", "centservaddrtype", - 0, static_cast ( AT_CUSTOM ), iValue ) ) - { - pClient->SetCentralServerAddressType ( static_cast ( iValue ) ); - } - else + // IP addresses + for ( iIdx = 0; iIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIdx++ ) + { + vstrIPAddress[iIdx] = + GetIniSetting ( IniXMLDocument, "client", + QString ( "ipaddress%1" ).arg ( iIdx ), "" ); + } + + // new client level + if ( GetNumericIniSet ( IniXMLDocument, "client", "newclientlevel", + 0, 100, iValue ) ) + { + iNewClientFaderLevel = iValue; + } + + // connect dialog show all musicians + if ( GetFlagIniSet ( IniXMLDocument, "client", "connectdlgshowallmusicians", bValue ) ) + { + bConnectDlgShowAllMusicians = bValue; + } + + // language + strLanguage = GetIniSetting ( IniXMLDocument, "client", "language", + CLocale::FindSysLangTransFileName ( CLocale::GetAvailableTranslations() ).first ); + + // fader channel sorting + if ( GetNumericIniSet ( IniXMLDocument, "client", "channelsort", + 0, 4 /* ST_BY_CITY */, iValue ) ) + { + eChannelSortType = static_cast ( iValue ); + } + + // number of mixer panel rows + if ( GetNumericIniSet ( IniXMLDocument, "client", "numrowsmixpan", + 1, 2, iValue ) ) + { + iNumMixerPanelRows = iValue; + } + + // name + pClient->ChannelInfo.strName = FromBase64ToString ( + GetIniSetting ( IniXMLDocument, "client", "name_base64", + ToBase64 ( QCoreApplication::translate ( "CMusProfDlg", "No Name" ) ) ) ); + + // instrument + if ( GetNumericIniSet ( IniXMLDocument, "client", "instrument", + 0, CInstPictures::GetNumAvailableInst() - 1, iValue ) ) + { + pClient->ChannelInfo.iInstrument = iValue; + } + + // country + if ( GetNumericIniSet ( IniXMLDocument, "client", "country", + 0, static_cast ( QLocale::LastCountry ), iValue ) ) + { + pClient->ChannelInfo.eCountry = static_cast ( iValue ); + } + else + { + // if no country is given, use the one from the operating system + pClient->ChannelInfo.eCountry = QLocale::system().country(); + } + + // city + pClient->ChannelInfo.strCity = FromBase64ToString ( + GetIniSetting ( IniXMLDocument, "client", "city_base64" ) ); + + // skill level + if ( GetNumericIniSet ( IniXMLDocument, "client", "skill", + 0, 3 /* SL_PROFESSIONAL */, iValue ) ) + { + pClient->ChannelInfo.eSkillLevel = static_cast ( iValue ); + } + + // audio fader + if ( GetNumericIniSet ( IniXMLDocument, "client", "audfad", + AUD_FADER_IN_MIN, AUD_FADER_IN_MAX, iValue ) ) + { + pClient->SetAudioInFader ( iValue ); + } + + // reverberation level + if ( GetNumericIniSet ( IniXMLDocument, "client", "revlev", + 0, AUD_REVERB_MAX, iValue ) ) + { + pClient->SetReverbLevel ( iValue ); + } + + // reverberation channel assignment + if ( GetFlagIniSet ( IniXMLDocument, "client", "reverblchan", bValue ) ) + { + pClient->SetReverbOnLeftChan ( bValue ); + } + + // sound card selection + const QString strError = pClient->SetSndCrdDev ( FromBase64ToString ( GetIniSetting ( IniXMLDocument, "client", "auddev_base64", "" ) ) ); + + if ( !strError.isEmpty() ) + { +#ifndef HEADLESS + // special case: when settings are loaded no GUI is yet created, therefore + // we have to create a warning message box here directly + QMessageBox::warning ( nullptr, APP_NAME, strError ); +#endif + } + + // sound card channel mapping settings: make sure these settings are + // set AFTER the sound card device is set, otherwise the settings are + // overwritten by the defaults + // + // sound card left input channel mapping + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinlch", + 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + { + pClient->SetSndCrdLeftInputChannel ( iValue ); + } + + // sound card right input channel mapping + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinrch", + 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + { + pClient->SetSndCrdRightInputChannel ( iValue ); + } + + // sound card left output channel mapping + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutlch", + 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + { + pClient->SetSndCrdLeftOutputChannel ( iValue ); + } + + // sound card right output channel mapping + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutrch", + 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + { + pClient->SetSndCrdRightOutputChannel ( iValue ); + } + + // sound card preferred buffer size index + if ( GetNumericIniSet ( IniXMLDocument, "client", "prefsndcrdbufidx", + FRAME_SIZE_FACTOR_PREFERRED, FRAME_SIZE_FACTOR_SAFE, iValue ) ) + { + // additional check required since only a subset of factors are + // defined + if ( ( iValue == FRAME_SIZE_FACTOR_PREFERRED ) || + ( iValue == FRAME_SIZE_FACTOR_DEFAULT ) || + ( iValue == FRAME_SIZE_FACTOR_SAFE ) ) { - // if no address type is given, choose one from the operating system locale - pClient->SetCentralServerAddressType ( CLocale::GetCentralServerAddressType ( QLocale::system().country() ) ); + pClient->SetSndCrdPrefFrameSizeFactor ( iValue ); } + } -// TODO compatibility to old version -if ( GetFlagIniSet ( IniXMLDocument, "client", "defcentservaddr", bValue ) ) -{ - // only the case that manual was set in old ini must be considered - if ( !bValue ) + // automatic network jitter buffer size setting + if ( GetFlagIniSet ( IniXMLDocument, "client", "autojitbuf", bValue ) ) + { + pClient->SetDoAutoSockBufSize ( bValue ); + } + + // network jitter buffer size + if ( GetNumericIniSet ( IniXMLDocument, "client", "jitbuf", + MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL, iValue ) ) { - pClient->SetCentralServerAddressType ( AT_CUSTOM ); + pClient->SetSockBufNumFrames ( iValue ); } -} - // window position of the main window - pClient->vecWindowPosMain = FromBase64ToByteArray ( - GetIniSetting ( IniXMLDocument, "client", "winposmain_base64" ) ); + // network jitter buffer size for server + if ( GetNumericIniSet ( IniXMLDocument, "client", "jitbufserver", + MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL, iValue ) ) + { + pClient->SetServerSockBufNumFrames ( iValue ); + } - // window position of the settings window - pClient->vecWindowPosSettings = FromBase64ToByteArray ( - GetIniSetting ( IniXMLDocument, "client", "winposset_base64" ) ); + // enable OPUS64 setting + if ( GetFlagIniSet ( IniXMLDocument, "client", "enableopussmall", bValue ) ) + { + pClient->SetEnableOPUS64 ( bValue ); + } - // window position of the chat window - pClient->vecWindowPosChat = FromBase64ToByteArray ( - GetIniSetting ( IniXMLDocument, "client", "winposchat_base64" ) ); + // GUI design + if ( GetNumericIniSet ( IniXMLDocument, "client", "guidesign", + 0, 2 /* GD_SLIMFADER */, iValue ) ) + { + pClient->SetGUIDesign ( static_cast ( iValue ) ); + } - // window position of the musician profile window - pClient->vecWindowPosProfile = FromBase64ToByteArray ( - GetIniSetting ( IniXMLDocument, "client", "winposprofile_base64" ) ); + // audio channels + if ( GetNumericIniSet ( IniXMLDocument, "client", "audiochannels", + 0, 2 /* CC_STEREO */, iValue ) ) + { + pClient->SetAudioChannels ( static_cast ( iValue ) ); + } - // window position of the connect window - pClient->vecWindowPosConnect = FromBase64ToByteArray ( - GetIniSetting ( IniXMLDocument, "client", "winposcon_base64" ) ); + // audio quality + if ( GetNumericIniSet ( IniXMLDocument, "client", "audioquality", + 0, 2 /* AQ_HIGH */, iValue ) ) + { + pClient->SetAudioQuality ( static_cast ( iValue ) ); + } - // visibility state of the settings window - if ( GetFlagIniSet ( IniXMLDocument, "client", "winvisset", bValue ) ) - { - pClient->bWindowWasShownSettings = bValue; - } +// TODO compatibility to old version (< 3.6.1) +// NOTE that the strCurAddr and "check for empty" can be removed if compatibility mode is removed +vstrCentralServerAddress[0] = GetIniSetting ( IniXMLDocument, "client", "centralservaddr" ); - // visibility state of the chat window - if ( GetFlagIniSet ( IniXMLDocument, "client", "winvischat", bValue ) ) - { - pClient->bWindowWasShownChat = bValue; - } + // central server addresses + for ( iIdx = 0; iIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIdx++ ) + { + const QString strCurAddr = GetIniSetting ( IniXMLDocument, "client", + QString ( "centralservaddr%1" ).arg ( iIdx ), "" ); - // visibility state of the musician profile window - if ( GetFlagIniSet ( IniXMLDocument, "client", "winvisprofile", bValue ) ) + if ( !strCurAddr.isEmpty() ) { - pClient->bWindowWasShownProfile = bValue; + vstrCentralServerAddress[iIdx] = strCurAddr; } + } - // visibility state of the connect window - if ( GetFlagIniSet ( IniXMLDocument, "client", "winviscon", bValue ) ) - { - pClient->bWindowWasShownConnect = bValue; - } + // central server address type + if ( GetNumericIniSet ( IniXMLDocument, "client", "centservaddrtype", + 0, static_cast ( AT_CUSTOM ), iValue ) ) + { + eCentralServerAddressType = static_cast ( iValue ); } else { - // server: - - // central server address type (note that it is important - // to set this setting prior to the "central server address") - if ( GetNumericIniSet ( IniXMLDocument, "server", "centservaddrtype", - 0, static_cast ( AT_CUSTOM ), iValue ) ) - { - pServer->SetCentralServerAddressType ( static_cast ( iValue ) ); - } - else - { - // if no address type is given, choose one from the operating system locale - pServer->SetCentralServerAddressType ( CLocale::GetCentralServerAddressType ( QLocale::system().country() ) ); - } + // if no address type is given, choose one from the operating system locale + eCentralServerAddressType = AT_DEFAULT; + } -// TODO compatibility to old version -if ( GetFlagIniSet ( IniXMLDocument, "server", "defcentservaddr", bValue ) ) +// TODO compatibility to old version (<3.4.7) +if ( GetFlagIniSet ( IniXMLDocument, "client", "defcentservaddr", bValue ) ) { // only the case that manual was set in old ini must be considered if ( !bValue ) { - pServer->SetCentralServerAddressType ( AT_CUSTOM ); + eCentralServerAddressType = AT_CUSTOM; } } - // central server address (to be set after the "use default central - // server address) - pServer->SetServerListCentralServerAddress ( - GetIniSetting ( IniXMLDocument, "server", "centralservaddr" ) ); + // window position of the main window + vecWindowPosMain = FromBase64ToByteArray ( + GetIniSetting ( IniXMLDocument, "client", "winposmain_base64" ) ); - // server list enabled flag - if ( GetFlagIniSet ( IniXMLDocument, "server", "servlistenabled", bValue ) ) - { - pServer->SetServerListEnabled ( bValue ); - } + // window position of the settings window + vecWindowPosSettings = FromBase64ToByteArray ( + GetIniSetting ( IniXMLDocument, "client", "winposset_base64" ) ); - // name - pServer->SetServerName ( GetIniSetting ( IniXMLDocument, "server", "name" ) ); + // window position of the chat window + vecWindowPosChat = FromBase64ToByteArray ( + GetIniSetting ( IniXMLDocument, "client", "winposchat_base64" ) ); - // city - pServer->SetServerCity ( GetIniSetting ( IniXMLDocument, "server", "city" ) ); + // window position of the musician profile window + vecWindowPosProfile = FromBase64ToByteArray ( + GetIniSetting ( IniXMLDocument, "client", "winposprofile_base64" ) ); - // country - if ( GetNumericIniSet ( IniXMLDocument, "server", "country", - 0, static_cast ( QLocale::LastCountry ), iValue ) ) - { - pServer->SetServerCountry ( static_cast ( iValue ) ); - } + // window position of the connect window + vecWindowPosConnect = FromBase64ToByteArray ( + GetIniSetting ( IniXMLDocument, "client", "winposcon_base64" ) ); - // start minimized on OS start - if ( GetFlagIniSet ( IniXMLDocument, "server", "autostartmin", bValue ) ) - { - pServer->SetAutoRunMinimized ( bValue ); - } + // visibility state of the settings window + if ( GetFlagIniSet ( IniXMLDocument, "client", "winvisset", bValue ) ) + { + bWindowWasShownSettings = bValue; + } - // licence type - if ( GetNumericIniSet ( IniXMLDocument, "server", "licencetype", - 0, 1 /* LT_CREATIVECOMMONS */, iValue ) ) - { - pServer->SetLicenceType ( static_cast ( iValue ) ); - } + // visibility state of the chat window + if ( GetFlagIniSet ( IniXMLDocument, "client", "winvischat", bValue ) ) + { + bWindowWasShownChat = bValue; } -} -void CSettings::Save() -{ - int iIdx; + // visibility state of the musician profile window + if ( GetFlagIniSet ( IniXMLDocument, "client", "winvisprofile", bValue ) ) + { + bWindowWasShownProfile = bValue; + } - // create XML document for storing initialization parameters - QDomDocument IniXMLDocument; + // visibility state of the connect window + if ( GetFlagIniSet ( IniXMLDocument, "client", "winviscon", bValue ) ) + { + bWindowWasShownConnect = bValue; + } + + // fader settings + ReadFaderSettingsFromXML ( IniXMLDocument ); +} +void CClientSettings::ReadFaderSettingsFromXML ( const QDomDocument& IniXMLDocument ) +{ + int iIdx; + int iValue; + bool bValue; - // Actual settings data --------------------------------------------------- - if ( bIsClient ) + for ( iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ ) { - // client: + // stored fader tags + vecStoredFaderTags[iIdx] = FromBase64ToString ( + GetIniSetting ( IniXMLDocument, "client", + QString ( "storedfadertag%1_base64" ).arg ( iIdx ), "" ) ); - // IP addresses - for ( iIdx = 0; iIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIdx++ ) + // stored fader levels + if ( GetNumericIniSet ( IniXMLDocument, "client", + QString ( "storedfaderlevel%1" ).arg ( iIdx ), + 0, AUD_MIX_FADER_MAX, iValue ) ) { - PutIniSetting ( IniXMLDocument, "client", - QString ( "ipaddress%1" ).arg ( iIdx ), - pClient->vstrIPAddress[iIdx] ); + vecStoredFaderLevels[iIdx] = iValue; } - // stored fader tags - for ( iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ ) + // stored pan values + if ( GetNumericIniSet ( IniXMLDocument, "client", + QString ( "storedpanvalue%1" ).arg ( iIdx ), + 0, AUD_MIX_PAN_MAX, iValue ) ) { - PutIniSetting ( IniXMLDocument, "client", - QString ( "storedfadertag%1_base64" ).arg ( iIdx ), - ToBase64 ( pClient->vecStoredFaderTags[iIdx] ) ); + vecStoredPanValues[iIdx] = iValue; } - // stored fader levels - for ( iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ ) + // stored fader solo state + if ( GetFlagIniSet ( IniXMLDocument, "client", + QString ( "storedfaderissolo%1" ).arg ( iIdx ), + bValue ) ) { - SetNumericIniSet ( IniXMLDocument, "client", - QString ( "storedfaderlevel%1" ).arg ( iIdx ), - pClient->vecStoredFaderLevels[iIdx] ); + vecStoredFaderIsSolo[iIdx] = bValue; } - // stored pan values - for ( iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ ) + // stored fader muted state + if ( GetFlagIniSet ( IniXMLDocument, "client", + QString ( "storedfaderismute%1" ).arg ( iIdx ), + bValue ) ) { - SetNumericIniSet ( IniXMLDocument, "client", - QString ( "storedpanvalue%1" ).arg ( iIdx ), - pClient->vecStoredPanValues[iIdx] ); + vecStoredFaderIsMute[iIdx] = bValue; } - // stored fader solo states - for ( iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ ) + // stored fader group ID + if ( GetNumericIniSet ( IniXMLDocument, "client", + QString ( "storedgroupid%1" ).arg ( iIdx ), + INVALID_INDEX, MAX_NUM_FADER_GROUPS - 1, iValue ) ) { - SetFlagIniSet ( IniXMLDocument, "client", - QString ( "storedfaderissolo%1" ).arg ( iIdx ), - pClient->vecStoredFaderIsSolo[iIdx] != 0 ); + vecStoredFaderGroupID[iIdx] = iValue; } + } +} - // stored fader muted states - for ( iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ ) - { - SetFlagIniSet ( IniXMLDocument, "client", - QString ( "storedfaderismute%1" ).arg ( iIdx ), - pClient->vecStoredFaderIsMute[iIdx] != 0 ); - } +void CClientSettings::WriteSettingsToXML ( QDomDocument& IniXMLDocument ) +{ + int iIdx; - // new client level - SetNumericIniSet ( IniXMLDocument, "client", "newclientlevel", - pClient->iNewClientFaderLevel ); + // IP addresses + for ( iIdx = 0; iIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIdx++ ) + { + PutIniSetting ( IniXMLDocument, "client", + QString ( "ipaddress%1" ).arg ( iIdx ), + vstrIPAddress[iIdx] ); + } - // connect dialog show all musicians - SetFlagIniSet ( IniXMLDocument, "client", "connectdlgshowallmusicians", - pClient->bConnectDlgShowAllMusicians ); + // new client level + SetNumericIniSet ( IniXMLDocument, "client", "newclientlevel", + iNewClientFaderLevel ); - // name - PutIniSetting ( IniXMLDocument, "client", "name_base64", - ToBase64 ( pClient->ChannelInfo.strName ) ); + // connect dialog show all musicians + SetFlagIniSet ( IniXMLDocument, "client", "connectdlgshowallmusicians", + bConnectDlgShowAllMusicians ); - // instrument - SetNumericIniSet ( IniXMLDocument, "client", "instrument", - pClient->ChannelInfo.iInstrument ); + // language + PutIniSetting ( IniXMLDocument, "client", "language", + strLanguage ); - // country - SetNumericIniSet ( IniXMLDocument, "client", "country", - static_cast ( pClient->ChannelInfo.eCountry ) ); + // fader channel sorting + SetNumericIniSet ( IniXMLDocument, "client", "channelsort", + static_cast ( eChannelSortType ) ); - // city - PutIniSetting ( IniXMLDocument, "client", "city_base64", - ToBase64 ( pClient->ChannelInfo.strCity ) ); + // number of mixer panel rows + SetNumericIniSet ( IniXMLDocument, "client", "numrowsmixpan", + iNumMixerPanelRows ); - // skill level - SetNumericIniSet ( IniXMLDocument, "client", "skill", - static_cast ( pClient->ChannelInfo.eSkillLevel ) ); + // name + PutIniSetting ( IniXMLDocument, "client", "name_base64", + ToBase64 ( pClient->ChannelInfo.strName ) ); - // audio fader - SetNumericIniSet ( IniXMLDocument, "client", "audfad", - pClient->GetAudioInFader() ); + // instrument + SetNumericIniSet ( IniXMLDocument, "client", "instrument", + pClient->ChannelInfo.iInstrument ); - // reverberation level - SetNumericIniSet ( IniXMLDocument, "client", "revlev", - pClient->GetReverbLevel() ); + // country + SetNumericIniSet ( IniXMLDocument, "client", "country", + static_cast ( pClient->ChannelInfo.eCountry ) ); - // reverberation channel assignment - SetFlagIniSet ( IniXMLDocument, "client", "reverblchan", - pClient->IsReverbOnLeftChan() ); + // city + PutIniSetting ( IniXMLDocument, "client", "city_base64", + ToBase64 ( pClient->ChannelInfo.strCity ) ); - // sound card selection - SetNumericIniSet ( IniXMLDocument, "client", "auddevidx", - pClient->GetSndCrdDev() ); + // skill level + SetNumericIniSet ( IniXMLDocument, "client", "skill", + static_cast ( pClient->ChannelInfo.eSkillLevel ) ); - // sound card left input channel mapping - SetNumericIniSet ( IniXMLDocument, "client", "sndcrdinlch", - pClient->GetSndCrdLeftInputChannel() ); + // audio fader + SetNumericIniSet ( IniXMLDocument, "client", "audfad", + pClient->GetAudioInFader() ); - // sound card right input channel mapping - SetNumericIniSet ( IniXMLDocument, "client", "sndcrdinrch", - pClient->GetSndCrdRightInputChannel() ); + // reverberation level + SetNumericIniSet ( IniXMLDocument, "client", "revlev", + pClient->GetReverbLevel() ); - // sound card left output channel mapping - SetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutlch", - pClient->GetSndCrdLeftOutputChannel() ); + // reverberation channel assignment + SetFlagIniSet ( IniXMLDocument, "client", "reverblchan", + pClient->IsReverbOnLeftChan() ); - // sound card right output channel mapping - SetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutrch", - pClient->GetSndCrdRightOutputChannel() ); + // sound card selection + PutIniSetting ( IniXMLDocument, "client", "auddev_base64", + ToBase64 ( pClient->GetSndCrdDev() ) ); - // sound card preferred buffer size index - SetNumericIniSet ( IniXMLDocument, "client", "prefsndcrdbufidx", - pClient->GetSndCrdPrefFrameSizeFactor() ); + // sound card left input channel mapping + SetNumericIniSet ( IniXMLDocument, "client", "sndcrdinlch", + pClient->GetSndCrdLeftInputChannel() ); - // automatic network jitter buffer size setting - SetFlagIniSet ( IniXMLDocument, "client", "autojitbuf", - pClient->GetDoAutoSockBufSize() ); + // sound card right input channel mapping + SetNumericIniSet ( IniXMLDocument, "client", "sndcrdinrch", + pClient->GetSndCrdRightInputChannel() ); - // network jitter buffer size - SetNumericIniSet ( IniXMLDocument, "client", "jitbuf", - pClient->GetSockBufNumFrames() ); + // sound card left output channel mapping + SetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutlch", + pClient->GetSndCrdLeftOutputChannel() ); - // network jitter buffer size for server - SetNumericIniSet ( IniXMLDocument, "client", "jitbufserver", - pClient->GetServerSockBufNumFrames() ); + // sound card right output channel mapping + SetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutrch", + pClient->GetSndCrdRightOutputChannel() ); - // enable OPUS64 setting - SetFlagIniSet ( IniXMLDocument, "client", "enableopussmall", - pClient->GetEnableOPUS64() ); + // sound card preferred buffer size index + SetNumericIniSet ( IniXMLDocument, "client", "prefsndcrdbufidx", + pClient->GetSndCrdPrefFrameSizeFactor() ); - // GUI design - SetNumericIniSet ( IniXMLDocument, "client", "guidesign", - static_cast ( pClient->GetGUIDesign() ) ); + // automatic network jitter buffer size setting + SetFlagIniSet ( IniXMLDocument, "client", "autojitbuf", + pClient->GetDoAutoSockBufSize() ); - // display channel levels preference - SetFlagIniSet ( IniXMLDocument, "client", "displaychannellevels", - pClient->GetDisplayChannelLevels() ); + // network jitter buffer size + SetNumericIniSet ( IniXMLDocument, "client", "jitbuf", + pClient->GetSockBufNumFrames() ); - // audio channels - SetNumericIniSet ( IniXMLDocument, "client", "audiochannels", - static_cast ( pClient->GetAudioChannels() ) ); + // network jitter buffer size for server + SetNumericIniSet ( IniXMLDocument, "client", "jitbufserver", + pClient->GetServerSockBufNumFrames() ); - // audio quality - SetNumericIniSet ( IniXMLDocument, "client", "audioquality", - static_cast ( pClient->GetAudioQuality() ) ); + // enable OPUS64 setting + SetFlagIniSet ( IniXMLDocument, "client", "enableopussmall", + pClient->GetEnableOPUS64() ); - // central server address - PutIniSetting ( IniXMLDocument, "client", "centralservaddr", - pClient->GetServerListCentralServerAddress() ); + // GUI design + SetNumericIniSet ( IniXMLDocument, "client", "guidesign", + static_cast ( pClient->GetGUIDesign() ) ); - // central server address type - SetNumericIniSet ( IniXMLDocument, "client", "centservaddrtype", - static_cast ( pClient->GetCentralServerAddressType() ) ); + // audio channels + SetNumericIniSet ( IniXMLDocument, "client", "audiochannels", + static_cast ( pClient->GetAudioChannels() ) ); - // window position of the main window - PutIniSetting ( IniXMLDocument, "client", "winposmain_base64", - ToBase64 ( pClient->vecWindowPosMain ) ); + // audio quality + SetNumericIniSet ( IniXMLDocument, "client", "audioquality", + static_cast ( pClient->GetAudioQuality() ) ); - // window position of the settings window - PutIniSetting ( IniXMLDocument, "client", "winposset_base64", - ToBase64 ( pClient->vecWindowPosSettings ) ); + // central server addresses + for ( iIdx = 0; iIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIdx++ ) + { + PutIniSetting ( IniXMLDocument, "client", + QString ( "centralservaddr%1" ).arg ( iIdx ), + vstrCentralServerAddress[iIdx] ); + } - // window position of the chat window - PutIniSetting ( IniXMLDocument, "client", "winposchat_base64", - ToBase64 ( pClient->vecWindowPosChat ) ); + // central server address type + SetNumericIniSet ( IniXMLDocument, "client", "centservaddrtype", + static_cast ( eCentralServerAddressType ) ); - // window position of the musician profile window - PutIniSetting ( IniXMLDocument, "client", "winposprofile_base64", - ToBase64 ( pClient->vecWindowPosProfile ) ); + // window position of the main window + PutIniSetting ( IniXMLDocument, "client", "winposmain_base64", + ToBase64 ( vecWindowPosMain ) ); - // window position of the connect window - PutIniSetting ( IniXMLDocument, "client", "winposcon_base64", - ToBase64 ( pClient->vecWindowPosConnect ) ); + // window position of the settings window + PutIniSetting ( IniXMLDocument, "client", "winposset_base64", + ToBase64 ( vecWindowPosSettings ) ); - // visibility state of the settings window - SetFlagIniSet ( IniXMLDocument, "client", "winvisset", - pClient->bWindowWasShownSettings ); + // window position of the chat window + PutIniSetting ( IniXMLDocument, "client", "winposchat_base64", + ToBase64 ( vecWindowPosChat ) ); - // visibility state of the chat window - SetFlagIniSet ( IniXMLDocument, "client", "winvischat", - pClient->bWindowWasShownChat ); + // window position of the musician profile window + PutIniSetting ( IniXMLDocument, "client", "winposprofile_base64", + ToBase64 ( vecWindowPosProfile ) ); - // visibility state of the musician profile window - SetFlagIniSet ( IniXMLDocument, "client", "winvisprofile", - pClient->bWindowWasShownProfile ); + // window position of the connect window + PutIniSetting ( IniXMLDocument, "client", "winposcon_base64", + ToBase64 ( vecWindowPosConnect ) ); - // visibility state of the connect window - SetFlagIniSet ( IniXMLDocument, "client", "winviscon", - pClient->bWindowWasShownConnect ); - } - else - { - // server: + // visibility state of the settings window + SetFlagIniSet ( IniXMLDocument, "client", "winvisset", + bWindowWasShownSettings ); - // central server address - PutIniSetting ( IniXMLDocument, "server", "centralservaddr", - pServer->GetServerListCentralServerAddress() ); + // visibility state of the chat window + SetFlagIniSet ( IniXMLDocument, "client", "winvischat", + bWindowWasShownChat ); - // central server address type - SetNumericIniSet ( IniXMLDocument, "server", "centservaddrtype", - static_cast ( pServer->GetCentralServerAddressType() ) ); + // visibility state of the musician profile window + SetFlagIniSet ( IniXMLDocument, "client", "winvisprofile", + bWindowWasShownProfile ); - // server list enabled flag - SetFlagIniSet ( IniXMLDocument, "server", "servlistenabled", - pServer->GetServerListEnabled() ); + // visibility state of the connect window + SetFlagIniSet ( IniXMLDocument, "client", "winviscon", + bWindowWasShownConnect ); - // name - PutIniSetting ( IniXMLDocument, "server", "name", - pServer->GetServerName() ); + // fader settings + WriteFaderSettingsToXML ( IniXMLDocument ); +} - // city - PutIniSetting ( IniXMLDocument, "server", "city", - pServer->GetServerCity() ); +void CClientSettings::WriteFaderSettingsToXML ( QDomDocument& IniXMLDocument ) +{ + int iIdx; - // country - SetNumericIniSet ( IniXMLDocument, "server", "country", - static_cast ( pServer->GetServerCountry() ) ); + for ( iIdx = 0; iIdx < MAX_NUM_STORED_FADER_SETTINGS; iIdx++ ) + { + // stored fader tags + PutIniSetting ( IniXMLDocument, "client", + QString ( "storedfadertag%1_base64" ).arg ( iIdx ), + ToBase64 ( vecStoredFaderTags[iIdx] ) ); - // start minimized on OS start - SetFlagIniSet ( IniXMLDocument, "server", "autostartmin", - pServer->GetAutoRunMinimized() ); + // stored fader levels + SetNumericIniSet ( IniXMLDocument, "client", + QString ( "storedfaderlevel%1" ).arg ( iIdx ), + vecStoredFaderLevels[iIdx] ); - // licence type - SetNumericIniSet ( IniXMLDocument, "server", "licencetype", - static_cast ( pServer->GetLicenceType() ) ); - } + // stored pan values + SetNumericIniSet ( IniXMLDocument, "client", + QString ( "storedpanvalue%1" ).arg ( iIdx ), + vecStoredPanValues[iIdx] ); - // prepare file name for storing initialization data in XML file and store - // XML data in file - QFile file ( strFileName ); - if ( file.open ( QIODevice::WriteOnly ) ) - { - QTextStream out ( &file ); - out << IniXMLDocument.toString(); + // stored fader solo states + SetFlagIniSet ( IniXMLDocument, "client", + QString ( "storedfaderissolo%1" ).arg ( iIdx ), + vecStoredFaderIsSolo[iIdx] != 0 ); - file.close(); + // stored fader muted states + SetFlagIniSet ( IniXMLDocument, "client", + QString ( "storedfaderismute%1" ).arg ( iIdx ), + vecStoredFaderIsMute[iIdx] != 0 ); + + // stored fader group ID + SetNumericIniSet ( IniXMLDocument, "client", + QString ( "storedgroupid%1" ).arg ( iIdx ), + vecStoredFaderGroupID[iIdx] ); } } -// Help functions ************************************************************** -void CSettings::SetFileName ( const QString& sNFiName ) +// Server settings ------------------------------------------------------------- +void CServerSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, + const QList& CommandLineOptions ) { - // return the file name with complete path, take care if given file name is - // empty - strFileName = sNFiName; - if ( strFileName.isEmpty() ) - { - // we use the Qt default setting file paths for the different OSs by - // utilizing the QSettings class - const QSettings TempSettingsObject ( - QSettings::IniFormat, QSettings::UserScope, APP_NAME, APP_NAME ); - - const QString sConfigDir = - QFileInfo ( TempSettingsObject.fileName() ).absolutePath(); - - // make sure the directory exists - if ( !QFile::exists ( sConfigDir ) ) - { - QDir TempDirectoryObject; - TempDirectoryObject.mkpath ( sConfigDir ); - } + int iValue; + bool bValue; - // append the actual file name - if ( bIsClient ) - { - strFileName = sConfigDir + "/" + DEFAULT_INI_FILE_NAME; - } - else - { - strFileName = sConfigDir + "/" + DEFAULT_INI_FILE_NAME_SERVER; - } + // central server address type (note that it is important + // to set this setting prior to the "central server address") + if ( GetNumericIniSet ( IniXMLDocument, "server", "centservaddrtype", + 0, static_cast ( AT_CUSTOM ), iValue ) ) + { + pServer->SetCentralServerAddressType ( static_cast ( iValue ) ); + } + else + { + // if no address type is given, choose one from the operating system locale + pServer->SetCentralServerAddressType ( AT_DEFAULT ); } -} -void CSettings::SetNumericIniSet ( QDomDocument& xmlFile, - const QString& strSection, - const QString& strKey, - const int iValue ) +// TODO compatibility to old version +if ( GetFlagIniSet ( IniXMLDocument, "server", "defcentservaddr", bValue ) ) { - // convert input parameter which is an integer to string and store - PutIniSetting ( xmlFile, strSection, strKey, QString("%1").arg(iValue) ); + // only the case that manual was set in old ini must be considered + if ( !bValue ) + { + pServer->SetCentralServerAddressType ( AT_CUSTOM ); + } } -bool CSettings::GetNumericIniSet ( const QDomDocument& xmlFile, - const QString& strSection, - const QString& strKey, - const int iRangeStart, - const int iRangeStop, - int& iValue ) -{ - // init return value - bool bReturn = false; + if ( !CommandLineOptions.contains ( "--centralserver" ) ) + { + // central server address (to be set after the "use default central + // server address) + pServer->SetServerListCentralServerAddress ( + GetIniSetting ( IniXMLDocument, "server", "centralservaddr" ) ); + } - const QString strGetIni = GetIniSetting ( xmlFile, strSection, strKey ); + // server list enabled flag + if ( GetFlagIniSet ( IniXMLDocument, "server", "servlistenabled", bValue ) ) + { + pServer->SetServerListEnabled ( bValue ); + } - // check if it is a valid parameter - if ( !strGetIni.isEmpty() ) + // language + strLanguage = GetIniSetting ( IniXMLDocument, "server", "language", + CLocale::FindSysLangTransFileName ( CLocale::GetAvailableTranslations() ).first ); + + // name/city/country + if ( !CommandLineOptions.contains ( "--serverinfo" ) ) { - // convert string from init file to integer - iValue = strGetIni.toInt(); + // name + pServer->SetServerName ( GetIniSetting ( IniXMLDocument, "server", "name" ) ); - // check range - if ( ( iValue >= iRangeStart ) && ( iValue <= iRangeStop ) ) + // city + pServer->SetServerCity ( GetIniSetting ( IniXMLDocument, "server", "city" ) ); + + // country + if ( GetNumericIniSet ( IniXMLDocument, "server", "country", + 0, static_cast ( QLocale::LastCountry ), iValue ) ) { - bReturn = true; + pServer->SetServerCountry ( static_cast ( iValue ) ); } } - return bReturn; -} + // start minimized on OS start + if ( !CommandLineOptions.contains ( "--startminimized" ) ) + { + if ( GetFlagIniSet ( IniXMLDocument, "server", "autostartmin", bValue ) ) + { + pServer->SetAutoRunMinimized ( bValue ); + } + } -void CSettings::SetFlagIniSet ( QDomDocument& xmlFile, - const QString& strSection, - const QString& strKey, - const bool bValue ) -{ - // we encode true -> "1" and false -> "0" - if ( bValue == true ) + // welcome message + if ( !CommandLineOptions.contains ( "--welcomemessage" ) ) { - PutIniSetting ( xmlFile, strSection, strKey, "1" ); + pServer->SetWelcomeMessage ( FromBase64ToString ( GetIniSetting ( IniXMLDocument, "server", "welcome" ) ) ); } - else + + // window position of the main window + vecWindowPosMain = FromBase64ToByteArray ( + GetIniSetting ( IniXMLDocument, "server", "winposmain_base64" ) ); + + // base recording directory + if ( !CommandLineOptions.contains ( "--recording" ) ) + { + pServer->SetRecordingDir ( FromBase64ToString ( GetIniSetting ( IniXMLDocument, "server", "recordingdir_base64" ) ) ); + } + + // norecord flag + if ( !CommandLineOptions.contains ( "--norecord" ) ) { - PutIniSetting ( xmlFile, strSection, strKey, "0" ); + if ( GetFlagIniSet ( IniXMLDocument, "server", "norecord", bValue ) ) + { + pServer->SetEnableRecording ( !bValue ); + } } } -bool CSettings::GetFlagIniSet ( const QDomDocument& xmlFile, - const QString& strSection, - const QString& strKey, - bool& bValue ) +void CServerSettings::WriteSettingsToXML ( QDomDocument& IniXMLDocument ) { - // init return value - bool bReturn = false; + // central server address + PutIniSetting ( IniXMLDocument, "server", "centralservaddr", + pServer->GetServerListCentralServerAddress() ); - const QString strGetIni = GetIniSetting ( xmlFile, strSection, strKey ); + // central server address type + SetNumericIniSet ( IniXMLDocument, "server", "centservaddrtype", + static_cast ( pServer->GetCentralServerAddressType() ) ); - if ( !strGetIni.isEmpty() ) - { - if ( strGetIni.toInt() ) - { - bValue = true; - } - else - { - bValue = false; - } + // server list enabled flag + SetFlagIniSet ( IniXMLDocument, "server", "servlistenabled", + pServer->GetServerListEnabled() ); - bReturn = true; - } + // language + PutIniSetting ( IniXMLDocument, "server", "language", + strLanguage ); - return bReturn; -} + // name + PutIniSetting ( IniXMLDocument, "server", "name", + pServer->GetServerName() ); + // city + PutIniSetting ( IniXMLDocument, "server", "city", + pServer->GetServerCity() ); -// Init-file routines using XML *********************************************** -QString CSettings::GetIniSetting ( const QDomDocument& xmlFile, - const QString& sSection, - const QString& sKey, - const QString& sDefaultVal ) -{ - // init return parameter with default value - QString sResult ( sDefaultVal ); + // country + SetNumericIniSet ( IniXMLDocument, "server", "country", + static_cast ( pServer->GetServerCountry() ) ); - // get section - QDomElement xmlSection = xmlFile.firstChildElement ( sSection ); - if ( !xmlSection.isNull() ) - { - // get key - QDomElement xmlKey = xmlSection.firstChildElement ( sKey ); - if ( !xmlKey.isNull() ) - { - // get value - sResult = xmlKey.text(); - } - } + // start minimized on OS start + SetFlagIniSet ( IniXMLDocument, "server", "autostartmin", + pServer->GetAutoRunMinimized() ); - return sResult; -} + // welcome message + PutIniSetting ( IniXMLDocument, "server", "welcome", + ToBase64 ( pServer->GetWelcomeMessage() ) ); -void CSettings::PutIniSetting ( QDomDocument& xmlFile, - const QString& sSection, - const QString& sKey, - const QString& sValue ) -{ - // check if section is already there, if not then create it - QDomElement xmlSection = xmlFile.firstChildElement ( sSection ); - if ( xmlSection.isNull() ) - { - // create new root element and add to document - xmlSection = xmlFile.createElement ( sSection ); - xmlFile.appendChild ( xmlSection ); - } + // window position of the main window + PutIniSetting ( IniXMLDocument, "server", "winposmain_base64", + ToBase64 ( vecWindowPosMain ) ); - // check if key is already there, if not then create it - QDomElement xmlKey = xmlSection.firstChildElement ( sKey ); - if ( xmlKey.isNull() ) - { - xmlKey = xmlFile.createElement ( sKey ); - xmlSection.appendChild ( xmlKey ); - } + // base recording directory + PutIniSetting ( IniXMLDocument, "server", "recordingdir_base64", + ToBase64 ( pServer->GetRecordingDir() ) ); - // add actual data to the key - QDomText currentValue = xmlFile.createTextNode ( sValue ); - xmlKey.appendChild ( currentValue ); + // norecord flag + SetFlagIniSet ( IniXMLDocument, "server", "norecord", + pServer->GetDisableRecording() ); } diff --git a/src/settings.h b/src/settings.h index 65ad07a5d4..b5c7774e2f 100755 --- a/src/settings.h +++ b/src/settings.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -29,6 +29,9 @@ #include #include #include +#ifndef HEADLESS +# include +#endif #include "global.h" #include "client.h" #include "server.h" @@ -36,22 +39,40 @@ /* Classes ********************************************************************/ -class CSettings +class CSettings : public QObject { -public: - CSettings ( CClient* pNCliP, const QString& sNFiName ) : - pClient ( pNCliP ), bIsClient ( true ) - { SetFileName ( sNFiName ); } - - CSettings ( CServer* pNSerP, const QString& sNFiName ) : - pServer ( pNSerP ), bIsClient ( false ) - { SetFileName ( sNFiName ); } + Q_OBJECT - void Load(); +public: + CSettings() : + vecWindowPosMain ( ), // empty array + strLanguage ( "" ), + strFileName ( "" ) + { + QObject::connect ( QCoreApplication::instance(), &QCoreApplication::aboutToQuit, + this, &CSettings::OnAboutToQuit ); + } + + void Load ( const QList CommandLineOptions ); void Save(); + // common settings + QByteArray vecWindowPosMain; + QString strLanguage; + protected: - void SetFileName ( const QString& sNFiName ); + virtual void WriteSettingsToXML ( QDomDocument& IniXMLDocument ) = 0; + virtual void ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, + const QList& CommandLineOptions ) = 0; + + void ReadFromFile ( const QString& strCurFileName, + QDomDocument& XMLDocument ); + + void WriteToFile ( const QString& strCurFileName, + const QDomDocument& XMLDocument ); + + void SetFileName ( const QString& sNFiName, + const QString& sDefaultFileName ); // The following functions implement the conversion from the general string // to base64 (which should be used for binary data in XML files). This @@ -105,10 +126,95 @@ class CSettings const QString& sKey, const QString& sValue = "" ); - // pointer to the client/server object which stores the various settings - CClient* pClient; // for client - CServer* pServer; // for server + QString strFileName; + +public slots: + void OnAboutToQuit() { Save(); } +}; + + +class CClientSettings : public CSettings +{ +public: + CClientSettings ( CClient* pNCliP, const QString& sNFiName ) : + CSettings ( ), + vecStoredFaderTags ( MAX_NUM_STORED_FADER_SETTINGS, "" ), + vecStoredFaderLevels ( MAX_NUM_STORED_FADER_SETTINGS, AUD_MIX_FADER_MAX ), + vecStoredPanValues ( MAX_NUM_STORED_FADER_SETTINGS, AUD_MIX_PAN_MAX / 2 ), + vecStoredFaderIsSolo ( MAX_NUM_STORED_FADER_SETTINGS, false ), + vecStoredFaderIsMute ( MAX_NUM_STORED_FADER_SETTINGS, false ), + vecStoredFaderGroupID ( MAX_NUM_STORED_FADER_SETTINGS, INVALID_INDEX ), + vstrIPAddress ( MAX_NUM_SERVER_ADDR_ITEMS, "" ), + iNewClientFaderLevel ( 100 ), + bConnectDlgShowAllMusicians ( true ), + eChannelSortType ( ST_NO_SORT ), + iNumMixerPanelRows ( 1 ), + vstrCentralServerAddress ( MAX_NUM_SERVER_ADDR_ITEMS, "" ), + eCentralServerAddressType ( AT_DEFAULT ), + vecWindowPosSettings ( ), // empty array + vecWindowPosChat ( ), // empty array + vecWindowPosProfile ( ), // empty array + vecWindowPosConnect ( ), // empty array + bWindowWasShownSettings ( false ), + bWindowWasShownChat ( false ), + bWindowWasShownProfile ( false ), + bWindowWasShownConnect ( false ), + pClient ( pNCliP ) + { SetFileName ( sNFiName, DEFAULT_INI_FILE_NAME ); } + + void LoadFaderSettings ( const QString& strCurFileName ); + void SaveFaderSettings ( const QString& strCurFileName ); + + // general settings + CVector vecStoredFaderTags; + CVector vecStoredFaderLevels; + CVector vecStoredPanValues; + CVector vecStoredFaderIsSolo; + CVector vecStoredFaderIsMute; + CVector vecStoredFaderGroupID; + CVector vstrIPAddress; + int iNewClientFaderLevel; + bool bConnectDlgShowAllMusicians; + EChSortType eChannelSortType; + int iNumMixerPanelRows; + CVector vstrCentralServerAddress; + ECSAddType eCentralServerAddressType; + + // window position/state settings + QByteArray vecWindowPosSettings; + QByteArray vecWindowPosChat; + QByteArray vecWindowPosProfile; + QByteArray vecWindowPosConnect; + bool bWindowWasShownSettings; + bool bWindowWasShownChat; + bool bWindowWasShownProfile; + bool bWindowWasShownConnect; + +protected: + // No CommandLineOptions used when reading Client inifile + virtual void WriteSettingsToXML ( QDomDocument& IniXMLDocument ) override; + virtual void ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, + const QList& ) override; + + void ReadFaderSettingsFromXML ( const QDomDocument& IniXMLDocument ); + void WriteFaderSettingsToXML ( QDomDocument& IniXMLDocument ); + + CClient* pClient; +}; + + +class CServerSettings : public CSettings +{ +public: + CServerSettings ( CServer* pNSerP, const QString& sNFiName ) : + CSettings ( ), + pServer ( pNSerP ) + { SetFileName ( sNFiName, DEFAULT_INI_FILE_NAME_SERVER); } + +protected: + virtual void WriteSettingsToXML ( QDomDocument& IniXMLDocument ) override; + virtual void ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, + const QList& CommandLineOptions ) override; - bool bIsClient; - QString strFileName; + CServer* pServer; }; diff --git a/src/signalhandler.cpp b/src/signalhandler.cpp index d0205c9e27..bf2735457a 100755 --- a/src/signalhandler.cpp +++ b/src/signalhandler.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * ****************************************************************************** * @@ -146,6 +146,7 @@ CSignalUnix::CSignalUnix ( CSignalHandler* nPSignalHandler ) : socketNotifier->setEnabled ( true ); setSignalHandled ( SIGUSR1, true ); + setSignalHandled ( SIGUSR2, true ); setSignalHandled ( SIGINT, true ); setSignalHandled ( SIGTERM, true ); } @@ -153,6 +154,7 @@ CSignalUnix::CSignalUnix ( CSignalHandler* nPSignalHandler ) : CSignalUnix::~CSignalUnix() { setSignalHandled ( SIGUSR1, false ); + setSignalHandled ( SIGUSR2, false ); setSignalHandled ( SIGINT, false ); setSignalHandled ( SIGTERM, false ); } @@ -167,11 +169,12 @@ bool CSignalUnix::setSignalHandled ( int sigNum, bool state ) if ( state ) { sa.sa_handler = CSignalUnix::signalHandler; - sa.sa_flags |= SA_RESTART; + sa.sa_flags = SA_RESTART; } else { sa.sa_handler = SIG_DFL; + sa.sa_flags = 0; } return ::sigaction ( sigNum, &sa, nullptr ) == 0; diff --git a/src/signalhandler.h b/src/signalhandler.h index 59409d135b..f214e63166 100755 --- a/src/signalhandler.h +++ b/src/signalhandler.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * ****************************************************************************** * diff --git a/src/socket.cpp b/src/socket.cpp index ebcddb5d5a..2aa7643800 100755 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -65,18 +65,23 @@ void CSocket::Init ( const quint16 iPortNumber ) } else { - // if the port is not available, try "NUM_SOCKET_PORTS_TO_TRY" times - // with incremented port numbers + // If the port is not available, try "NUM_SOCKET_PORTS_TO_TRY" times + // with incremented port numbers. Randomize the start port, in case a + // faulty router gets stuck and confused by a particular port (like + // the starting port). Might work around frustrating "cannot connect" + // problems (#568) + const quint16 startingPortNumber = iPortNumber + rand() % NUM_SOCKET_PORTS_TO_TRY; + quint16 iClientPortIncrement = 0; bSuccess = false; // initialization for while loop while ( !bSuccess && ( iClientPortIncrement <= NUM_SOCKET_PORTS_TO_TRY ) ) { - UdpSocketInAddr.sin_port = htons ( iPortNumber + iClientPortIncrement ); + UdpSocketInAddr.sin_port = htons ( startingPortNumber + iClientPortIncrement ); bSuccess = ( ::bind ( UdpSocket , - (sockaddr*) &UdpSocketInAddr, - sizeof ( sockaddr_in ) ) == 0 ); + (sockaddr*) &UdpSocketInAddr, + sizeof ( sockaddr_in ) ) == 0 ); iClientPortIncrement++; } @@ -111,37 +116,30 @@ void CSocket::Init ( const quint16 iPortNumber ) { // client connections: - QObject::connect ( this, - SIGNAL ( ProtcolMessageReceived ( int, int, CVector, CHostAddress ) ), - pChannel, SLOT ( OnProtcolMessageReceived ( int, int, CVector, CHostAddress ) ) ); + QObject::connect ( this, &CSocket::ProtcolMessageReceived, + pChannel, &CChannel::OnProtcolMessageReceived ); - QObject::connect ( this, - SIGNAL ( ProtcolCLMessageReceived ( int, CVector, CHostAddress ) ), - pChannel, SLOT ( OnProtcolCLMessageReceived ( int, CVector, CHostAddress ) ) ); + QObject::connect ( this, &CSocket::ProtcolCLMessageReceived, + pChannel, &CChannel::OnProtcolCLMessageReceived ); - QObject::connect ( this, - SIGNAL ( NewConnection() ), - pChannel, SLOT ( OnNewConnection() ) ); + QObject::connect ( this, static_cast ( &CSocket::NewConnection ), + pChannel, &CChannel::OnNewConnection ); } else { // server connections: - QObject::connect ( this, - SIGNAL ( ProtcolMessageReceived ( int, int, CVector, CHostAddress ) ), - pServer, SLOT ( OnProtcolMessageReceived ( int, int, CVector, CHostAddress ) ) ); + QObject::connect ( this, &CSocket::ProtcolMessageReceived, + pServer, &CServer::OnProtcolMessageReceived ); - QObject::connect ( this, - SIGNAL ( ProtcolCLMessageReceived ( int, CVector, CHostAddress ) ), - pServer, SLOT ( OnProtcolCLMessageReceived ( int, CVector, CHostAddress ) ) ); + QObject::connect ( this, &CSocket::ProtcolCLMessageReceived, + pServer, &CServer::OnProtcolCLMessageReceived ); - QObject::connect ( this, - SIGNAL ( NewConnection ( int, CHostAddress ) ), - pServer, SLOT ( OnNewConnection ( int, CHostAddress ) ) ); + QObject::connect ( this, static_cast ( &CSocket::NewConnection ), + pServer, &CServer::OnNewConnection ); - QObject::connect ( this, - SIGNAL ( ServerFull ( CHostAddress ) ), - pServer, SLOT ( OnServerFull ( CHostAddress ) ) ); + QObject::connect ( this, &CSocket::ServerFull, + pServer, &CServer::OnServerFull ); } } diff --git a/src/socket.h b/src/socket.h index 864b82c5f2..ba07613c33 100755 --- a/src/socket.h +++ b/src/socket.h @@ -18,14 +18,13 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ #pragma once #include -#include #include #include #include @@ -47,7 +46,7 @@ class CChannel; // forward declaration of CChannel /* Definitions ****************************************************************/ // number of ports we try to bind until we give up -#define NUM_SOCKET_PORTS_TO_TRY 50 +#define NUM_SOCKET_PORTS_TO_TRY 100 /* Classes ********************************************************************/ @@ -100,7 +99,7 @@ class CSocket : public QObject bool bJitterBufferOK; -public slots: +public: void OnDataReceived(); signals: @@ -170,7 +169,7 @@ class CHighPrioSocket : public QObject { public: CSocketThread ( CSocket* pNewSocket = nullptr, QObject* parent = nullptr ) : - QThread ( parent ), pSocket ( pNewSocket ), bRun ( true ) {} + QThread ( parent ), pSocket ( pNewSocket ), bRun ( true ) { setObjectName ( "CSocketThread" ); } void Stop() { @@ -217,9 +216,8 @@ class CHighPrioSocket : public QObject NetworkWorkerThread.SetSocket ( &Socket ); // connect the "InvalidPacketReceived" signal - QObject::connect ( &Socket, - SIGNAL ( InvalidPacketReceived ( CHostAddress ) ), - SIGNAL ( InvalidPacketReceived ( CHostAddress ) ) ); + QObject::connect ( &Socket, &CSocket::InvalidPacketReceived, + this, &CHighPrioSocket::InvalidPacketReceived ); } CSocketThread NetworkWorkerThread; diff --git a/src/soundbase.cpp b/src/soundbase.cpp index daadacff08..d82da17f84 100755 --- a/src/soundbase.cpp +++ b/src/soundbase.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -27,47 +27,26 @@ /* Implementation *************************************************************/ CSoundBase::CSoundBase ( const QString& strNewSystemDriverTechniqueName, - const bool bNewIsCallbackAudioInterface, void (*fpNewProcessCallback) ( CVector& psData, void* pParg ), void* pParg, - const int iNewCtrlMIDIChannel ) : + const QString& strMIDISetup ) : fpProcessCallback ( fpNewProcessCallback ), - pProcessCallbackArg ( pParg ), bRun ( false ), - bIsCallbackAudioInterface ( bNewIsCallbackAudioInterface ), + pProcessCallbackArg ( pParg ), + bRun ( false ), + bCallbackEntered ( false ), strSystemDriverTechniqueName ( strNewSystemDriverTechniqueName ), - iCtrlMIDIChannel ( iNewCtrlMIDIChannel ) + iCtrlMIDIChannel ( INVALID_MIDI_CH ), + iMIDIOffsetFader ( 70 ) // Behringer X-TOUCH: offset of 0x46 { + // parse the MIDI setup command line argument string + ParseCommandLineArgument ( strMIDISetup ); + // initializations for the sound card names (default) lNumDevs = 1; strDriverNames[0] = strSystemDriverTechniqueName; // set current device - lCurDev = 0; // default device -} - -int CSoundBase::Init ( const int iNewPrefMonoBufferSize ) -{ - // init audio sound card buffer - if ( !bIsCallbackAudioInterface ) - { - vecsAudioSndCrdStereo.Init ( 2 * iNewPrefMonoBufferSize /* stereo */ ); - } - - return iNewPrefMonoBufferSize; -} - -void CSoundBase::Start() -{ - bRun = true; - -// TODO start audio interface - - // start the audio thread in case we do not have an callback - // based audio interface - if ( !bIsCallbackAudioInterface ) - { - start(); - } + strCurDevName = ""; // default device } void CSoundBase::Stop() @@ -75,97 +54,80 @@ void CSoundBase::Stop() // set flag so that thread can leave the main loop bRun = false; - // give thread some time to terminate - if ( !bIsCallbackAudioInterface ) - { - wait ( 5000 ); - } + // wait for draining the audio process callback + QMutexLocker locker ( &MutexAudioProcessCallback ); } -void CSoundBase::run() + +/******************************************************************************\ +* Device handling * +\******************************************************************************/ +QStringList CSoundBase::GetDevNames() { - // main loop of working thread - while ( bRun ) - { - // get audio from sound card (blocking function) - Read ( vecsAudioSndCrdStereo ); + QMutexLocker locker ( &MutexDevProperties ); - // process audio data - (*fpProcessCallback) ( vecsAudioSndCrdStereo, pProcessCallbackArg ); + QStringList slDevNames; - // play the new block - Write ( vecsAudioSndCrdStereo ); + // put all device names in the string list + for ( int iDev = 0; iDev < lNumDevs; iDev++ ) + { + slDevNames << strDriverNames[iDev]; } -} + return slDevNames; +} -/******************************************************************************\ -* Device handling * -\******************************************************************************/ -QString CSoundBase::SetDev ( const int iNewDev ) +QString CSoundBase::SetDev ( const QString strDevName ) { + QMutexLocker locker ( &MutexDevProperties ); + // init return parameter with "no error" QString strReturn = ""; - // first check if valid input parameter - if ( iNewDev >= lNumDevs ) - { - // we should actually never get here... - return tr ( "Invalid device selection." ); - } + // init flag for "load any driver" + bool bTryLoadAnyDriver = false; // check if an ASIO driver was already initialized - if ( lCurDev != INVALID_INDEX ) + if ( !strCurDevName.isEmpty() ) { // a device was already been initialized and is used, first clean up // driver UnloadCurrentDriver(); - const QString strErrorMessage = LoadAndInitializeDriver ( iNewDev, false ); + const QString strErrorMessage = LoadAndInitializeDriver ( strDevName, false ); if ( !strErrorMessage.isEmpty() ) { - if ( iNewDev != lCurDev ) + if ( strDevName != strCurDevName ) { // loading and initializing the new driver failed, go back to - // original driver and display error message - LoadAndInitializeDriver ( lCurDev, false ); + // original driver and create error message + LoadAndInitializeDriver ( strCurDevName, false ); + + // store error return message + strReturn = QString ( tr ( "The selected audio device could not be used " + "because of the following error: " ) ) + strErrorMessage + + QString ( tr ( " The previous driver will be selected." ) ); } else { - // the same driver is used but the driver properties seems to - // have changed so that they are not compatible to our - // software anymore - QMessageBox::critical ( - nullptr, APP_NAME, QString ( tr ( "The audio driver properties " - "have changed to a state which is incompatible with this " - "software. The selected audio device could not be used " - "because of the following error:" ) + " " ) + - strErrorMessage + - QString ( "

" + tr ( "Please restart the software." ) ), - tr ( "Close" ), nullptr ); - - _exit ( 0 ); + // loading and initializing the current driver failed, try to find + // at least one usable driver + bTryLoadAnyDriver = true; } - - // store error return message - strReturn = strErrorMessage; } } else { - // init flag for "load any driver" - bool bTryLoadAnyDriver = false; - - if ( iNewDev != INVALID_INDEX ) + if ( !strDevName.isEmpty() ) { // This is the first time a driver is to be initialized, we first // try to load the selected driver, if this fails, we try to load // the first available driver in the system. If this fails, too, we // throw an error that no driver is available -> it does not make // sense to start the software if no audio hardware is available. - if ( !LoadAndInitializeDriver ( iNewDev, false ).isEmpty() ) + if ( !LoadAndInitializeDriver ( strDevName, false ).isEmpty() ) { // loading and initializing the new driver failed, try to find // at least one usable driver @@ -177,43 +139,53 @@ QString CSoundBase::SetDev ( const int iNewDev ) // try to find one usable driver (select the first valid driver) bTryLoadAnyDriver = true; } + } - if ( bTryLoadAnyDriver ) + if ( bTryLoadAnyDriver ) + { + // if a driver was previously selected, show a warning message + if ( !strDevName.isEmpty() ) { - // try to load and initialize any valid driver - QVector vsErrorList = LoadAndInitializeFirstValidDriver(); + strReturn = tr ( "The previously selected audio device " + "is no longer available or the audio driver properties have changed to a state which " + "is incompatible with this software. We now try to find a valid audio device. This new " + "audio device might cause audio feedback. So, before connecting to a server, please " + "check the audio device setting." ); + } - if ( !vsErrorList.isEmpty() ) + // try to load and initialize any valid driver + QVector vsErrorList = LoadAndInitializeFirstValidDriver(); + + if ( !vsErrorList.isEmpty() ) + { + // create error message with all details + QString sErrorMessage = "" + tr ( "No usable " ) + + strSystemDriverTechniqueName + tr ( " audio device " + "(driver) found." ) + "

" + tr ( + "In the following there is a list of all available drivers " + "with the associated error message:" ) + "
    "; + + for ( int i = 0; i < lNumDevs; i++ ) { - // create error message with all details - QString sErrorMessage = "" + tr ( "No usable " ) + - strSystemDriverTechniqueName + tr ( " audio device " - "(driver) found." ) + "

    " + tr ( - "In the following there is a list of all available drivers " - "with the associated error message:" ) + "
      "; - - for ( int i = 0; i < lNumDevs; i++ ) - { - sErrorMessage += "
    • " + GetDeviceName ( i ) + ": " + vsErrorList[i] + "
    • "; - } - sErrorMessage += "
    "; + sErrorMessage += "
  • " + GetDeviceName ( i ) + ": " + vsErrorList[i] + "
  • "; + } + sErrorMessage += "
"; #ifdef _WIN32 - // 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 ( "Do you want to open the ASIO driver setups?" ); + // 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 ( "Do you want to open the ASIO driver setups?" ); - if ( QMessageBox::Yes == QMessageBox::information ( nullptr, APP_NAME, sErrorMessage, QMessageBox::Yes|QMessageBox::No ) ) - { - LoadAndInitializeFirstValidDriver ( true ); - } + if ( QMessageBox::Yes == QMessageBox::information ( nullptr, APP_NAME, sErrorMessage, QMessageBox::Yes|QMessageBox::No ) ) + { + LoadAndInitializeFirstValidDriver ( true ); + } - sErrorMessage = APP_NAME + tr ( " could not be started because of audio interface issues." ); + sErrorMessage = APP_NAME + tr ( " could not be started because of audio interface issues." ); #endif - throw CGenErr ( sErrorMessage ); - } + throw CGenErr ( sErrorMessage ); } } @@ -226,13 +198,13 @@ QVector CSoundBase::LoadAndInitializeFirstValidDriver ( const bool bOpe // load and initialize first valid ASIO driver bool bValidDriverDetected = false; - int iCurDriverIdx = 0; + int iDriverCnt = 0; // try all available drivers in the system ("lNumDevs" devices) - while ( !bValidDriverDetected && ( iCurDriverIdx < lNumDevs ) ) + while ( !bValidDriverDetected && ( iDriverCnt < lNumDevs ) ) { // try to load and initialize current driver, store error message - const QString strCurError = LoadAndInitializeDriver ( iCurDriverIdx, bOpenDriverSetup ); + const QString strCurError = LoadAndInitializeDriver ( GetDeviceName ( iDriverCnt ), bOpenDriverSetup ); vsErrorList.append ( strCurError ); @@ -242,14 +214,14 @@ QVector CSoundBase::LoadAndInitializeFirstValidDriver ( const bool bOpe bValidDriverDetected = true; // store ID of selected driver - lCurDev = iCurDriverIdx; + strCurDevName = GetDeviceName ( iDriverCnt ); // empty error list shows that init was successful vsErrorList.clear(); } // try next driver - iCurDriverIdx++; + iDriverCnt++; } return vsErrorList; @@ -260,6 +232,29 @@ QVector CSoundBase::LoadAndInitializeFirstValidDriver ( const bool bOpe /******************************************************************************\ * MIDI handling * \******************************************************************************/ +void CSoundBase::ParseCommandLineArgument ( const QString& strMIDISetup ) +{ + // parse the server info string according to definition: + // [MIDI channel];[offset for level] + if ( !strMIDISetup.isEmpty() ) + { + // split the different parameter strings + const QStringList slMIDIParams = strMIDISetup.split ( ";" ); + + // [MIDI channel] + if ( slMIDIParams.count() >= 1 ) + { + iCtrlMIDIChannel = slMIDIParams[0].toUInt(); + } + + // [offset for level] + if ( slMIDIParams.count() >= 2 ) + { + iMIDIOffsetFader = slMIDIParams[1].toUInt(); + } + } +} + void CSoundBase::ParseMIDIMessage ( const CVector& vMIDIPaketBytes ) { if ( vMIDIPaketBytes.Size() > 0 ) @@ -289,7 +284,7 @@ printf ( "\n" ); // we only want to parse controller messages if ( ( iStatusByte >= 0xB0 ) && ( iStatusByte < 0xC0 ) ) { - // make sure paket is long enough + // make sure packet is long enough if ( vMIDIPaketBytes.Size() > 2 ) { // we are assuming that the controller number is the same @@ -297,10 +292,10 @@ printf ( "\n" ); const int iFaderLevel = static_cast ( static_cast ( qMin ( vMIDIPaketBytes[2], uint8_t ( 127 ) ) ) / 127 * AUD_MIX_FADER_MAX ); - // Behringer X-TOUCH: offset of 0x46 - const int iChID = vMIDIPaketBytes[1] - 70; + // consider offset for the faders + const int iChID = vMIDIPaketBytes[1] - iMIDIOffsetFader; - EmitControllerInFaderLevel ( iChID, iFaderLevel ); + emit ControllerInFaderLevel ( iChID, iFaderLevel ); } } } diff --git a/src/soundbase.h b/src/soundbase.h index 32b62b5e92..f9b4fc30cf 100755 --- a/src/soundbase.h +++ b/src/soundbase.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -26,7 +26,10 @@ #include #include -#include +#include +#ifndef HEADLESS +# include +#endif #include "global.h" #include "util.h" @@ -48,20 +51,18 @@ class CSoundBase : public QThread public: CSoundBase ( const QString& strNewSystemDriverTechniqueName, - const bool bNewIsCallbackAudioInterface, void (*fpNewProcessCallback) ( CVector& psData, void* pParg ), void* pParg, - const int iNewCtrlMIDIChannel ); + const QString& strMIDISetup ); - virtual int Init ( const int iNewPrefMonoBufferSize ); - virtual void Start(); + virtual int Init ( const int iNewPrefMonoBufferSize ) { return iNewPrefMonoBufferSize; } + virtual void Start() { bRun = true; bCallbackEntered = false; } virtual void Stop(); // device selection - virtual int GetNumDev() { return lNumDevs; } - virtual QString GetDeviceName ( const int iDiD ) { return strDriverNames[iDiD]; } - virtual QString SetDev ( const int ); - virtual int GetDev() { return lCurDev; } + QStringList GetDevNames(); + QString SetDev ( const QString strDevName ); + QString GetDev() { QMutexLocker locker ( &MutexDevProperties ); return strCurDevName; } virtual int GetNumInputChannels() { return 2; } virtual QString GetInputChannelName ( const int ) { return "Default"; } @@ -77,25 +78,24 @@ class CSoundBase : public QThread virtual int GetLeftOutputChannel() { return 0; } virtual int GetRightOutputChannel() { return 1; } - virtual double GetInOutLatencyMs() { return 0.0; } // "0.0" means no latency is available + virtual float GetInOutLatencyMs() { return 0.0f; } // "0.0" means no latency is available virtual void OpenDriverSetup() {} bool IsRunning() const { return bRun; } + bool IsCallbackEntered() const { return bCallbackEntered; } // TODO this should be protected but since it is used // in a callback function it has to be public -> better solution void EmitReinitRequestSignal ( const ESndCrdResetType eSndCrdResetType ) { emit ReinitRequest ( eSndCrdResetType ); } - void EmitControllerInFaderLevel ( const int iChannelIdx, - const int iValue ) { emit ControllerInFaderLevel ( iChannelIdx, iValue ); } - protected: - // driver handling - virtual QString LoadAndInitializeDriver ( int, bool ) { return ""; } + virtual QString LoadAndInitializeDriver ( QString, bool ) { return ""; } virtual void UnloadCurrentDriver() {} QVector LoadAndInitializeFirstValidDriver ( const bool bOpenDriverSetup = false ); + void ParseCommandLineArgument ( const QString& strMIDISetup ); + QString GetDeviceName ( const int iDiD ) { return strDriverNames[iDiD]; } static void GetSelCHAndAddCH ( const int iSelCH, const int iNumInChan, int& iSelCHOut, int& iSelAddCHOut ) @@ -125,28 +125,24 @@ class CSoundBase : public QThread // callback function call for derived classes void ProcessCallback ( CVector& psData ) { + bCallbackEntered = true; (*fpProcessCallback) ( psData, pProcessCallbackArg ); } - // these functions should be overwritten by derived class for - // non callback based audio interfaces - virtual bool Read ( CVector& ) { printf ( "no sound!" ); return false; } - virtual bool Write ( CVector& ) { printf ( "no sound!" ); return false; } - - void run(); - bool bRun; - - void ParseMIDIMessage ( const CVector& vMIDIPaketBytes ); + void ParseMIDIMessage ( const CVector& vMIDIPaketBytes ); - bool bIsCallbackAudioInterface; - QString strSystemDriverTechniqueName; - int iCtrlMIDIChannel; + bool bRun; + bool bCallbackEntered; + QMutex MutexAudioProcessCallback; + QMutex MutexDevProperties; - CVector vecsAudioSndCrdStereo; + QString strSystemDriverTechniqueName; + int iCtrlMIDIChannel; + int iMIDIOffsetFader; - long lNumDevs; - long lCurDev; - QString strDriverNames[MAX_NUMBER_SOUND_CARDS]; + long lNumDevs; + QString strCurDevName; + QString strDriverNames[MAX_NUMBER_SOUND_CARDS]; signals: void ReinitRequest ( int iSndCrdResetType ); diff --git a/src/testbench.h b/src/testbench.h index 0fb35fbd1f..a70bdca299 100755 --- a/src/testbench.h +++ b/src/testbench.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -46,10 +46,10 @@ class CTestbench : public QObject iPort ( iNewPort ) { sLAddress = GenRandomIPv4Address().toString(); - iLPort = static_cast ( GenRandomIntInRange ( -2, 10000 ) ); + iLPort = static_cast ( GenRandomIntInRange ( -2, 10000 ) ); // bind socket (try 100 port numbers) - quint16 iPortIncrement = 0; // start value: port nubmer plus ten + quint16 iPortIncrement = 0; // start value: port number plus ten bool bSuccess = false; // initialization for while loop while ( !bSuccess && ( iPortIncrement <= 100 ) ) @@ -60,13 +60,16 @@ class CTestbench : public QObject iPortIncrement++; } - // connect protocol signal - QObject::connect ( &Protocol, SIGNAL ( MessReadyForSending ( CVector ) ), - this, SLOT ( OnSendProtMessage ( CVector ) ) ); + // connect protocol signals + QObject::connect ( &Protocol, &CProtocol::MessReadyForSending, + this, &CTestbench::OnSendProtMessage ); + + QObject::connect ( &Protocol, &CProtocol::CLMessReadyForSending, + this, &CTestbench::OnSendCLMessage ); // connect and start the timer (testbench heartbeat) - QObject::connect ( &Timer, SIGNAL ( timeout() ), - this, SLOT ( OnTimer() ) ); + QObject::connect ( &Timer, &QTimer::timeout, + this, &CTestbench::OnTimer ); Timer.start ( 1 ); // 1 ms } @@ -115,13 +118,15 @@ public slots: CNetworkTransportProps NetTrProps; CServerCoreInfo ServerInfo; CVector vecServerInfo ( 1 ); + CVector vecLevelList ( 1 ); CHostAddress CurHostAddress ( QHostAddress ( sAddress ), iPort ); CHostAddress CurLocalAddress ( QHostAddress ( sLAddress ), iLPort ); CChannelCoreInfo ChannelCoreInfo; ELicenceType eLicenceType; + ESvrRegResult eSvrRegResult; // generate random protocol message - switch ( GenRandomIntInRange ( 0, 27 ) ) + switch ( GenRandomIntInRange ( 0, 34 ) ) { case 0: // PROTMESSID_JITT_BUF_SIZE Protocol.CreateJitBufMes ( GenRandomIntInRange ( 0, 10 ) ); @@ -149,12 +154,8 @@ public slots: break; case 7: // PROTMESSID_CHANNEL_INFOS - ChannelCoreInfo.eCountry = - static_cast ( GenRandomIntInRange ( 0, 100 ) ); - - ChannelCoreInfo.eSkillLevel = - static_cast ( GenRandomIntInRange ( 0, 3 ) ); - + ChannelCoreInfo.eCountry = static_cast ( GenRandomIntInRange ( 0, 100 ) ); + ChannelCoreInfo.eSkillLevel = static_cast ( GenRandomIntInRange ( 0, 3 ) ); ChannelCoreInfo.iInstrument = GenRandomIntInRange ( 0, 100 ); ChannelCoreInfo.strCity = GenRandomString(); ChannelCoreInfo.strName = GenRandomString(); @@ -178,15 +179,13 @@ public slots: break; case 11: // PROTMESSID_NETW_TRANSPORT_PROPS - NetTrProps.eAudioCodingType = - static_cast ( GenRandomIntInRange ( 0, 2 ) ); - + NetTrProps.eAudioCodingType = static_cast ( GenRandomIntInRange ( 0, 2 ) ); NetTrProps.iAudioCodingArg = GenRandomIntInRange ( -100, 100 ); NetTrProps.iBaseNetworkPacketSize = GenRandomIntInRange ( -2, 1000 ); NetTrProps.iBlockSizeFact = GenRandomIntInRange ( -2, 100 ); NetTrProps.iNumAudioChannels = GenRandomIntInRange ( -2, 10 ); NetTrProps.iSampleRate = GenRandomIntInRange ( -2, 10000 ); - NetTrProps.iVersion = GenRandomIntInRange ( -2, 10000 ); + NetTrProps.eFlags = static_cast ( GenRandomIntInRange ( 0, 1 ) ); Protocol.CreateNetwTranspPropsMes ( NetTrProps ); break; @@ -211,12 +210,8 @@ public slots: break; case 17: // PROTMESSID_CLM_REGISTER_SERVER - ServerInfo.bPermanentOnline = - static_cast ( GenRandomIntInRange ( 0, 1 ) ); - - ServerInfo.eCountry = - static_cast ( GenRandomIntInRange ( 0, 100 ) ); - + ServerInfo.bPermanentOnline = static_cast ( GenRandomIntInRange ( 0, 1 ) ); + ServerInfo.eCountry = static_cast ( GenRandomIntInRange ( 0, 100 ) ); ServerInfo.iMaxNumClients = GenRandomIntInRange ( -2, 10000 ); ServerInfo.strCity = GenRandomString(); ServerInfo.strName = GenRandomString(); @@ -231,12 +226,8 @@ public slots: break; case 19: // PROTMESSID_CLM_SERVER_LIST - vecServerInfo[0].bPermanentOnline = - static_cast ( GenRandomIntInRange ( 0, 1 ) ); - - vecServerInfo[0].eCountry = - static_cast ( GenRandomIntInRange ( 0, 100 ) ); - + vecServerInfo[0].bPermanentOnline = static_cast ( GenRandomIntInRange ( 0, 1 ) ); + vecServerInfo[0].eCountry = static_cast ( GenRandomIntInRange ( 0, 100 ) ); vecServerInfo[0].HostAddr = CurHostAddress; vecServerInfo[0].LHostAddr = CurLocalAddress; vecServerInfo[0].iMaxNumClients = GenRandomIntInRange ( -2, 10000 ); @@ -278,11 +269,55 @@ public slots: break; case 27: + { // arbitrary "audio" packet (with random sizes) CVector vecMessage ( GenRandomIntInRange ( 1, 1000 ) ); OnSendProtMessage ( vecMessage ); break; } + + case 28: // PROTMESSID_CLM_CONN_CLIENTS_LIST + vecChanInfo[0].iChanID = GenRandomIntInRange ( -2, 20 ); + vecChanInfo[0].iIpAddr = GenRandomIPv4Address().toIPv4Address(); + vecChanInfo[0].strName = GenRandomString(); + + Protocol.CreateCLConnClientsListMes ( CurHostAddress, vecChanInfo ); + break; + + case 29: // PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST + Protocol.CreateCLReqConnClientsListMes ( CurHostAddress ); + break; + + case 30: // PROTMESSID_CLM_CHANNEL_LEVEL_LIST + vecLevelList[0] = GenRandomIntInRange ( 0, 0xF ); + + Protocol.CreateCLChannelLevelListMes ( CurHostAddress, + vecLevelList, + 1 ); + break; + + case 31: // PROTMESSID_CLM_REGISTER_SERVER_RESP + eSvrRegResult = + static_cast ( GenRandomIntInRange ( 0, 1 ) ); + + Protocol.CreateCLRegisterServerResp ( CurHostAddress, + eSvrRegResult ); + break; + + case 32: // PROTMESSID_CHANNEL_PAN + Protocol.CreateChanPanMes ( GenRandomIntInRange ( -2, 20 ), + GenRandomIntInRange ( 0, 32767 ) ); + break; + + case 33: // PROTMESSID_MUTE_STATE_CHANGED + Protocol.CreateMuteStateHasChangedMes ( GenRandomIntInRange ( -2, 20 ), + GenRandomIntInRange ( 0, 1 ) ); + break; + + case 34: // PROTMESSID_CLIENT_ID + Protocol.CreateClientIDMes ( GenRandomIntInRange ( -2, 20 ) ); + break; + } } void OnSendProtMessage ( CVector vecMessage ) @@ -295,4 +330,9 @@ public slots: // send the next message Protocol.Reset(); } + + void OnSendCLMessage ( CHostAddress, CVector vecMessage ) + { + OnSendProtMessage ( vecMessage ); + } }; diff --git a/src/util.cpp b/src/util.cpp index ce17d6abb9..f39e2eef19 100755 --- a/src/util.cpp +++ b/src/util.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -28,48 +28,64 @@ /* Implementation *************************************************************/ // Input level meter implementation -------------------------------------------- -void CStereoSignalLevelMeter::Update ( const CVector& vecsAudio ) +void CStereoSignalLevelMeter::Update ( const CVector& vecsAudio, + const int iMonoBlockSizeSam, + const bool bIsStereoIn ) { - // get the stereo vector size - const int iStereoVecSize = vecsAudio.Size(); - // Get maximum of current block // // Speed optimization: - // - we only make use of the positive values and ignore the negative ones - // -> we do not need to call the fabs() function + // - we only make use of the negative values and ignore the positive ones (since + // int16 has range {-32768, 32767}) -> we do not need to call the fabs() function // - we only evaluate every third sample // // With these speed optimizations we might loose some information in // special cases but for the average music signals the following code // should give good results. - // - short sMaxL = 0; - short sMaxR = 0; + short sMinLOrMono = 0; + short sMinR = 0; - for ( int i = 0; i < iStereoVecSize; i += 6 ) // 2 * 3 = 6 -> stereo + if ( bIsStereoIn ) { - // left channel - sMaxL = std::max ( sMaxL, vecsAudio[i] ); + // stereo in + for ( int i = 0; i < 2 * iMonoBlockSizeSam; i += 6 ) // 2 * 3 = 6 -> stereo + { + // left (or mono) and right channel + sMinLOrMono = std::min ( sMinLOrMono, vecsAudio[i] ); + sMinR = std::min ( sMinR, vecsAudio[i + 1] ); + } - // right channel - sMaxR = std::max ( sMaxR, vecsAudio[i + 1] ); + // in case of mono out use minimum of both channels + if ( !bIsStereoOut ) + { + sMinLOrMono = std::min ( sMinLOrMono, sMinR ); + } + } + else + { + // mono in + for ( int i = 0; i < iMonoBlockSizeSam; i += 3 ) + { + sMinLOrMono = std::min ( sMinLOrMono, vecsAudio[i] ); + } } - dCurLevelL = UpdateCurLevel ( dCurLevelL, sMaxL ); - dCurLevelR = UpdateCurLevel ( dCurLevelR, sMaxR ); + // apply smoothing, if in stereo out mode, do this for two channels + dCurLevelLOrMono = UpdateCurLevel ( dCurLevelLOrMono, -sMinLOrMono ); + + if ( bIsStereoOut ) + { + dCurLevelR = UpdateCurLevel ( dCurLevelR, -sMinR ); + } } double CStereoSignalLevelMeter::UpdateCurLevel ( double dCurLevel, - const short& sMax ) + const double dMax ) { // decrease max with time if ( dCurLevel >= METER_FLY_BACK ) { -// TODO Calculate factor from sample rate and frame size (64 or 128 samples frame size). -// But tests with 128 and 64 samples frame size have shown that the meter fly back -// is ok for both numbers of samples frame size. - dCurLevel *= 0.97; + dCurLevel *= dSmoothingFactor; } else { @@ -77,9 +93,9 @@ double CStereoSignalLevelMeter::UpdateCurLevel ( double dCurLevel, } // update current level -> only use maximum - if ( static_cast ( sMax ) > dCurLevel ) + if ( dMax > dCurLevel ) { - return static_cast ( sMax ); + return dMax; } else { @@ -87,19 +103,29 @@ double CStereoSignalLevelMeter::UpdateCurLevel ( double dCurLevel, } } -double CStereoSignalLevelMeter::CalcLogResult ( const double& dLinearLevel ) +double CStereoSignalLevelMeter::CalcLogResultForMeter ( const double& dLinearLevel ) { - const double dNormMicLevel = dLinearLevel / _MAXSHORT; + const double dNormLevel = dLinearLevel / _MAXSHORT; // logarithmic measure - if ( dNormMicLevel > 0 ) + double dLevelForMeterdB = -100000.0; // large negative value + + if ( dNormLevel > 0 ) { - return 20.0 * log10 ( dNormMicLevel ); + dLevelForMeterdB = 20.0 * log10 ( dNormLevel ); } - else + + // map to signal level meter (linear transformation of the input + // level range to the level meter range) + dLevelForMeterdB -= LOW_BOUND_SIG_METER; + dLevelForMeterdB *= NUM_STEPS_LED_BAR / ( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER ); + + if ( dLevelForMeterdB < 0 ) { - return -100000.0; // large negative value + dLevelForMeterdB = 0; } + + return dLevelForMeterdB; } @@ -115,12 +141,12 @@ void CCRC::AddByte ( const uint8_t byNewInput ) { for ( int i = 0; i < 8; i++ ) { - // shift bits in shift-register for transistion + // shift bits in shift-register for transition iStateShiftReg <<= 1; // take bit, which was shifted out of the register-size and place it // at the beginning (LSB) - // (If condition is not satisfied, implicitely a "0" is added) + // (If condition is not satisfied, implicitly a "0" is added) if ( ( iStateShiftReg & iBitOutMask) > 0 ) { iStateShiftReg |= 1; @@ -165,20 +191,24 @@ uint32_t CCRC::GetCRC() three series allpass units, followed by four parallel comb filters, and two decorrelation delay lines in parallel at the output. */ -void CAudioReverb::Init ( const int iSampleRate, - const double rT60 ) +void CAudioReverb::Init ( const EAudChanConf eNAudioChannelConf, + const int iNStereoBlockSizeSam, + const int iSampleRate, + const float fT60 ) { - int delay, i; + // store parameters + eAudioChannelConf = eNAudioChannelConf; + iStereoBlockSizeSam = iNStereoBlockSizeSam; // delay lengths for 44100 Hz sample rate - int lengths[9] = { 1116, 1356, 1422, 1617, 225, 341, 441, 211, 179 }; - const double scaler = static_cast ( iSampleRate ) / 44100.0; + int lengths[9] = { 1116, 1356, 1422, 1617, 225, 341, 441, 211, 179 }; + const float scaler = static_cast ( iSampleRate ) / 44100.0f; - if ( scaler != 1.0 ) + if ( scaler != 1.0f ) { - for ( i = 0; i < 9; i++ ) + for ( int i = 0; i < 9; i++ ) { - delay = static_cast ( floor ( scaler * lengths[i] ) ); + int delay = static_cast ( floorf ( scaler * lengths[i] ) ); if ( ( delay & 1 ) == 0 ) { @@ -194,21 +224,21 @@ void CAudioReverb::Init ( const int iSampleRate, } } - for ( i = 0; i < 3; i++ ) + for ( int i = 0; i < 3; i++ ) { allpassDelays[i].Init ( lengths[i + 4] ); } - for ( i = 0; i < 4; i++ ) + for ( int i = 0; i < 4; i++ ) { combDelays[i].Init ( lengths[i] ); - combFilters[i].setPole ( 0.2 ); + combFilters[i].setPole ( 0.2f ); } - setT60 ( rT60, iSampleRate ); + setT60 ( fT60, iSampleRate ); outLeftDelay.Init ( lengths[7] ); outRightDelay.Init ( lengths[8] ); - allpassCoefficient = 0.7; + allpassCoefficient = 0.7f; Clear(); } @@ -225,7 +255,7 @@ bool CAudioReverb::isPrime ( const int number ) if ( number & 1 ) { - for ( int i = 3; i < static_cast ( sqrt ( static_cast ( number ) ) ) + 1; i += 2 ) + for ( int i = 3; i < static_cast ( sqrtf ( static_cast ( number ) ) ) + 1; i += 2 ) { if ( ( number % i ) == 0 ) { @@ -259,84 +289,107 @@ void CAudioReverb::Clear() outLeftDelay.Reset ( 0 ); } -void CAudioReverb::setT60 ( const double rT60, - const int iSampleRate ) +void CAudioReverb::setT60 ( const float fT60, + const int iSampleRate ) { // set the reverberation T60 decay time for ( int i = 0; i < 4; i++ ) { - combCoefficient[i] = pow ( 10.0, static_cast ( -3.0 * - combDelays[i].Size() / ( rT60 * iSampleRate ) ) ); + combCoefficient[i] = powf ( 10.0f, static_cast ( -3.0f * + combDelays[i].Size() / ( fT60 * iSampleRate ) ) ); } } -void CAudioReverb::COnePole::setPole ( const double dPole ) +void CAudioReverb::COnePole::setPole ( const float fPole ) { // calculate IIR filter coefficients based on the pole value - dA = -dPole; - dB = 1.0 - dPole; + fA = -fPole; + fB = 1.0f - fPole; } -double CAudioReverb::COnePole::Calc ( const double dIn ) +float CAudioReverb::COnePole::Calc ( const float fIn ) { // calculate IIR filter - dLastSample = dB * dIn - dA * dLastSample; + fLastSample = fB * fIn - fA * fLastSample; - return dLastSample; + return fLastSample; } -void CAudioReverb::ProcessSample ( int16_t& iInputOutputLeft, - int16_t& iInputOutputRight, - const double dAttenuation ) +void CAudioReverb::Process ( CVector& vecsStereoInOut, + const bool bReverbOnLeftChan, + const float fAttenuation ) { - // compute one output sample - double temp, temp0, temp1, temp2; - - // we sum up the stereo input channels (in case mono input is used, a zero - // shall be input for the right channel) - const double dMixedInput = 0.5 * ( iInputOutputLeft + iInputOutputRight ); - - temp = allpassDelays[0].Get(); - temp0 = allpassCoefficient * temp; - temp0 += dMixedInput; - allpassDelays[0].Add ( temp0 ); - temp0 = - ( allpassCoefficient * temp0 ) + temp; - - temp = allpassDelays[1].Get(); - temp1 = allpassCoefficient * temp; - temp1 += temp0; - allpassDelays[1].Add ( temp1 ); - temp1 = - ( allpassCoefficient * temp1 ) + temp; - - temp = allpassDelays[2].Get(); - temp2 = allpassCoefficient * temp; - temp2 += temp1; - allpassDelays[2].Add ( temp2 ); - temp2 = - ( allpassCoefficient * temp2 ) + temp; - - const double temp3 = temp2 + combFilters[0].Calc ( combCoefficient[0] * combDelays[0].Get() ); - const double temp4 = temp2 + combFilters[1].Calc ( combCoefficient[1] * combDelays[1].Get() ); - const double temp5 = temp2 + combFilters[2].Calc ( combCoefficient[2] * combDelays[2].Get() ); - const double temp6 = temp2 + combFilters[3].Calc ( combCoefficient[3] * combDelays[3].Get() ); - - combDelays[0].Add ( temp3 ); - combDelays[1].Add ( temp4 ); - combDelays[2].Add ( temp5 ); - combDelays[3].Add ( temp6 ); - - const double filtout = temp3 + temp4 + temp5 + temp6; - - outLeftDelay.Add ( filtout ); - outRightDelay.Add ( filtout ); - - // inplace apply the attenuated reverb signal - iInputOutputLeft = Double2Short ( - ( 1.0 - dAttenuation ) * iInputOutputLeft + - 0.5 * dAttenuation * outLeftDelay.Get() ); - - iInputOutputRight = Double2Short ( - ( 1.0 - dAttenuation ) * iInputOutputRight + - 0.5 * dAttenuation * outRightDelay.Get() ); + float fMixedInput, temp, temp0, temp1, temp2; + + for ( int i = 0; i < iStereoBlockSizeSam; i += 2 ) + { + // we sum up the stereo input channels (in case mono input is used, a zero + // shall be input for the right channel) + if ( eAudioChannelConf == CC_STEREO ) + { + fMixedInput = 0.5f * ( vecsStereoInOut[i] + vecsStereoInOut[i + 1] ); + } + else + { + if ( bReverbOnLeftChan ) + { + fMixedInput = vecsStereoInOut[i]; + } + else + { + fMixedInput = vecsStereoInOut[i + 1]; + } + } + + temp = allpassDelays[0].Get(); + temp0 = allpassCoefficient * temp; + temp0 += fMixedInput; + allpassDelays[0].Add ( temp0 ); + temp0 = - ( allpassCoefficient * temp0 ) + temp; + + temp = allpassDelays[1].Get(); + temp1 = allpassCoefficient * temp; + temp1 += temp0; + allpassDelays[1].Add ( temp1 ); + temp1 = - ( allpassCoefficient * temp1 ) + temp; + + temp = allpassDelays[2].Get(); + temp2 = allpassCoefficient * temp; + temp2 += temp1; + allpassDelays[2].Add ( temp2 ); + temp2 = - ( allpassCoefficient * temp2 ) + temp; + + const float temp3 = temp2 + combFilters[0].Calc ( combCoefficient[0] * combDelays[0].Get() ); + const float temp4 = temp2 + combFilters[1].Calc ( combCoefficient[1] * combDelays[1].Get() ); + const float temp5 = temp2 + combFilters[2].Calc ( combCoefficient[2] * combDelays[2].Get() ); + const float temp6 = temp2 + combFilters[3].Calc ( combCoefficient[3] * combDelays[3].Get() ); + + combDelays[0].Add ( temp3 ); + combDelays[1].Add ( temp4 ); + combDelays[2].Add ( temp5 ); + combDelays[3].Add ( temp6 ); + + const float filtout = temp3 + temp4 + temp5 + temp6; + + outLeftDelay.Add ( filtout ); + outRightDelay.Add ( filtout ); + + // inplace apply the attenuated reverb signal (for stereo always apply + // reverberation effect on both channels) + if ( ( eAudioChannelConf == CC_STEREO ) || bReverbOnLeftChan ) + { + vecsStereoInOut[i] = Float2Short ( + ( 1.0f - fAttenuation ) * vecsStereoInOut[i] + + 0.5f * fAttenuation * outLeftDelay.Get() ); + } + + if ( ( eAudioChannelConf == CC_STEREO ) || !bReverbOnLeftChan ) + { + vecsStereoInOut[i + 1] = Float2Short ( + ( 1.0f - fAttenuation ) * vecsStereoInOut[i + 1] + + 0.5f * fAttenuation * outRightDelay.Get() ); + } + } } @@ -344,18 +397,17 @@ void CAudioReverb::ProcessSample ( int16_t& iInputOutputLeft, * GUI Utilities * \******************************************************************************/ // About dialog ---------------------------------------------------------------- +#ifndef HEADLESS CAboutDlg::CAboutDlg ( QWidget* parent ) : QDialog ( parent ) { setupUi ( this ); // general description of software txvAbout->setText ( - "

" + tr ( "The " ) + APP_NAME + - tr ( " software enables musicians to perform real-time jam sessions " - "over the internet." ) + "
" + 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." ) + "

" + "

" + tr ( "This app enables musicians to perform real-time jam sessions " + "over the internet." ) + "
" + tr ( "There is a server which collects " + " the audio data from each client, mixes the audio data and sends the mix " + " back to each client." ) + "

" "

" // 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 " @@ -371,8 +423,8 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : QDialog ( parent ) "

" ); // libraries used by this compilation - txvLibraries->setText ( APP_NAME + - tr ( " uses the following libraries, resources or code snippets:" ) + + txvLibraries->setText ( + tr ( "This app uses the following libraries, resources or code snippets:" ) + "
" "

Opus Interactive Audio Codec" @@ -382,7 +434,7 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : QDialog ( parent ) "The Synthesis ToolKit in C++ (STK)

" "

" + tr ( "Some pixmaps are from the" ) + " Open Clip Art Library (OCAL), " "http://openclipart.org

" - "

" + tr ( "Country flag icons from Mark James" ) + + "

" + tr ( "Country flag icons by Mark James" ) + ", http://www.famfamfam.com

" ); // contributors list @@ -390,17 +442,39 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : QDialog ( parent ) "

Peter L. Jones (pljones)

" "

Jonathan Baker-Bates (gilgongo)

" "

Daniele Masato (doloopuntil)

" + "

Martin Schilde (geheimerEichkater)

" "

Simon Tomlinson (sthenos)

" "

Marc jr. Landolt (braindef)

" "

Olivier Humbert (trebmuh)

" "

Tarmo Johannes (tarmoj)

" "

mirabilos (mirabilos)

" + "

Hector Martin (marcan)

" "

newlaurent62 (newlaurent62)

" + "

AronVietti (AronVietti)

" "

Emlyn Bolton (emlynmac)

" "

Jos van den Oever (vandenoever)

" "

Tormod Volden (tormodvolden)

" + "

Alberstein8 (Alberstein8)

" + "

Gauthier Fleutot Östervall (fleutot)

" + "

Tony Mountifield (softins)

" + "

HPS (hselasky)

" "

Stanislas Michalak (stanislas-m)

" "

JP Cimalando (jpcima)

" + "

Adam Sampson (atsampson)

" + "

Jakob Jarmar (jarmar)

" + "

Stefan Weil (stweil)

" + "

Nils Brederlow (dingodoppelt)

" + "

Sebastian Krzyszkowiak (dos1)

" + "

Bryan Flamig (bflamig)

" + "

Kris Raney (kraney)

" + "

dszgit (dszgit)

" + "

ann0see (ann0see)

" + "

jc-Rosichini (jc-Rosichini)

" + "

Julian Santander (j-santander)

" + "

chigkim (chigkim)

" + "

Bodo (bomm)

" + "

jp8 (jp8)

" + "

bspeer (bspeer)

" "
" + tr ( "For details on the contributions check out the " ) + "" + tr ( "Github Contributors list" ) + "." ); @@ -412,12 +486,20 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : QDialog ( parent ) "

Olivier Humbert (trebmuh)

" "

" + tr ( "Portuguese" ) + "

" "

Miguel de Matos (Snayler)

" + "

Melcon Moraes (melcon)

" "

" + tr ( "Dutch" ) + "

" "

Jeroen Geertzen (jerogee)

" "

" + tr ( "Italian" ) + "

" "

Giuseppe Sapienza (dzpex)

" "

" + tr ( "German" ) + "

" - "

Volker Fischer (corrados)

" ); + "

Volker Fischer (corrados)

" + "

" + tr ( "Polish" ) + "

" + "

Martyna Danysz (Martyna27)

" + "

Tomasz Bojczuk (SeeLook)

" + "

" + tr ( "Swedish" ) + "

" + "

Daniel (genesisproject2020)

" + "

" + tr ( "Slovak" ) + "

" + "

Jose Riha (jose1711)

" ); // set version number in about dialog lblVersion->setText ( GetVersionAndNameStr() ); @@ -426,42 +508,6 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : QDialog ( parent ) setWindowTitle ( tr ( "About " ) + APP_NAME ); } -QString CAboutDlg::GetVersionAndNameStr ( const bool bWithHtml ) -{ - QString strVersionText = ""; - - // name, short description and GPL hint - if ( bWithHtml ) - { - strVersionText += ""; - } - else - { - strVersionText += " *** "; - } - - strVersionText += APP_NAME + tr ( ", Version " ) + VERSION; - - if ( bWithHtml ) - { - strVersionText += "
"; - } - else - { - strVersionText += "\n *** "; - } - - if ( !bWithHtml ) - { - strVersionText += tr ( "Internet Jam Session Software" ); - strVersionText += "\n *** "; - } - - strVersionText += tr ( "Under the GNU General Public License (GPL)" ); - - return strVersionText; -} - // Licence dialog -------------------------------------------------------------- CLicenceDlg::CLicenceDlg ( QWidget* parent ) : QDialog ( parent ) @@ -474,12 +520,11 @@ CLicenceDlg::CLicenceDlg ( QWidget* parent ) : QDialog ( parent ) - Decline button */ setWindowIcon ( QIcon ( QString::fromUtf8 ( ":/png/main/res/fronticon.png" ) ) ); - resize ( 700, 450 ); QVBoxLayout* pLayout = new QVBoxLayout ( this ); QHBoxLayout* pSubLayout = new QHBoxLayout; - QTextBrowser* txvLicence = new QTextBrowser ( this ); - QCheckBox* chbAgree = new QCheckBox ( tr ( "I &agree to the above licence terms" ), this ); + QLabel* lblLicence = new QLabel ( tr ( "This server requires you accept conditions before you can join. Please read these in the chat window." ), this ); + QCheckBox* chbAgree = new QCheckBox ( tr ( "I have read the conditions and &agree." ), this ); butAccept = new QPushButton ( tr ( "Accept" ), this ); QPushButton* butDecline = new QPushButton ( tr ( "Decline" ), this ); @@ -487,57 +532,21 @@ CLicenceDlg::CLicenceDlg ( QWidget* parent ) : QDialog ( parent ) pSubLayout->addWidget ( chbAgree ); pSubLayout->addWidget ( butAccept ); pSubLayout->addWidget ( butDecline ); - pLayout->addWidget ( txvLicence ); + pLayout->addWidget ( lblLicence ); pLayout->addLayout ( pSubLayout ); // set some properties butAccept->setEnabled ( false ); butAccept->setDefault ( true ); - txvLicence->setOpenExternalLinks ( true ); - - // define the licence text (similar to what we have in Ninjam) - txvLicence->setText ( - "

" + tr ( - "By connecting to this server and agreeing to this notice, you agree to the " - "following:" ) + "

" + tr ( - "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 " ) + - "" - "http://creativecommons.org/licenses/by-nc-sa/4.0):

" - "

Attribution-NonCommercial-ShareAlike 4.0

" - "

" + tr ( "You are free to:" ) + - "

    " - "
  • " + tr ( "Share" ) + " - " + - tr ( "copy and redistribute the material in any medium or format" ) + "
  • " - "
  • " + tr ( "Adapt" ) + " - " + - tr ( "remix, transform, and build upon the material" ) + "
  • " - "
" + tr ( "The licensor cannot revoke these freedoms as long as you follow the " - "license terms." ) + "

" - "

" + tr ( "Under the following terms:" ) + - "

    " - "
  • " + tr ( "Attribution" ) + " - " + - tr ( "You must give appropriate credit, provide a link to the license, and indicate " - "if changes were made. You may do so in any reasonable manner, but not in any way " - "that suggests the licensor endorses you or your use." ) + "
  • " - "
  • " + tr ( "NonCommercial" ) + " - " + - tr ( "You may not use the material for commercial purposes." ) + "
  • " - "
  • " + tr ( "ShareAlike" ) + " - " + - tr ( "If you remix, transform, or build upon the material, you must distribute your " - "contributions under the same license as the original." ) + "
  • " - "
" + tr ( "No additional restrictions" ) + " — " + - tr ( "You may not apply legal terms or technological measures that legally restrict " - "others from doing anything the license permits." ) + "

" ); - - QObject::connect ( chbAgree, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnAgreeStateChanged ( int ) ) ); - - QObject::connect ( butAccept, SIGNAL ( clicked() ), - this, SLOT ( accept() ) ); - - QObject::connect ( butDecline, SIGNAL ( clicked() ), - this, SLOT ( reject() ) ); + + QObject::connect ( chbAgree, &QCheckBox::stateChanged, + this, &CLicenceDlg::OnAgreeStateChanged ); + + QObject::connect ( butAccept, &QPushButton::clicked, + this, &CLicenceDlg::accept ); + + QObject::connect ( butDecline, &QPushButton::clicked, + this, &CLicenceDlg::reject ); } @@ -704,16 +713,15 @@ CMusProfDlg::CMusProfDlg ( CClient* pNCliP, // Add help text to controls ----------------------------------------------- // fader tag - QString strFaderTag = "" + tr ( "Musician Profile" ) + ": " + 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 in. " - "The city you live in and the skill level playing your instrument " - "may also be added." ) + "
" + 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 " - "you. If the name is left empty, the IP address is shown instead." ); + QString strFaderTag = "" + tr ( "Musician Profile" ) + ": " + + tr ( "Write your name or an alias here so the other musicians you want to " + "play with know who you are. You may also add a picture of the instrument " + "you play and a flag of the country you are located in. " + "Your city and skill level playing your instrument may also be added." ) + + "
" + tr ( "What you set here will appear at your fader on the mixer " + "board when you are connected to a Jamulus server. This tag will " + "also be shown at each client which is connected to the same server as " + "you." ); pedtAlias->setWhatsThis ( strFaderTag ); pedtAlias->setAccessibleName ( tr ( "Alias or name edit box" ) ); @@ -728,23 +736,23 @@ CMusProfDlg::CMusProfDlg ( CClient* pNCliP, // Connections ------------------------------------------------------------- - QObject::connect ( pedtAlias, SIGNAL ( textChanged ( const QString& ) ), - this, SLOT ( OnAliasTextChanged ( const QString& ) ) ); + QObject::connect ( pedtAlias, &QLineEdit::textChanged, + this, &CMusProfDlg::OnAliasTextChanged ); - QObject::connect ( pcbxInstrument, SIGNAL ( activated ( int ) ), - this, SLOT ( OnInstrumentActivated ( int ) ) ); + QObject::connect ( pcbxInstrument, static_cast ( &QComboBox::activated ), + this, &CMusProfDlg::OnInstrumentActivated ); - QObject::connect ( pcbxCountry, SIGNAL ( activated ( int ) ), - this, SLOT ( OnCountryActivated ( int ) ) ); + QObject::connect ( pcbxCountry, static_cast ( &QComboBox::activated ), + this, &CMusProfDlg::OnCountryActivated ); - QObject::connect ( pedtCity, SIGNAL ( textChanged ( const QString& ) ), - this, SLOT ( OnCityTextChanged ( const QString& ) ) ); + QObject::connect ( pedtCity, &QLineEdit::textChanged, + this, &CMusProfDlg::OnCityTextChanged ); - QObject::connect ( pcbxSkill, SIGNAL ( activated ( int ) ), - this, SLOT ( OnSkillActivated ( int ) ) ); + QObject::connect ( pcbxSkill, static_cast ( &QComboBox::activated ), + this, &CMusProfDlg::OnSkillActivated ); - QObject::connect ( butClose, SIGNAL ( clicked() ), - this, SLOT ( accept() ) ); + QObject::connect ( butClose, &QPushButton::clicked, + this, &CMusProfDlg::accept ); } void CMusProfDlg::showEvent ( QShowEvent* ) @@ -785,7 +793,7 @@ void CMusProfDlg::OnAliasTextChanged ( const QString& strNewName ) } else { - // text is too long, update control with shortend text + // text is too long, update control with shortened text pedtAlias->setText ( strNewName.left ( MAX_LEN_FADER_TAG ) ); } } @@ -823,7 +831,7 @@ void CMusProfDlg::OnCityTextChanged ( const QString& strNewCity ) } else { - // text is too long, update control with shortend text + // text is too long, update control with shortened text pedtCity->setText ( strNewCity.left ( MAX_LEN_SERVER_CITY ) ); } } @@ -859,6 +867,72 @@ CHelpMenu::CHelpMenu ( const bool bIsClient, QWidget* parent ) : QMenu ( tr ( "& } +// Language combo box ---------------------------------------------------------- +CLanguageComboBox::CLanguageComboBox ( QWidget* parent ) : + QComboBox ( parent ), + iIdxSelectedLanguage ( INVALID_INDEX ) +{ + QObject::connect ( this, static_cast ( &QComboBox::activated ), + this, &CLanguageComboBox::OnLanguageActivated ); +} + +void CLanguageComboBox::Init ( QString& strSelLanguage ) +{ + // load available translations + const QMap TranslMap = CLocale::GetAvailableTranslations(); + QMapIterator MapIter ( TranslMap ); + + // add translations to the combobox list + clear(); + int iCnt = 0; + int iIdxOfEnglishLanguage = 0; + iIdxSelectedLanguage = INVALID_INDEX; + + while ( MapIter.hasNext() ) + { + MapIter.next(); + addItem ( QLocale ( MapIter.key() ).nativeLanguageName() + " (" + MapIter.key() + ")", MapIter.key() ); + + // store the combo box index of the default english language + if ( MapIter.key().compare ( "en" ) == 0 ) + { + iIdxOfEnglishLanguage = iCnt; + } + + // if the selected language is found, store the combo box index + if ( MapIter.key().compare ( strSelLanguage ) == 0 ) + { + iIdxSelectedLanguage = iCnt; + } + + iCnt++; + } + + // if the selected language was not found, use the english language + if ( iIdxSelectedLanguage == INVALID_INDEX ) + { + strSelLanguage = "en"; + iIdxSelectedLanguage = iIdxOfEnglishLanguage; + } + + setCurrentIndex ( iIdxSelectedLanguage ); +} + +void CLanguageComboBox::OnLanguageActivated ( int iLanguageIdx ) +{ + // only update if the language selection is different from the current selected language + if ( iIdxSelectedLanguage != iLanguageIdx ) + { + QMessageBox::information ( this, + tr ( "Restart Required" ), + tr ( "Please restart the application for the language change to take effect." ) ); + + emit LanguageChanged ( itemData ( iLanguageIdx ).toString() ); + } +} +#endif + + /******************************************************************************\ * Other Classes * \******************************************************************************/ @@ -905,22 +979,32 @@ bool NetworkUtil::ParseNetworkAddress ( QString strAddress, // first try if this is an IP number an can directly applied to QHostAddress if ( !InetAddr.setAddress ( strAddress ) ) { - // it was no vaild IP address, try to get host by name, assuming + // it was no valid IP address, try to get host by name, assuming // that the string contains a valid host name string const QHostInfo HostInfo = QHostInfo::fromName ( strAddress ); - if ( HostInfo.error() == QHostInfo::NoError ) + if ( HostInfo.error() != QHostInfo::NoError ) { - // apply IP address to QT object - if ( !HostInfo.addresses().isEmpty() ) - { - // use the first IP address - InetAddr = HostInfo.addresses().first(); - } + return false; // invalid address } - else + + // use the first IPv4 address, if any + bool bFoundIPv4 = false; + + foreach ( const QHostAddress HostAddr, HostInfo.addresses() ) { - return false; // invalid address + if ( HostAddr.protocol() == QAbstractSocket::IPv4Protocol ) + { + InetAddr = HostAddr; + bFoundIPv4 = true; + break; + } + } + + if ( !bFoundIPv4 ) + { + // only found IPv6 addresses + return false; } } @@ -962,20 +1046,27 @@ QString NetworkUtil::GetCentralServerAddress ( const ECSAddType eCentralServerAd } } +QString NetworkUtil::FixAddress ( const QString& strAddress ) +{ + // remove all spaces from the address string + return strAddress.simplified().replace ( " ", "" ); +} + // Instrument picture data base ------------------------------------------------ -CVector& CInstPictures::GetTable() +CVector& CInstPictures::GetTable ( const bool bReGenerateTable ) { // make sure we generate the table only once static bool TableIsInitialized = false; static CVector vecDataBase; - if ( !TableIsInitialized ) + if ( !TableIsInitialized || bReGenerateTable ) { // 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.Init ( 0 ); // first clear all existing data since we create the list be adding entries vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "None" ), ":/png/instr/res/instruments/none.png", IC_OTHER_INSTRUMENT ) ); // special first element vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Drum Set" ), ":/png/instr/res/instruments/drumset.png", IC_PERCUSSION_INSTRUMENT ) ); vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Djembe" ), ":/png/instr/res/instruments/djembe.png", IC_PERCUSSION_INSTRUMENT ) ); @@ -1017,6 +1108,13 @@ CVector& CInstPictures::GetTable() vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Vocal Soprano" ), ":/png/instr/res/instruments/vocalsoprano.png", IC_OTHER_INSTRUMENT ) ); vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Banjo" ), ":/png/instr/res/instruments/banjo.png", IC_PLUCKING_INSTRUMENT ) ); vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Mandolin" ), ":/png/instr/res/instruments/mandolin.png", IC_PLUCKING_INSTRUMENT ) ); + vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Ukulele" ), ":/png/instr/res/instruments/ukulele.png", IC_PLUCKING_INSTRUMENT ) ); + vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Bass Ukulele" ), ":/png/instr/res/instruments/bassukulele.png", IC_PLUCKING_INSTRUMENT ) ); + vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Vocal Baritone" ), ":/png/instr/res/instruments/vocalbaritone.png", IC_OTHER_INSTRUMENT ) ); + vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Vocal Lead" ), ":/png/instr/res/instruments/vocallead.png", IC_OTHER_INSTRUMENT ) ); + vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Mountain Dulcimer" ), ":/png/instr/res/instruments/mountaindulcimer.png", IC_STRING_INSTRUMENT ) ); + vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Scratching" ), ":/png/instr/res/instruments/scratching.png", IC_OTHER_INSTRUMENT ) ); + vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Rapping" ), ":/png/instr/res/instruments/rapping.png", IC_OTHER_INSTRUMENT ) ); // now the table is initialized TableIsInitialized = true; @@ -1086,13 +1184,11 @@ QString CLocale::GetCountryFlagIconsResourceReference ( const QLocale::Country e } else { - // NOTE: The following code was introduced to support old QT versions. The problem // is that the number of countries displayed is less than the one displayed // with the new code below (which is disabled). Therefore, as soon as the // compatibility to the very old versions of QT is not required anymore, use // the new code. - // COMPATIBLE FOR OLD QT VERSIONS -> use a table: QString strISO3166 = ""; switch ( static_cast ( eCountry ) ) @@ -1288,7 +1384,6 @@ QString CLocale::GetCountryFlagIconsResourceReference ( const QLocale::Country e strReturn = ""; } - // AT LEAST QT 4.8 IS REQUIRED: /* // There is no direct query of the country code in Qt, therefore we use a @@ -1306,8 +1401,7 @@ QString CLocale::GetCountryFlagIconsResourceReference ( const QLocale::Country e // the second split contains the name we need if ( vstrLocParts.size() > 1 ) { - strReturn = - ":/png/flags/res/flags/" + vstrLocParts.at ( 1 ).toLower() + ".png"; + strReturn = ":/png/flags/res/flags/" + vstrLocParts.at ( 1 ).toLower() + ".png"; // check if file actually exists, if not then invalidate reference if ( !QFile::exists ( strReturn ) ) @@ -1330,20 +1424,79 @@ QString CLocale::GetCountryFlagIconsResourceReference ( const QLocale::Country e return strReturn; } -ECSAddType CLocale::GetCentralServerAddressType ( const QLocale::Country eCountry ) +QMap CLocale::GetAvailableTranslations() { -// TODO this is the initial implementation and should be extended in the future, -// maybe there is/will be some function in Qt to get the continent - switch ( eCountry ) + QMap TranslMap; + QDirIterator DirIter ( ":/translations" ); + + // add english language (default which is in the actual source code) + TranslMap["en"] = ""; // empty file name means that the translation load fails and we get the default english language + + while ( DirIter.hasNext() ) { - case QLocale::UnitedStates: - case QLocale::Canada: - case QLocale::Mexico: - case QLocale::Greenland: - return AT_ALL_GENRES; - - default: - return AT_DEFAULT; + // get alias of translation file + const QString strCurFileName = DirIter.next(); + + // extract only language code (must be at the end, separated with a "_") + const QString strLoc = strCurFileName.right ( strCurFileName.length() - strCurFileName.indexOf ( "_" ) - 1 ); + + TranslMap[strLoc] = strCurFileName; + } + + return TranslMap; +} + +QPair CLocale::FindSysLangTransFileName ( const QMap& TranslMap ) +{ + QPair PairSysLang ( "", "" ); + QStringList slUiLang = QLocale().uiLanguages(); + + if ( !slUiLang.isEmpty() ) + { + QString strUiLang = QLocale().uiLanguages().at ( 0 ); + strUiLang.replace ( "-", "_" ); + + // first try to find the complete language string + if ( TranslMap.constFind ( strUiLang ) != TranslMap.constEnd() ) + { + PairSysLang.first = strUiLang; + PairSysLang.second = TranslMap[PairSysLang.first]; + } + else + { + // only extract two first characters to identify language (ignoring + // location for getting a simpler implementation -> if the language + // is not correct, the user can change it in the GUI anyway) + if ( strUiLang.length() >= 2 ) + { + PairSysLang.first = strUiLang.left ( 2 ); + PairSysLang.second = TranslMap[PairSysLang.first]; + } + } + } + + return PairSysLang; +} + +void CLocale::LoadTranslation ( const QString strLanguage, + QCoreApplication* pApp ) +{ + // The translator objects must be static! + static QTranslator myappTranslator; + static QTranslator myqtTranslator; + + QMap TranslMap = CLocale::GetAvailableTranslations(); + const QString strTranslationFileName = TranslMap[strLanguage]; + + if ( myappTranslator.load ( strTranslationFileName ) ) + { + pApp->installTranslator ( &myappTranslator ); + } + + // allows the Qt messages to be translated in the application + if ( myqtTranslator.load ( QLocale ( strLanguage ), "qt", "_", QLibraryInfo::location ( QLibraryInfo::TranslationsPath ) ) ) + { + pApp->installTranslator ( &myqtTranslator ); } } @@ -1376,25 +1529,38 @@ QTextStream* ConsoleWriterFactory::get() /******************************************************************************\ * Global Functions Implementation * \******************************************************************************/ -void DebugError ( const QString& pchErDescr, - const QString& pchPar1Descr, - const double dPar1, - const QString& pchPar2Descr, - const double dPar2 ) +QString GetVersionAndNameStr ( const bool bWithHtml ) { - QFile File ( "DebugError.dat" ); - if ( File.open ( QIODevice::Append ) ) + QString strVersionText = ""; + + // name, short description and GPL hint + if ( bWithHtml ) { - // append new line in logging file - QTextStream out ( &File ); - out << pchErDescr << " ### " << - pchPar1Descr << ": " << QString().setNum ( dPar1, 'f', 2 ) << - " ### " << - pchPar2Descr << ": " << QString().setNum ( dPar2, 'f', 2 ) << - endl; - - File.close(); + strVersionText += ""; } - printf ( "\nDebug error! For more information see test/DebugError.dat\n" ); - exit ( 1 ); + else + { + strVersionText += " *** "; + } + + strVersionText += APP_NAME + QCoreApplication::tr ( ", Version " ) + VERSION; + + if ( bWithHtml ) + { + strVersionText += "
"; + } + else + { + strVersionText += "\n *** "; + } + + if ( !bWithHtml ) + { + strVersionText += QCoreApplication::tr ( "Internet Jam Session Software" ); + strVersionText += "\n *** "; + } + + strVersionText += QCoreApplication::tr ( "Released under the GNU General Public License (GPL)" ); + + return strVersionText; } diff --git a/src/util.h b/src/util.h index ebeeb05e1d..d8a7485dc2 100755 --- a/src/util.h +++ b/src/util.h @@ -18,25 +18,32 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ #pragma once +#include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#ifndef HEADLESS +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include "ui_aboutdlgbase.h" +#endif #include -#include +#include +#include +#include #include #include #include @@ -56,7 +63,6 @@ using namespace std; // because of the library: "vector" #else # include #endif -#include "ui_aboutdlgbase.h" class CClient; // forward declaration of CClient @@ -68,31 +74,24 @@ class CClient; // forward declaration of CClient /* Global functions ***********************************************************/ -// converting double to short -inline short Double2Short ( const double dInput ) +// converting float to short +inline short Float2Short ( const float fInput ) { // lower bound - if ( dInput < _MINSHORT ) + if ( fInput < _MINSHORT ) { return _MINSHORT; } // upper bound - if ( dInput > _MAXSHORT ) + if ( fInput > _MAXSHORT ) { return _MAXSHORT; } - return static_cast ( dInput ); + return static_cast ( fInput ); } -// debug error handling -void DebugError ( const QString& pchErDescr, - const QString& pchPar1Descr, - const double dPar1, - const QString& pchPar2Descr, - const double dPar2 ); - // calculate the bit rate in bits per second from the number of coded bytes inline int CalcBitRateBitsPerSecFromCodedBytes ( const int iCeltNumCodedBytes, const int iFrameSize ) @@ -100,6 +99,7 @@ inline int CalcBitRateBitsPerSecFromCodedBytes ( const int iCeltNumCodedBytes, return ( SYSTEM_SAMPLE_RATE_HZ * iCeltNumCodedBytes * 8 ) / iFrameSize; } +QString GetVersionAndNameStr ( const bool bWithHtml = true ); /******************************************************************************\ @@ -143,47 +143,6 @@ template class CVector : public std::vector // this function simply converts the type of size to integer inline int Size() const { return static_cast ( std::vector::size() ); } - - // This operator allows for a l-value assignment of this object: - // CVector[x] = y is possible - inline TData& operator[] ( const int iPos ) - { -#ifdef _DEBUG_ - if ( ( iPos < 0 ) || ( iPos > Size() - 1 ) ) - { - DebugError ( "Writing vector out of bounds", "Vector size", - Size(), "New parameter", iPos ); - } -#endif - return std::vector::operator[] ( iPos ); - } - - inline TData operator[] ( const int iPos ) const - { -#ifdef _DEBUG_ - if ( ( iPos < 0 ) || ( iPos > Size() - 1 ) ) - { - DebugError ( "Reading vector out of bounds", "Vector size", - Size(), "New parameter", iPos ); - } -#endif - return std::vector::operator[] ( iPos ); - } - - inline CVector& operator= ( const CVector& vecI ) - { -#ifdef _DEBUG_ - // vectors which shall be copied MUST have same size! - if ( vecI.Size() != Size() ) - { - DebugError ( "Vector operator=() different size", "Vector size", - Size(), "New parameter", vecI.Size() ); - } -#endif - std::vector::operator= ( vecI ); - - return *this; - } }; @@ -233,9 +192,9 @@ template int CVector::StringFiFoWithCompare ( const QString { // only add old element if it is not the same as the // selected one - if ( operator[] ( iIdx ).compare ( strNewValue ) ) + if ( std::vector::operator[] ( iIdx ).compare ( strNewValue ) ) { - vstrTempList[iTempListCnt] = operator[] ( iIdx ); + vstrTempList[iTempListCnt] = std::vector::operator[] ( iIdx ); iTempListCnt++; } @@ -408,6 +367,7 @@ template void CMovingAv::Add ( const TData tNewD ) /******************************************************************************\ * GUI Utilities * \******************************************************************************/ +#ifndef HEADLESS // About dialog ---------------------------------------------------------------- class CAboutDlg : public QDialog, private Ui_CAboutDlgBase { @@ -415,8 +375,6 @@ class CAboutDlg : public QDialog, private Ui_CAboutDlgBase public: CAboutDlg ( QWidget* parent = nullptr ); - - static QString GetVersionAndNameStr ( const bool bWithHtml = true ); }; @@ -485,6 +443,28 @@ public slots: }; +// Language combo box ---------------------------------------------------------- +class CLanguageComboBox : public QComboBox +{ + Q_OBJECT + +public: + CLanguageComboBox ( QWidget* parent = nullptr ); + + void Init ( QString& strSelLanguage ); + +protected: + int iIdxSelectedLanguage; + +public slots: + void OnLanguageActivated ( int iLanguageIdx ); + +signals: + void LanguageChanged ( QString strLanguage ); +}; +#endif + + // Console writer factory ------------------------------------------------------ // this class was written by pljones class ConsoleWriterFactory @@ -501,8 +481,6 @@ class ConsoleWriterFactory /******************************************************************************\ * Other Classes/Enums * \******************************************************************************/ - - // Audio channel configuration ------------------------------------------------- enum EAudChanConf { @@ -524,6 +502,15 @@ enum EAudComprType }; +// Network transport flags ----------------------------------------------------- +enum ENetwFlags +{ + // used for protocol -> enum values must be fixed! + NF_NONE = 0, + NF_WITH_COUNTER = 1 // using a network counter to correctly order UDP packets in jitter buffer +}; + + // Audio quality enum ---------------------------------------------------------- enum EAudioQuality { @@ -549,7 +536,8 @@ enum EGUIDesign { // used for settings -> enum values should be fixed GD_STANDARD = 0, - GD_ORIGINAL = 1 + GD_ORIGINAL = 1, + GD_SLIMFADER = 2 }; @@ -562,6 +550,29 @@ enum ELicenceType }; +// Server jam recorder state enum ---------------------------------------------- +enum ERecorderState +{ + // used for protocol -> enum values must be fixed! + RS_UNDEFINED = 0, + RS_NOT_INITIALISED = 1, + RS_NOT_ENABLED = 2, + RS_RECORDING = 3 +}; + + +// Channel sort type ----------------------------------------------------------- +enum EChSortType +{ + // used for settings -> enum values should be fixed + ST_NO_SORT = 0, + ST_BY_NAME = 1, + ST_BY_INSTRUMENT = 2, + ST_BY_GROUPID = 3, + ST_BY_CITY = 4 +}; + + // Central server address type ------------------------------------------------- enum ECSAddType { @@ -591,7 +602,7 @@ inline QString csCentServAddrTypeToString ( ECSAddType eAddrType ) return QCoreApplication::translate ( "CClientSettingsDlg", "Genre Jazz" ); case AT_GENRE_CLASSICAL_FOLK: - return QCoreApplication::translate ( "CClientSettingsDlg", "Genre Classical/Folk/Choir" ); + return QCoreApplication::translate ( "CClientSettingsDlg", "Genre Classical/Folk/Choral" ); default: // AT_DEFAULT return QCoreApplication::translate ( "CClientSettingsDlg", "Default" ); @@ -602,13 +613,15 @@ inline QString csCentServAddrTypeToString ( ECSAddType eAddrType ) // Slave server registration state --------------------------------------------- enum ESvrRegStatus { - SRS_UNREGISTERED = 0, - SRS_BAD_ADDRESS = 1, - SRS_REQUESTED = 2, - SRS_TIME_OUT = 3, - SRS_UNKNOWN_RESP = 4, - SRS_REGISTERED = 5, - SRS_CENTRAL_SVR_FULL = 6 + SRS_UNREGISTERED, + SRS_BAD_ADDRESS, + SRS_REQUESTED, + SRS_TIME_OUT, + SRS_UNKNOWN_RESP, + SRS_REGISTERED, + SRS_CENTRAL_SVR_FULL, + SRS_VERSION_TOO_OLD, + SRS_NOT_FULFILL_REQUIREMENTS }; inline QString svrRegStatusToString ( ESvrRegStatus eSvrRegStatus ) @@ -635,6 +648,12 @@ inline QString svrRegStatusToString ( ESvrRegStatus eSvrRegStatus ) case SRS_CENTRAL_SVR_FULL: return QCoreApplication::translate ( "CServerDlg", "Central Server full" ); + + case SRS_VERSION_TOO_OLD: + return QCoreApplication::translate ( "CServerDlg", "Your server version is too old" ); + + case SRS_NOT_FULFILL_REQUIREMENTS: + return QCoreApplication::translate ( "CServerDlg", "Requirements not fulfilled" ); } return QString ( QCoreApplication::translate ( "CServerDlg", "Unknown value " ) ).append ( eSvrRegStatus ); @@ -646,7 +665,9 @@ enum ESvrRegResult { // used for protocol -> enum values must be fixed! SRR_REGISTERED = 0, - SRR_CENTRAL_SVR_FULL = 1 + SRR_CENTRAL_SVR_FULL = 1, + SRR_VERSION_TOO_OLD = 2, + SRR_NOT_FULFILL_REQIREMENTS = 3 }; @@ -679,25 +700,35 @@ enum ESkillLevel class CStereoSignalLevelMeter { public: - CStereoSignalLevelMeter() { Reset(); } +// TODO Calculate smoothing factor from sample rate and frame size (64 or 128 samples frame size). +// But tests with 128 and 64 samples frame size have shown that the meter fly back +// is ok for both numbers of samples frame size with a factor of 0.99. + CStereoSignalLevelMeter ( const bool bNIsStereoOut = true, + const double dNSmoothingFactor = 0.99 ) : + dSmoothingFactor ( dNSmoothingFactor ), bIsStereoOut ( bNIsStereoOut ) { Reset(); } + + void Update ( const CVector& vecsAudio, + const int iInSize, + const bool bIsStereoIn ); - void Update ( const CVector& vecsAudio ); - double MicLeveldBLeft() { return CalcLogResult ( dCurLevelL ); } - double MicLeveldBRight() { return CalcLogResult ( dCurLevelR ); } - static double CalcLogResult ( const double& dLinearLevel ); + double GetLevelForMeterdBLeftOrMono() { return CalcLogResultForMeter ( dCurLevelLOrMono ); } + double GetLevelForMeterdBRight() { return CalcLogResultForMeter ( dCurLevelR ); } + static double CalcLogResultForMeter ( const double& dLinearLevel ); void Reset() { - dCurLevelL = 0.0; - dCurLevelR = 0.0; + dCurLevelLOrMono = 0.0; + dCurLevelR = 0.0; } protected: - double UpdateCurLevel ( double dCurLevel, - const short& sMax ); + double UpdateCurLevel ( double dCurLevel, + const double dMax ); - double dCurLevelL; + double dCurLevelLOrMono; double dCurLevelR; + double dSmoothingFactor; + bool bIsStereoOut; }; @@ -792,6 +823,7 @@ class CInstPictures static QString GetResourceReference ( const int iInstrument ); static QString GetName ( const int iInstrument ); static EInstCategory GetCategory ( const int iInstrument ); + static void UpdateTableOnLanguageChange() { GetTable ( true ); } // TODO make use of instrument category (not yet implemented) @@ -817,8 +849,7 @@ class CInstPictures }; static bool IsInstIndexInRange ( const int iIdx ); - - static CVector& GetTable(); + static CVector& GetTable ( const bool bReGenerateTable = false ); }; @@ -826,8 +857,11 @@ class CInstPictures class CLocale { public: - static QString GetCountryFlagIconsResourceReference ( const QLocale::Country eCountry ); - static ECSAddType GetCentralServerAddressType ( const QLocale::Country eCountry ); + static QString GetCountryFlagIconsResourceReference ( const QLocale::Country eCountry ); + static QMap GetAvailableTranslations(); + static QPair FindSysLangTransFileName ( const QMap& TranslMap ); + static void LoadTranslation ( const QString strLanguage, + QCoreApplication* pApp ); }; @@ -836,11 +870,11 @@ class CChannelCoreInfo { public: CChannelCoreInfo() : - strName ( "" ), - eCountry ( QLocale::AnyCountry ), - strCity ( "" ), - iInstrument ( CInstPictures::GetNotUsedInstrument() ), - eSkillLevel ( SL_NOT_SET ) {} + strName ( "" ), + eCountry ( QLocale::AnyCountry ), + strCity ( "" ), + iInstrument ( CInstPictures::GetNotUsedInstrument() ), + eSkillLevel ( SL_NOT_SET ) {} CChannelCoreInfo ( const QString NsName, const QLocale::Country& NeCountry, @@ -917,25 +951,6 @@ class CChannelInfo : public CChannelCoreInfo iChanID ( NiID ), iIpAddr ( NiIP ) {} - QString GenNameForDisplay() const - { - // if text is empty, show IP address instead - if ( strName.isEmpty() ) - { - // convert IP address to text and show it (use dummy port number - // since it is not used here) - const CHostAddress TempAddr = - CHostAddress ( QHostAddress ( iIpAddr ), 0 ); - - return TempAddr.toString ( CHostAddress::SM_IP_NO_LAST_BYTE ); - } - else - { - // show name of channel - return strName; - } - } - // ID of the channel int iChanID; @@ -1026,6 +1041,7 @@ class CNetworkTransportProps iNumAudioChannels ( 0 ), iSampleRate ( 0 ), eAudioCodingType ( CT_NONE ), + eFlags ( NF_NONE ), iAudioCodingArg ( 0 ) {} CNetworkTransportProps ( const uint32_t iNBNPS, @@ -1033,14 +1049,14 @@ class CNetworkTransportProps const uint32_t iNNACH, const uint32_t iNSR, const EAudComprType eNACT, - const uint32_t iNVers, + const ENetwFlags eNFlags, const int32_t iNACA ) : iBaseNetworkPacketSize ( iNBNPS ), iBlockSizeFact ( iNBSF ), iNumAudioChannels ( iNNACH ), iSampleRate ( iNSR ), eAudioCodingType ( eNACT ), - iVersion ( iNVers ), + eFlags ( eNFlags ), iAudioCodingArg ( iNACA ) {} uint32_t iBaseNetworkPacketSize; @@ -1048,7 +1064,7 @@ class CNetworkTransportProps uint32_t iNumAudioChannels; uint32_t iSampleRate; EAudComprType eAudioCodingType; - uint32_t iVersion; + ENetwFlags eFlags; int32_t iAudioCodingArg; }; @@ -1060,6 +1076,7 @@ class NetworkUtil static bool ParseNetworkAddress ( QString strAddress, CHostAddress& HostAddress ); + static QString FixAddress ( const QString& strAddress ); static CHostAddress GetLocalAddress(); static QString GetCentralServerAddress ( const ECSAddType eCentralServerAddressType, const QString& strCentralServerAddress ); @@ -1116,37 +1133,43 @@ class CAudioReverb public: CAudioReverb() {} - void Init ( const int iSampleRate, const double rT60 = 1.1 ); + void Init ( const EAudChanConf eNAudioChannelConf, + const int iNStereoBlockSizeSam, + const int iSampleRate, + const float fT60 = 1.1f ); + void Clear(); - void ProcessSample ( int16_t& iInputOutputLeft, - int16_t& iInputOutputRight, - const double dAttenuation ); + void Process ( CVector& vecsStereoInOut, + const bool bReverbOnLeftChan, + const float fAttenuation ); protected: - void setT60 ( const double rT60, const int iSampleRate ); + void setT60 ( const float fT60, const int iSampleRate ); bool isPrime ( const int number ); class COnePole { public: - COnePole() : dA ( 0 ), dB ( 0 ) { Reset(); } - void setPole ( const double dPole ); - double Calc ( const double dIn ); - void Reset() { dLastSample = 0; } + COnePole() : fA ( 0 ), fB ( 0 ) { Reset(); } + void setPole ( const float fPole ); + float Calc ( const float fIn ); + void Reset() { fLastSample = 0; } protected: - double dA; - double dB; - double dLastSample; + float fA; + float fB; + float fLastSample; }; - CFIFO allpassDelays[3]; - CFIFO combDelays[4]; - COnePole combFilters[4]; - CFIFO outLeftDelay; - CFIFO outRightDelay; - double allpassCoefficient; - double combCoefficient[4]; + EAudChanConf eAudioChannelConf; + int iStereoBlockSizeSam; + CFIFO allpassDelays[3]; + CFIFO combDelays[4]; + COnePole combFilters[4]; + CFIFO outLeftDelay; + CFIFO outRightDelay; + float allpassCoefficient; + float combCoefficient[4]; }; @@ -1186,13 +1209,11 @@ class MathUtils // different IIR weights for up and down direction if ( dNewValue < dOldValue ) { - dOldValue = - dOldValue * dWeightDown + ( 1.0 - dWeightDown ) * dNewValue; + dOldValue = dOldValue * dWeightDown + ( 1.0 - dWeightDown ) * dNewValue; } else { - dOldValue = - dOldValue * dWeightUp + ( 1.0 - dWeightUp ) * dNewValue; + dOldValue = dOldValue * dWeightUp + ( 1.0 - dWeightUp ) * dNewValue; } } @@ -1210,33 +1231,34 @@ class MathUtils return round ( dValue + dHysteresis ); } } -}; + // calculate pan gains: in cross fade mode the pan center is attenuated + // by 6 dB, otherwise the center equals full gain for both channels + static inline float GetLeftPan ( const float fPan, const bool bXFade) + { + return bXFade ? 1 - fPan : std::min ( 0.5f, 1 - fPan ) * 2; + } + static inline float GetRightPan ( const float fPan, const bool bXFade) + { + return bXFade ? fPan : std::min ( 0.5f, fPan ) * 2; + } -// Precise time ---------------------------------------------------------------- -// required for ping measurement -class CPreciseTime -{ -public: -#ifdef _WIN32 - // for the Windows version we have to define a minimum timer precision - // -> set it to 1 ms - CPreciseTime() { timeBeginPeriod ( 1 ); } - virtual ~CPreciseTime() { timeEndPeriod ( 1 ); } -#endif - - // precise time (on Windows the QTime is not precise enough) - int elapsed() + // calculate linear gain from fader values which are in dB + static float CalcFaderGain ( const float fValue ) { -#ifdef _WIN32 - return timeGetTime(); -#elif defined ( __APPLE__ ) || defined ( __MACOSX ) - return mach_absolute_time() / 1000000; // convert ns in ms -#else - timespec tp; - clock_gettime ( CLOCK_MONOTONIC, &tp ); - return tp.tv_sec * 1000 + tp.tv_nsec / 1000000; // convert ns in ms and add the seconds part -#endif + // convert actual slider range in gain values + // and normalize so that maximum gain is 1 + const float fInValueRange0_1 = fValue / AUD_MIX_FADER_MAX; + + // map range from 0..1 to range -35..0 dB and calculate linear gain + if ( fValue == 0 ) + { + return 0; // -infinity + } + else + { + return powf ( 10.0f, ( fInValueRange0_1 * 35.0f - 35.0f ) / 20.0f ); + } } }; diff --git a/src/vstmain.cpp b/src/vstmain.cpp index 6766e179cc..6044c909f5 100755 --- a/src/vstmain.cpp +++ b/src/vstmain.cpp @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ diff --git a/src/vstmain.h b/src/vstmain.h index 90e216faee..38edc5e7a8 100755 --- a/src/vstmain.h +++ b/src/vstmain.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ diff --git a/src/vstsound.h b/src/vstsound.h index 284449c9bb..04ea94a675 100755 --- a/src/vstsound.h +++ b/src/vstsound.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ diff --git a/tools/jamulus-docker b/tools/jamulus-docker new file mode 160000 index 0000000000..c268ce2d91 --- /dev/null +++ b/tools/jamulus-docker @@ -0,0 +1 @@ +Subproject commit c268ce2d91081413a162913120a463e4949c328c diff --git a/tools/jamulus-historytool b/tools/jamulus-historytool new file mode 160000 index 0000000000..d5ef80df8f --- /dev/null +++ b/tools/jamulus-historytool @@ -0,0 +1 @@ +Subproject commit d5ef80df8f2f2b31fa20e78343e8e31acf60c009 diff --git a/tools/jamulus-jamexporter b/tools/jamulus-jamexporter new file mode 160000 index 0000000000..aad0012014 --- /dev/null +++ b/tools/jamulus-jamexporter @@ -0,0 +1 @@ +Subproject commit aad00120143448134ac51f4bc07fb6e874744395 diff --git a/tools/jamulus-php b/tools/jamulus-php new file mode 160000 index 0000000000..65be50ca76 --- /dev/null +++ b/tools/jamulus-php @@ -0,0 +1 @@ +Subproject commit 65be50ca766c7a42979b19ac4d978357ad07b408 diff --git a/tools/jamulus-web b/tools/jamulus-web new file mode 160000 index 0000000000..2112478e2e --- /dev/null +++ b/tools/jamulus-web @@ -0,0 +1 @@ +Subproject commit 2112478e2e0fb83a07f592df2168ca938661994f diff --git a/tools/jamulus-wireshark b/tools/jamulus-wireshark new file mode 160000 index 0000000000..593bf49411 --- /dev/null +++ b/tools/jamulus-wireshark @@ -0,0 +1 @@ +Subproject commit 593bf494119df13a0d77512ffb238c90b32b41f5 diff --git a/windows/deploy_windows.ps1 b/windows/deploy_windows.ps1 index 7b7bbdefa7..935c38d4a9 100644 --- a/windows/deploy_windows.ps1 +++ b/windows/deploy_windows.ps1 @@ -1,6 +1,12 @@ param( # Replace default path with system Qt installation folder if necessary - [string] $QtInstallPath = "C:\Qt\5.12.3" + [string] $QtInstallPath = "C:\Qt\5.12.3", + [string] $QtCompile32 = "msvc2019", + [string] $QtCompile64 = "msvc2019_64", + [string] $AsioSDKName = "ASIOSDK2.3.2", + [string] $AsioSDKUrl = "https://www.steinberg.net/sdk_downloads/ASIOSDK2.3.2.zip", + [string] $NsisName = "nsis-3.06.1", + [string] $NsisUrl = "https://jztkft.dl.sourceforge.net/project/nsis/NSIS%203/3.06.1/nsis-3.06.1.zip" ) # Global constants @@ -53,9 +59,13 @@ Function Install-Dependency if (Test-Path -Path "$WindowsPath\$Destination") { return } $TempFileName = [System.IO.Path]::GetTempFileName() + ".zip" + $TempDir = [System.IO.Path]::GetTempPath() + Invoke-WebRequest -Uri $Uri -OutFile $TempFileName - Expand-Archive -Path $TempFileName -DestinationPath $Env:TEMP -Force - Move-Item -Path "$Env:TEMP\$Name" -Destination "$WindowsPath\$Destination" -Force + echo $TempFileName + Expand-Archive -Path $TempFileName -DestinationPath $TempDir -Force + echo $WindowsPath\$Destination + Move-Item -Path "$TempDir\$Name" -Destination "$WindowsPath\$Destination" -Force Remove-Item -Path $TempFileName -Force } @@ -64,10 +74,10 @@ Function Install-Dependencies { Install-PackageProvider -Name "Nuget" -Scope CurrentUser -Force Install-Module -Name "VSSetup" -Scope CurrentUser -Force - Install-Dependency -Uri "http://www.steinberg.net/sdk_downloads/ASIOSDK2.3.2.zip" ` - -Name "ASIOSDK2.3.2" -Destination "ASIOSDK2" - Install-Dependency -Uri "https://netix.dl.sourceforge.net/project/nsis/NSIS%203/3.05/nsis-3.05.zip" ` - -Name "nsis-3.05" -Destination "NSIS" + Install-Dependency -Uri $AsioSDKUrl ` + -Name $AsioSDKName -Destination "ASIOSDK2" + Install-Dependency -Uri $NsisUrl ` + -Name $NsisName -Destination "NSIS" } # Setup environment variables and build tool paths @@ -90,12 +100,12 @@ Function Setup-Build-Environment if ($BuildArch -Eq "x86_64") { $VcVarsBin = "$VsInstallPath\VC\Auxiliary\build\vcvars64.bat" - $QtMsvcSpecPath = "$QtInstallPath\msvc2017_64\bin" + $QtMsvcSpecPath = "$QtInstallPath\$QtCompile64\bin" } else { $VcVarsBin = "$VsInstallPath\VC\Auxiliary\build\vcvars32.bat" - $QtMsvcSpecPath = "$QtInstallPath\msvc2017\bin" + $QtMsvcSpecPath = "$QtInstallPath\$QtCompile32\bin" } # Setup Qt executables paths for later calls diff --git a/windows/installer.nsi b/windows/installer.nsi index 06cfec108b..9c59b0162e 100755 --- a/windows/installer.nsi +++ b/windows/installer.nsi @@ -1,4 +1,4 @@ -; Jamulus NSIS Installer with Modern User Interface +; Jamulus NSIS Installer with Modern User Interface ; Includes !include "x64.nsh" ; 64bit architecture support @@ -33,6 +33,8 @@ BrandingText "${APP_NAME} powers your online jam session" ; Installer graphical element configuration !define MUI_ICON "${WINDOWS_PATH}\mainicon.ico" +!define MUI_UNICON "${WINDOWS_PATH}\uninsticon.ico" +!define SERVER_ICON "${WINDOWS_PATH}\jamulus-server-icon-2020.ico" !define MUI_HEADERIMAGE !define MUI_HEADERIMAGE_BITMAP "${WINDOWS_PATH}\installer-banner.bmp" !define MUI_WELCOMEFINISHPAGE_BITMAP "${WINDOWS_PATH}\installer-welcome.bmp" @@ -109,7 +111,7 @@ LangString RUNNING_APP_MSG ${LANG_ENGLISH} \ ; Add the redistribution license File "/oname=$INSTDIR\COPYING" "${ROOT_PATH}\COPYING" - + File "/oname=$INSTDIR\servericon.ico" "${SERVER_ICON}" ; Cleanup !delfile "${files}" !undef files @@ -131,10 +133,10 @@ LangString RUNNING_APP_MSG ${LANG_ENGLISH} \ WriteUninstaller "$INSTDIR\${UNINSTALL_EXE}" ; Add the Start Menu and desktop shortcuts - CreateShortCut "$DESKTOP\${APP_NAME}.lnk" "$OUTDIR\${APP_EXE}" + CreateShortCut "$DESKTOP\${APP_NAME}.lnk" "$INSTDIR\${APP_EXE}" CreateDirectory "$SMPROGRAMS\${APP_NAME}" CreateShortCut "$SMPROGRAMS\${APP_NAME}\${APP_NAME}.lnk" "$INSTDIR\${APP_EXE}" - CreateShortCut "$SMPROGRAMS\${APP_NAME}\${APP_NAME} Server.lnk" "$INSTDIR\${APP_EXE}" "-s" + CreateShortCut "$SMPROGRAMS\${APP_NAME}\${APP_NAME} Server.lnk" "$INSTDIR\${APP_EXE}" "-s" "$INSTDIR\servericon.ico" CreateShortCut "$SMPROGRAMS\${APP_NAME}\${APP_NAME} Uninstall.lnk" "$INSTDIR\${UNINSTALL_EXE}" !macroend @@ -236,6 +238,7 @@ FunctionEnd !include "${files}" Delete "$INSTDIR\COPYING" + Delete "$INSTDIR\servericon.ico" Delete "$INSTDIR\${UNINSTALL_EXE}" RMDir "$INSTDIR" diff --git a/windows/jamulus-server-icon-2020.ico b/windows/jamulus-server-icon-2020.ico new file mode 100644 index 0000000000..11127554fb Binary files /dev/null and b/windows/jamulus-server-icon-2020.ico differ diff --git a/windows/mainicon.ico b/windows/mainicon.ico old mode 100755 new mode 100644 index 4ae8d8c9e6..3ad9d05523 Binary files a/windows/mainicon.ico and b/windows/mainicon.ico differ diff --git a/windows/sound.cpp b/windows/sound.cpp index 82cf37b011..c112eb52c1 100755 --- a/windows/sound.cpp +++ b/windows/sound.cpp @@ -21,7 +21,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -40,10 +40,26 @@ CSound* pSound; /******************************************************************************\ * Common * \******************************************************************************/ -QString CSound::LoadAndInitializeDriver ( int iDriverIdx, - bool bOpenDriverSetup ) +QString CSound::LoadAndInitializeDriver ( QString strDriverName, + bool bOpenDriverSetup ) { - // load driver + // find and load driver + int iDriverIdx = INVALID_INDEX; // initialize with an invalid index + + for ( int i = 0; i < MAX_NUMBER_SOUND_CARDS; i++ ) + { + if ( strDriverName.compare ( cDriverNames[i] ) == 0 ) + { + iDriverIdx = i; + } + } + + // if the selected driver was not found, return an error message + if ( iDriverIdx == INVALID_INDEX ) + { + return tr ( "The current selected audio device is no longer present in the system." ); + } + loadAsioDriver ( cDriverNames[iDriverIdx] ); if ( ASIOInit ( &driverInfo ) != ASE_OK ) @@ -53,18 +69,22 @@ QString CSound::LoadAndInitializeDriver ( int iDriverIdx, return tr ( "The audio driver could not be initialized." ); } - // check device capabilities if it fullfills our requirements + // check device capabilities if it fulfills our requirements const QString strStat = CheckDeviceCapabilities(); // check if device is capable if ( strStat.isEmpty() ) { - // the device has changed, per definition we reset the channel - // mapping to the defaults (first two available channels) - ResetChannelMapping(); + // only reset the channel mapping if a new device was selected + if ( strCurDevName.compare ( strDriverNames[iDriverIdx] ) != 0 ) + { + // the device has changed, per definition we reset the channel + // mapping to the defaults (first two available channels) + ResetChannelMapping(); - // store ID of selected driver if initialization was successful - lCurDev = iDriverIdx; + // store ID of selected driver if initialization was successful + strCurDevName = cDriverNames[iDriverIdx]; + } } else { @@ -165,7 +185,7 @@ QString CSound::CheckDeviceCapabilities() // support the required sample format. But since we have support for // all known sample types, the following check should always pass and // therefore we throw the error message on any channel which does not - // fullfill the sample format requirement (quick hack solution). + // fulfill the sample format requirement (quick hack solution). if ( !CheckSampleTypeSupported ( channelInfosInput[i].type ) ) { // return error string @@ -195,7 +215,7 @@ QString CSound::CheckDeviceCapabilities() // support the required sample format. But since we have support for // all known sample types, the following check should always pass and // therefore we throw the error message on any channel which does not - // fullfill the sample format requirement (quick hack solution). + // fulfill the sample format requirement (quick hack solution). if ( !CheckSampleTypeSupported ( channelInfosOutput[i].type ) ) { // return error string @@ -439,17 +459,17 @@ int CSound::Init ( const int iNewPrefMonoBufferSize ) { // add the input and output latencies (returned in number of // samples) and calculate the time in ms - dInOutLatencyMs = - ( static_cast ( lInputLatency ) + lOutputLatency ) * + fInOutLatencyMs = + ( static_cast ( lInputLatency ) + lOutputLatency ) * 1000 / SYSTEM_SAMPLE_RATE_HZ; } else { // no latency available - dInOutLatencyMs = 0.0; + fInOutLatencyMs = 0.0f; } - // check wether the driver requires the ASIOOutputReady() optimization + // check whether the driver requires the ASIOOutputReady() optimization // (can be used by the driver to reduce output latency by one block) bASIOPostOutput = ( ASIOOutputReady() == ASE_OK ); } @@ -485,14 +505,14 @@ void CSound::Stop() CSound::CSound ( void (*fpNewCallback) ( CVector& psData, void* arg ), void* arg, - const int iCtrlMIDIChannel, + const QString& strMIDISetup, const bool , const QString& ) : - CSoundBase ( "ASIO", true, fpNewCallback, arg, iCtrlMIDIChannel ), + CSoundBase ( "ASIO", fpNewCallback, arg, strMIDISetup ), lNumInChan ( 0 ), lNumInChanPlusAddChan ( 0 ), lNumOutChan ( 0 ), - dInOutLatencyMs ( 0.0 ), // "0.0" means that no latency value is available + fInOutLatencyMs ( 0.0f ), // "0.0" means that no latency value is available vSelectedInputChannels ( NUM_IN_OUT_CHANNELS ), vSelectedOutputChannels ( NUM_IN_OUT_CHANNELS ) { @@ -533,7 +553,7 @@ CSound::CSound ( void (*fpNewCallback) ( CVector& psData, voi } // init device index as not initialized (invalid) - lCurDev = INVALID_INDEX; + strCurDevName = ""; // init channel mapping ResetChannelMapping(); @@ -637,8 +657,8 @@ void CSound::bufferSwitch ( long index, ASIOBool ) for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) { vecsMultChanAudioSndCrd[2 * iCurSample + i] = - Double2Short ( (double) vecsMultChanAudioSndCrd[2 * iCurSample + i] + - (double) pASIOBufAdd[iCurSample] ); + Float2Short ( (float) vecsMultChanAudioSndCrd[2 * iCurSample + i] + + (float) pASIOBufAdd[iCurSample] ); } } break; @@ -664,8 +684,8 @@ void CSound::bufferSwitch ( long index, ASIOBool ) iCurSam >>= 8; vecsMultChanAudioSndCrd[2 * iCurSample + i] = - Double2Short ( (double) vecsMultChanAudioSndCrd[2 * iCurSample + i] + - (double) static_cast ( iCurSam ) ); + Float2Short ( (float) vecsMultChanAudioSndCrd[2 * iCurSample + i] + + (float) static_cast ( iCurSam ) ); } } break; @@ -688,8 +708,8 @@ void CSound::bufferSwitch ( long index, ASIOBool ) for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) { vecsMultChanAudioSndCrd[2 * iCurSample + i] = - Double2Short ( (double) vecsMultChanAudioSndCrd[2 * iCurSample + i] + - (double) static_cast ( pASIOBufAdd[iCurSample] >> 16 ) ); + Float2Short ( (float) vecsMultChanAudioSndCrd[2 * iCurSample + i] + + (float) static_cast ( pASIOBufAdd[iCurSample] >> 16 ) ); } } break; diff --git a/windows/sound.h b/windows/sound.h index 46e078e710..6a12bda40a 100755 --- a/windows/sound.h +++ b/windows/sound.h @@ -18,7 +18,7 @@ * * 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 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * \******************************************************************************/ @@ -48,7 +48,7 @@ class CSound : public CSoundBase public: CSound ( void (*fpNewCallback) ( CVector& psData, void* arg ), void* arg, - const int iCtrlMIDIChannel, + const QString& strMIDISetup, const bool , const QString& ); @@ -75,11 +75,11 @@ class CSound : public CSoundBase virtual int GetLeftOutputChannel() { return vSelectedOutputChannels[0]; } virtual int GetRightOutputChannel() { return vSelectedOutputChannels[1]; } - virtual double GetInOutLatencyMs() { return dInOutLatencyMs; } + virtual float GetInOutLatencyMs() { return fInOutLatencyMs; } protected: - virtual QString LoadAndInitializeDriver ( int iIdx, - bool bOpenDriverSetup ); + virtual QString LoadAndInitializeDriver ( QString strDriverName, + bool bOpenDriverSetup ); virtual void UnloadCurrentDriver(); int GetActualBufferSize ( const int iDesiredBufferSizeMono ); QString CheckDeviceCapabilities(); @@ -93,7 +93,7 @@ class CSound : public CSoundBase long lNumInChan; long lNumInChanPlusAddChan; // includes additional "added" channels long lNumOutChan; - double dInOutLatencyMs; + float fInOutLatencyMs; CVector vSelectedInputChannels; CVector vSelectedOutputChannels; diff --git a/windows/uninsticon.ico b/windows/uninsticon.ico new file mode 100644 index 0000000000..94135ef219 Binary files /dev/null and b/windows/uninsticon.ico differ

" + tr ( "Qt cross-platform application framework" ) + ", http://www.qt.io