jamulus/src/util.cpp
Volker Fischer 0c928c0a30 merge fixes
2020-08-11 17:56:36 +02:00

1623 lines
66 KiB
C++
Executable file

/******************************************************************************\
* Copyright (c) 2004-2020
*
* 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
*
\******************************************************************************/
#include "util.h"
#include "client.h"
/* Implementation *************************************************************/
// Input level meter implementation --------------------------------------------
void CStereoSignalLevelMeter::Update ( const CVector<short>& vecsAudio,
const int iMonoBlockSizeSam,
const bool bIsStereoIn )
{
// Get maximum of current block
//
// Speed optimization:
// - 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 sMinLOrMono = 0;
short sMinR = 0;
if ( bIsStereoIn )
{
// 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] );
}
// 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] );
}
}
// 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 double dMax )
{
// decrease max with time
if ( dCurLevel >= METER_FLY_BACK )
{
dCurLevel *= dSmoothingFactor;
}
else
{
dCurLevel = 0;
}
// update current level -> only use maximum
if ( dMax > dCurLevel )
{
return dMax;
}
else
{
return dCurLevel;
}
}
double CStereoSignalLevelMeter::CalcLogResultForMeter ( const double& dLinearLevel )
{
const double dNormLevel = dLinearLevel / _MAXSHORT;
// logarithmic measure
double dLevelForMeterdB = -100000.0; // large negative value
if ( dNormLevel > 0 )
{
dLevelForMeterdB = 20.0 * log10 ( dNormLevel );
}
// 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 )
{
dLevelForMeterdB = 0;
}
return dLevelForMeterdB;
}
// CRC -------------------------------------------------------------------------
void CCRC::Reset()
{
// init state shift-register with ones. Set all registers to "1" with
// bit-wise not operation
iStateShiftReg = ~uint32_t ( 0 );
}
void CCRC::AddByte ( const uint8_t byNewInput )
{
for ( int i = 0; i < 8; i++ )
{
// 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, implicitly a "0" is added)
if ( ( iStateShiftReg & iBitOutMask) > 0 )
{
iStateShiftReg |= 1;
}
// add new data bit to the LSB
if ( ( byNewInput & ( 1 << ( 8 - i - 1 ) ) ) > 0 )
{
iStateShiftReg ^= 1;
}
// add mask to shift-register if first bit is true
if ( iStateShiftReg & 1 )
{
iStateShiftReg ^= iPoly;
}
}
}
uint32_t CCRC::GetCRC()
{
// return inverted shift-register (1's complement)
iStateShiftReg = ~iStateShiftReg;
// remove bit which where shifted out of the shift-register frame
return iStateShiftReg & ( iBitOutMask - 1 );
}
/******************************************************************************\
* Audio Reverberation *
\******************************************************************************/
/*
The following code is based on "JCRev: John Chowning's reverberator class"
by Perry R. Cook and Gary P. Scavone, 1995 - 2004
which is in "The Synthesis ToolKit in C++ (STK)"
http://ccrma.stanford.edu/software/stk
Original description:
This class is derived from the CLM JCRev function, which is based on the use
of networks of simple allpass and comb delay filters. This class implements
three series allpass units, followed by four parallel comb filters, and two
decorrelation delay lines in parallel at the output.
*/
void CAudioReverb::Init ( const EAudChanConf eNAudioChannelConf,
const int iNStereoBlockSizeSam,
const int iSampleRate,
const double rT60 )
{
// 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<double> ( iSampleRate ) / 44100.0;
if ( scaler != 1.0 )
{
for ( int i = 0; i < 9; i++ )
{
int delay = static_cast<int> ( floor ( scaler * lengths[i] ) );
if ( ( delay & 1 ) == 0 )
{
delay++;
}
while ( !isPrime ( delay ) )
{
delay += 2;
}
lengths[i] = delay;
}
}
for ( int i = 0; i < 3; i++ )
{
allpassDelays[i].Init ( lengths[i + 4] );
}
for ( int i = 0; i < 4; i++ )
{
combDelays[i].Init ( lengths[i] );
combFilters[i].setPole ( 0.2 );
}
setT60 ( rT60, iSampleRate );
outLeftDelay.Init ( lengths[7] );
outRightDelay.Init ( lengths[8] );
allpassCoefficient = 0.7;
Clear();
}
bool CAudioReverb::isPrime ( const int number )
{
/*
Returns true if argument value is prime. Taken from "class Effect" in
"STK abstract effects parent class".
*/
if ( number == 2 )
{
return true;
}
if ( number & 1 )
{
for ( int i = 3; i < static_cast<int> ( sqrt ( static_cast<double> ( number ) ) ) + 1; i += 2 )
{
if ( ( number % i ) == 0 )
{
return false;
}
}
return true; // prime
}
else
{
return false; // even
}
}
void CAudioReverb::Clear()
{
// reset and clear all internal state
allpassDelays[0].Reset ( 0 );
allpassDelays[1].Reset ( 0 );
allpassDelays[2].Reset ( 0 );
combDelays[0].Reset ( 0 );
combDelays[1].Reset ( 0 );
combDelays[2].Reset ( 0 );
combDelays[3].Reset ( 0 );
combFilters[0].Reset();
combFilters[1].Reset();
combFilters[2].Reset();
combFilters[3].Reset();
outRightDelay.Reset ( 0 );
outLeftDelay.Reset ( 0 );
}
void CAudioReverb::setT60 ( const double rT60,
const int iSampleRate )
{
// set the reverberation T60 decay time
for ( int i = 0; i < 4; i++ )
{
combCoefficient[i] = pow ( 10.0, static_cast<double> ( -3.0 *
combDelays[i].Size() / ( rT60 * iSampleRate ) ) );
}
}
void CAudioReverb::COnePole::setPole ( const double dPole )
{
// calculate IIR filter coefficients based on the pole value
dA = -dPole;
dB = 1.0 - dPole;
}
double CAudioReverb::COnePole::Calc ( const double dIn )
{
// calculate IIR filter
dLastSample = dB * dIn - dA * dLastSample;
return dLastSample;
}
void CAudioReverb::Process ( CVector<int16_t>& vecsStereoInOut,
const bool bReverbOnLeftChan,
const double dAttenuation )
{
double dMixedInput, 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 )
{
dMixedInput = 0.5 * ( vecsStereoInOut[i] + vecsStereoInOut[i + 1] );
}
else
{
if ( bReverbOnLeftChan )
{
dMixedInput = vecsStereoInOut[i];
}
else
{
dMixedInput = vecsStereoInOut[i + 1];
}
}
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 (for stereo always apply
// reverberation effect on both channels)
if ( ( eAudioChannelConf == CC_STEREO ) || bReverbOnLeftChan )
{
vecsStereoInOut[i] = Double2Short (
( 1.0 - dAttenuation ) * vecsStereoInOut[i] +
0.5 * dAttenuation * outLeftDelay.Get() );
}
if ( ( eAudioChannelConf == CC_STEREO ) || !bReverbOnLeftChan )
{
vecsStereoInOut[i + 1] = Double2Short (
( 1.0 - dAttenuation ) * vecsStereoInOut[i + 1] +
0.5 * dAttenuation * outRightDelay.Get() );
}
}
}
/******************************************************************************\
* GUI Utilities *
\******************************************************************************/
// About dialog ----------------------------------------------------------------
#ifndef HEADLESS
CAboutDlg::CAboutDlg ( QWidget* parent ) : QDialog ( parent )
{
setupUi ( this );
// general description of software
txvAbout->setText (
"<p>" + tr ( "This app enables musicians to perform real-time jam sessions "
"over the internet." ) + "<br>" + 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." ) + "</p>"
"<p><font face=\"courier\">" // GPL header text
"This program is free software; you can redistribute it and/or modify "
"it under the terms of the GNU General Public License as published by "
"the Free Software Foundation; either version 2 of the License, or "
"(at your option) any later version.<br>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.<br>You should have received a copy of the GNU General Public "
"License along with his program; if not, write to the Free Software "
"Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 "
"USA"
"</font></p>" );
// libraries used by this compilation
txvLibraries->setText (
tr ( "This app uses the following libraries, resources or code snippets:" ) +
"<br><p>" + tr ( "Qt cross-platform application framework" ) +
", <i><a href=""http://www.qt.io"">http://www.qt.io</a></i></p>"
"<p>Opus Interactive Audio Codec"
", <i><a href=""http://www.opus-codec.org"">http://www.opus-codec.org</a></i></p>"
"<p>" + tr ( "Audio reverberation code by Perry R. Cook and Gary P. Scavone" ) +
", 1995 - 2004, <i><a href=""http://ccrma.stanford.edu/software/stk"">"
"The Synthesis ToolKit in C++ (STK)</a></i></p>"
"<p>" + tr ( "Some pixmaps are from the" ) + " Open Clip Art Library (OCAL), "
"<i><a href=""http://openclipart.org"">http://openclipart.org</a></i></p>"
"<p>" + tr ( "Country flag icons by Mark James" ) +
", <i><a href=""http://www.famfamfam.com"">http://www.famfamfam.com</a></i></p>" );
// contributors list
txvContributors->setText (
"<p>Peter L. Jones (<a href=""https://github.com/pljones"">pljones</a>)</p>"
"<p>Jonathan Baker-Bates (<a href=""https://github.com/gilgongo"">gilgongo</a>)</p>"
"<p>Daniele Masato (<a href=""https://github.com/doloopuntil"">doloopuntil</a>)</p>"
"<p>Martin Schilde (<a href=""https://github.com/geheimerEichkater"">geheimerEichkater</a>)</p>"
"<p>Simon Tomlinson (<a href=""https://github.com/sthenos"">sthenos</a>)</p>"
"<p>Marc jr. Landolt (<a href=""https://github.com/braindef"">braindef</a>)</p>"
"<p>Olivier Humbert (<a href=""https://github.com/trebmuh"">trebmuh</a>)</p>"
"<p>Tarmo Johannes (<a href=""https://github.com/tarmoj"">tarmoj</a>)</p>"
"<p>mirabilos (<a href=""https://github.com/mirabilos"">mirabilos</a>)</p>"
"<p>Hector Martin (<a href=""https://github.com/marcan"">marcan</a>)</p>"
"<p>newlaurent62 (<a href=""https://github.com/newlaurent62"">newlaurent62</a>)</p>"
"<p>AronVietti (<a href=""https://github.com/AronVietti"">AronVietti</a>)</p>"
"<p>Emlyn Bolton (<a href=""https://github.com/emlynmac"">emlynmac</a>)</p>"
"<p>Jos van den Oever (<a href=""https://github.com/vandenoever"">vandenoever</a>)</p>"
"<p>Tormod Volden (<a href=""https://github.com/tormodvolden"">tormodvolden</a>)</p>"
"<p>Alberstein8 (<a href=""https://github.com/Alberstein8"">Alberstein8</a>)</p>"
"<p>Gauthier Fleutot Östervall (<a href=""https://github.com/fleutot"">fleutot</a>)</p>"
"<p>Stanislas Michalak (<a href=""https://github.com/stanislas-m"">stanislas-m</a>)</p>"
"<p>JP Cimalando (<a href=""https://github.com/jpcima"">jpcima</a>)</p>"
"<p>Adam Sampson (<a href=""https://github.com/atsampson"">atsampson</a>)</p>"
"<p>Stefan Weil (<a href=""https://github.com/stweil"">stweil</a>)</p>"
"<p>Nils Brederlow (<a href=""https://github.com/dingodoppelt"">dingodoppelt</a>)</p>"
"<p>Sebastian Krzyszkowiak (<a href=""https://github.com/dos1"">dos1</a>)</p>"
"<p>dszgit (<a href=""https://github.com/dszgit"">dszgit</a>)</p>"
"<p>chigkim (<a href=""https://github.com/chigkim"">chigkim</a>)</p>"
"<p>Bodo (<a href=""https://github.com/bomm"">bomm</a>)</p>"
"<p>jp8 (<a href=""https://github.com/jp8"">jp8</a>)</p>"
"<p>bspeer (<a href=""https://github.com/bspeer"">bspeer</a>)</p>"
"<br>" + tr ( "For details on the contributions check out the " ) +
"<a href=""https://github.com/corrados/jamulus/graphs/contributors"">" + tr ( "Github Contributors list" ) + "</a>." );
// translators
txvTranslation->setText (
"<p><b>" + tr ( "Spanish" ) + "</b></p>"
"<p>Daryl Hanlon (<a href=""https://github.com/ignotus666"">ignotus666</a>)</p>"
"<p><b>" + tr ( "French" ) + "</b></p>"
"<p>Olivier Humbert (<a href=""https://github.com/trebmuh"">trebmuh</a>)</p>"
"<p><b>" + tr ( "Portuguese" ) + "</b></p>"
"<p>Miguel de Matos (<a href=""https://github.com/Snayler"">Snayler</a>)</p>"
"<p>Melcon Moraes (<a href=""https://github.com/melcon"">melcon</a>)</p>"
"<p><b>" + tr ( "Dutch" ) + "</b></p>"
"<p>Jeroen Geertzen (<a href=""https://github.com/jerogee"">jerogee</a>)</p>"
"<p><b>" + tr ( "Italian" ) + "</b></p>"
"<p>Giuseppe Sapienza (<a href=""https://github.com/dzpex"">dzpex</a>)</p>"
"<p><b>" + tr ( "German" ) + "</b></p>"
"<p>Volker Fischer (<a href=""https://github.com/corrados"">corrados</a>)</p>"
"<p><b>" + tr ( "Polish" ) + "</b></p>"
"<p>Martyna Danysz (<a href=""https://github.com/Martyna27"">Martyna27</a>)</p>"
"<p><b>" + tr ( "Swedish" ) + "</b></p>"
"<p>Daniel (<a href=""https://github.com/genesisproject2020"">genesisproject2020</a>)</p>");
// set version number in about dialog
lblVersion->setText ( GetVersionAndNameStr() );
// set window title
setWindowTitle ( tr ( "About " ) + APP_NAME );
}
// Licence dialog --------------------------------------------------------------
CLicenceDlg::CLicenceDlg ( QWidget* parent ) : QDialog ( parent )
{
/*
The licence dialog is structured as follows:
- text box with the licence text on the top
- check box: I &agree to the above licence terms
- Accept button (disabled if check box not checked)
- 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 );
butAccept = new QPushButton ( tr ( "Accept" ), this );
QPushButton* butDecline = new QPushButton ( tr ( "Decline" ), this );
pSubLayout->addStretch();
pSubLayout->addWidget ( chbAgree );
pSubLayout->addWidget ( butAccept );
pSubLayout->addWidget ( butDecline );
pLayout->addWidget ( txvLicence );
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 (
"<p><big>" + tr (
"By connecting to this server and agreeing to this notice, you agree to the "
"following:" ) + "</big></p><p><big>" + 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 " ) +
"<i><a href=""http://creativecommons.org/licenses/by-nc-sa/4.0"">"
"http://creativecommons.org/licenses/by-nc-sa/4.0</a></i>):</big></p>"
"<h3>Attribution-NonCommercial-ShareAlike 4.0</h3>"
"<p>" + tr ( "You are free to:" ) +
"<ul>"
"<li><b>" + tr ( "Share" ) + "</b> - " +
tr ( "copy and redistribute the material in any medium or format" ) + "</li>"
"<li><b>" + tr ( "Adapt" ) + "</b> - " +
tr ( "remix, transform, and build upon the material" ) + "</li>"
"</ul>" + tr ( "The licensor cannot revoke these freedoms as long as you follow the "
"license terms." ) + "</p>"
"<p>" + tr ( "Under the following terms:" ) +
"<ul>"
"<li><b>" + tr ( "Attribution" ) + "</b> - " +
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." ) + "</li>"
"<li><b>" + tr ( "NonCommercial" ) + "</b> - " +
tr ( "You may not use the material for commercial purposes." ) + "</li>"
"<li><b>" + tr ( "ShareAlike" ) + "</b> - " +
tr ( "If you remix, transform, or build upon the material, you must distribute your "
"contributions under the same license as the original." ) + "</li>"
"</ul><b>" + tr ( "No additional restrictions" ) + "</b> — " +
tr ( "You may not apply legal terms or technological measures that legally restrict "
"others from doing anything the license permits." ) + "</p>" );
QObject::connect ( chbAgree, &QCheckBox::stateChanged,
this, &CLicenceDlg::OnAgreeStateChanged );
QObject::connect ( butAccept, &QPushButton::clicked,
this, &CLicenceDlg::accept );
QObject::connect ( butDecline, &QPushButton::clicked,
this, &CLicenceDlg::reject );
}
// Musician profile dialog -----------------------------------------------------
CMusProfDlg::CMusProfDlg ( CClient* pNCliP,
QWidget* parent ) :
QDialog ( parent ),
pClient ( pNCliP )
{
/*
The musician profile dialog is structured as follows:
- label with edit box for alias/name
- label with combo box for instrument
- label with combo box for country flag
- label with edit box for city
- label with combo box for skill level
- OK button
*/
setWindowTitle ( tr ( "Musician Profile" ) );
setWindowIcon ( QIcon ( QString::fromUtf8 ( ":/png/main/res/fronticon.png" ) ) );
QVBoxLayout* pLayout = new QVBoxLayout ( this );
QHBoxLayout* pButSubLayout = new QHBoxLayout;
QLabel* plblAlias = new QLabel ( tr ( "Alias/Name" ), this );
pedtAlias = new QLineEdit ( this );
QLabel* plblInstrument = new QLabel ( tr ( "Instrument" ), this );
pcbxInstrument = new QComboBox ( this );
QLabel* plblCountry = new QLabel ( tr ( "Country" ), this );
pcbxCountry = new QComboBox ( this );
QLabel* plblCity = new QLabel ( tr ( "City" ), this );
pedtCity = new QLineEdit ( this );
QLabel* plblSkill = new QLabel ( tr ( "Skill" ), this );
pcbxSkill = new QComboBox ( this );
QPushButton* butClose = new QPushButton ( tr ( "&Close" ), this );
QGridLayout* pGridLayout = new QGridLayout;
plblAlias->setSizePolicy ( QSizePolicy::Minimum, QSizePolicy::Expanding );
pGridLayout->addWidget ( plblAlias, 0, 0 );
pGridLayout->addWidget ( pedtAlias, 0, 1 );
plblInstrument->setSizePolicy ( QSizePolicy::Minimum, QSizePolicy::Expanding );
pGridLayout->addWidget ( plblInstrument, 1, 0 );
pGridLayout->addWidget ( pcbxInstrument, 1, 1 );
plblCountry->setSizePolicy ( QSizePolicy::Minimum, QSizePolicy::Expanding );
pGridLayout->addWidget ( plblCountry, 2, 0 );
pGridLayout->addWidget ( pcbxCountry, 2, 1 );
plblCity->setSizePolicy ( QSizePolicy::Minimum, QSizePolicy::Expanding );
pGridLayout->addWidget ( plblCity, 3, 0 );
pGridLayout->addWidget ( pedtCity, 3, 1 );
plblSkill->setSizePolicy ( QSizePolicy::Minimum, QSizePolicy::Expanding );
pGridLayout->addWidget ( plblSkill, 4, 0 );
pGridLayout->addWidget ( pcbxSkill, 4, 1 );
pButSubLayout->addStretch();
pButSubLayout->addWidget ( butClose );
pLayout->addLayout ( pGridLayout );
pLayout->addLayout ( pButSubLayout );
// we do not want to close button to be a default one (the mouse pointer
// may jump to that button which we want to avoid)
butClose->setAutoDefault ( false );
butClose->setDefault ( false );
// Instrument pictures combo box -------------------------------------------
// add an entry for all known instruments
for ( int iCurInst = 0; iCurInst < CInstPictures::GetNumAvailableInst(); iCurInst++ )
{
// create a combo box item with text, image and background color
QColor InstrColor;
pcbxInstrument->addItem ( QIcon ( CInstPictures::GetResourceReference ( iCurInst ) ),
CInstPictures::GetName ( iCurInst ),
iCurInst );
switch ( CInstPictures::GetCategory ( iCurInst ) )
{
case CInstPictures::IC_OTHER_INSTRUMENT: InstrColor = QColor ( Qt::blue ); break;
case CInstPictures::IC_WIND_INSTRUMENT: InstrColor = QColor ( Qt::green ); break;
case CInstPictures::IC_STRING_INSTRUMENT: InstrColor = QColor ( Qt::red ); break;
case CInstPictures::IC_PLUCKING_INSTRUMENT: InstrColor = QColor ( Qt::cyan ); break;
case CInstPictures::IC_PERCUSSION_INSTRUMENT: InstrColor = QColor ( Qt::white ); break;
case CInstPictures::IC_KEYBOARD_INSTRUMENT: InstrColor = QColor ( Qt::yellow ); break;
case CInstPictures::IC_MULTIPLE_INSTRUMENT: InstrColor = QColor ( Qt::black ); break;
}
InstrColor.setAlpha ( 10 );
pcbxInstrument->setItemData ( iCurInst, InstrColor, Qt::BackgroundRole );
}
// sort the items in alphabetical order
pcbxInstrument->model()->sort ( 0 );
// Country flag icons combo box --------------------------------------------
// add an entry for all known country flags
for ( int iCurCntry = static_cast<int> ( QLocale::AnyCountry );
iCurCntry < static_cast<int> ( QLocale::LastCountry ); iCurCntry++ )
{
// exclude the "None" entry since it is added after the sorting
if ( static_cast<QLocale::Country> ( iCurCntry ) != QLocale::AnyCountry )
{
// get current country enum
QLocale::Country eCountry = static_cast<QLocale::Country> ( iCurCntry );
// try to load icon from resource file name
QIcon CurFlagIcon;
CurFlagIcon.addFile ( CLocale::GetCountryFlagIconsResourceReference ( eCountry ) );
// only add the entry if a flag is available
if ( !CurFlagIcon.isNull() )
{
// create a combo box item with text and image
pcbxCountry->addItem ( QIcon ( CurFlagIcon ),
QLocale::countryToString ( eCountry ),
iCurCntry );
}
}
}
// sort country combo box items in alphabetical order
pcbxCountry->model()->sort ( 0, Qt::AscendingOrder );
// the "None" country gets a special icon and is the very first item
QIcon FlagNoneIcon;
FlagNoneIcon.addFile ( ":/png/flags/res/flags/flagnone.png" );
pcbxCountry->insertItem ( 0,
FlagNoneIcon,
tr ( "None" ),
static_cast<int> ( QLocale::AnyCountry ) );
// Skill level combo box ---------------------------------------------------
// create a pixmap showing the skill level colors
QPixmap SLPixmap ( 16, 11 ); // same size as the country flags
SLPixmap.fill ( QColor::fromRgb ( RGBCOL_R_SL_NOT_SET,
RGBCOL_G_SL_NOT_SET,
RGBCOL_B_SL_NOT_SET ) );
pcbxSkill->addItem ( QIcon ( SLPixmap ), tr ( "None" ), SL_NOT_SET );
SLPixmap.fill ( QColor::fromRgb ( RGBCOL_R_SL_BEGINNER,
RGBCOL_G_SL_BEGINNER,
RGBCOL_B_SL_BEGINNER ) );
pcbxSkill->addItem ( QIcon ( SLPixmap ), tr ( "Beginner" ), SL_BEGINNER );
SLPixmap.fill ( QColor::fromRgb ( RGBCOL_R_SL_INTERMEDIATE,
RGBCOL_G_SL_INTERMEDIATE,
RGBCOL_B_SL_INTERMEDIATE ) );
pcbxSkill->addItem ( QIcon ( SLPixmap ), tr ( "Intermediate" ), SL_INTERMEDIATE );
SLPixmap.fill ( QColor::fromRgb ( RGBCOL_R_SL_SL_PROFESSIONAL,
RGBCOL_G_SL_SL_PROFESSIONAL,
RGBCOL_B_SL_SL_PROFESSIONAL ) );
pcbxSkill->addItem ( QIcon ( SLPixmap ), tr ( "Expert" ), SL_PROFESSIONAL );
// Add help text to controls -----------------------------------------------
// fader tag
QString strFaderTag = "<b>" + tr ( "Musician Profile" ) + ":</b> " +
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." ) +
"<br>" + 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" ) );
pcbxInstrument->setWhatsThis ( strFaderTag );
pcbxInstrument->setAccessibleName ( tr ( "Instrument picture button" ) );
pcbxCountry->setWhatsThis ( strFaderTag );
pcbxCountry->setAccessibleName ( tr ( "Country flag button" ) );
pedtCity->setWhatsThis ( strFaderTag );
pedtCity->setAccessibleName ( tr ( "City edit box" ) );
pcbxSkill->setWhatsThis ( strFaderTag );
pcbxSkill->setAccessibleName ( tr ( "Skill level combo box" ) );
// Connections -------------------------------------------------------------
QObject::connect ( pedtAlias, &QLineEdit::textChanged,
this, &CMusProfDlg::OnAliasTextChanged );
QObject::connect ( pcbxInstrument, static_cast<void (QComboBox::*) ( int )> ( &QComboBox::activated ),
this, &CMusProfDlg::OnInstrumentActivated );
QObject::connect ( pcbxCountry, static_cast<void (QComboBox::*) ( int )> ( &QComboBox::activated ),
this, &CMusProfDlg::OnCountryActivated );
QObject::connect ( pedtCity, &QLineEdit::textChanged,
this, &CMusProfDlg::OnCityTextChanged );
QObject::connect ( pcbxSkill, static_cast<void (QComboBox::*) ( int )> ( &QComboBox::activated ),
this, &CMusProfDlg::OnSkillActivated );
QObject::connect ( butClose, &QPushButton::clicked,
this, &CMusProfDlg::accept );
}
void CMusProfDlg::showEvent ( QShowEvent* )
{
// Update the controls with the current client settings --------------------
// set the name
pedtAlias->setText ( pClient->ChannelInfo.strName );
// select current instrument
pcbxInstrument->setCurrentIndex (
pcbxInstrument->findData ( pClient->ChannelInfo.iInstrument ) );
// select current country
pcbxCountry->setCurrentIndex (
pcbxCountry->findData (
static_cast<int> ( pClient->ChannelInfo.eCountry ) ) );
// set the city
pedtCity->setText ( pClient->ChannelInfo.strCity );
// select the skill level
pcbxSkill->setCurrentIndex (
pcbxSkill->findData (
static_cast<int> ( pClient->ChannelInfo.eSkillLevel ) ) );
}
void CMusProfDlg::OnAliasTextChanged ( const QString& strNewName )
{
// check length
if ( strNewName.length() <= MAX_LEN_FADER_TAG )
{
// refresh internal name parameter
pClient->ChannelInfo.strName = strNewName;
// update channel info at the server
pClient->SetRemoteInfo();
}
else
{
// text is too long, update control with shortened text
pedtAlias->setText ( strNewName.left ( MAX_LEN_FADER_TAG ) );
}
}
void CMusProfDlg::OnInstrumentActivated ( int iCntryListItem )
{
// set the new value in the data base
pClient->ChannelInfo.iInstrument =
pcbxInstrument->itemData ( iCntryListItem ).toInt();
// update channel info at the server
pClient->SetRemoteInfo();
}
void CMusProfDlg::OnCountryActivated ( int iCntryListItem )
{
// set the new value in the data base
pClient->ChannelInfo.eCountry = static_cast<QLocale::Country> (
pcbxCountry->itemData ( iCntryListItem ).toInt() );
// update channel info at the server
pClient->SetRemoteInfo();
}
void CMusProfDlg::OnCityTextChanged ( const QString& strNewCity )
{
// check length
if ( strNewCity.length() <= MAX_LEN_SERVER_CITY )
{
// refresh internal name parameter
pClient->ChannelInfo.strCity = strNewCity;
// update channel info at the server
pClient->SetRemoteInfo();
}
else
{
// text is too long, update control with shortened text
pedtCity->setText ( strNewCity.left ( MAX_LEN_SERVER_CITY ) );
}
}
void CMusProfDlg::OnSkillActivated ( int iCntryListItem )
{
// set the new value in the data base
pClient->ChannelInfo.eSkillLevel = static_cast<ESkillLevel> (
pcbxSkill->itemData ( iCntryListItem ).toInt() );
// update channel info at the server
pClient->SetRemoteInfo();
}
// Help menu -------------------------------------------------------------------
CHelpMenu::CHelpMenu ( const bool bIsClient, QWidget* parent ) : QMenu ( tr ( "&Help" ), parent )
{
// standard help menu consists of about and what's this help
if ( bIsClient )
{
addAction ( tr ( "Getting &Started..." ), this, SLOT ( OnHelpClientGetStarted() ) );
addAction ( tr ( "Software &Manual..." ), this, SLOT ( OnHelpSoftwareMan() ) );
}
else
{
addAction ( tr ( "Getting &Started..." ), this, SLOT ( OnHelpServerGetStarted() ) );
}
addSeparator();
addAction ( tr ( "What's &This" ), this, SLOT ( OnHelpWhatsThis() ), QKeySequence ( Qt::SHIFT + Qt::Key_F1 ) );
addSeparator();
addAction ( tr ( "&About..." ), this, SLOT ( OnHelpAbout() ) );
}
// Language combo box ----------------------------------------------------------
CLanguageComboBox::CLanguageComboBox ( QWidget* parent ) :
QComboBox ( parent ),
iIdxSelectedLanguage ( INVALID_INDEX )
{
QObject::connect ( this, static_cast<void (QComboBox::*) ( int )> ( &QComboBox::activated ),
this, &CLanguageComboBox::OnLanguageActivated );
}
void CLanguageComboBox::Init ( QString& strSelLanguage )
{
// load available translations
const QMap<QString, QString> TranslMap = CLocale::GetAvailableTranslations();
QMapIterator<QString, QString> 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 *
\******************************************************************************/
// Network utility functions ---------------------------------------------------
bool NetworkUtil::ParseNetworkAddress ( QString strAddress,
CHostAddress& HostAddress )
{
QHostAddress InetAddr;
quint16 iNetPort = DEFAULT_PORT_NUMBER;
// init requested host address with invalid address first
HostAddress = CHostAddress();
// parse input address for the type "IP4 address:port number" or
// "[IP6 address]:port number" assuming the syntax is correctly given
QStringList slAddress = strAddress.split ( ":" );
QString strSep = ":";
bool bIsIP6 = false;
if ( slAddress.count() > 2 )
{
// IP6 address
bIsIP6 = true;
strSep = "]:";
}
QString strPort = strAddress.section ( strSep, 1, 1 );
if ( !strPort.isEmpty() )
{
// a colon is present in the address string, try to extract port number
iNetPort = strPort.toInt();
// extract address port before separator (should be actual internet address)
strAddress = strAddress.section ( strSep, 0, 0 );
if ( bIsIP6 )
{
// remove "[" at the beginning
strAddress.remove ( 0, 1 );
}
}
// first try if this is an IP number an can directly applied to QHostAddress
if ( !InetAddr.setAddress ( strAddress ) )
{
// 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 )
{
// apply IP address to QT object
if ( !HostInfo.addresses().isEmpty() )
{
// use the first IP address
InetAddr = HostInfo.addresses().first();
}
}
else
{
return false; // invalid address
}
}
HostAddress = CHostAddress ( InetAddr, iNetPort );
return true;
}
CHostAddress NetworkUtil::GetLocalAddress()
{
QTcpSocket socket;
socket.connectToHost ( WELL_KNOWN_HOST, WELL_KNOWN_PORT );
if ( socket.waitForConnected ( IP_LOOKUP_TIMEOUT ) )
{
return CHostAddress ( socket.localAddress(), 0 );
}
else
{
qWarning() << "could not determine local IPv4 address:"
<< socket.errorString()
<< "- using localhost";
return CHostAddress( QHostAddress::LocalHost, 0 );
}
}
QString NetworkUtil::GetCentralServerAddress ( const ECSAddType eCentralServerAddressType,
const QString& strCentralServerAddress )
{
switch ( eCentralServerAddressType )
{
case AT_CUSTOM: return strCentralServerAddress;
case AT_ALL_GENRES: return CENTSERV_ALL_GENRES;
case AT_GENRE_ROCK: return CENTSERV_GENRE_ROCK;
case AT_GENRE_JAZZ: return CENTSERV_GENRE_JAZZ;
case AT_GENRE_CLASSICAL_FOLK: return CENTSERV_GENRE_CLASSICAL_FOLK;
default: return DEFAULT_SERVER_ADDRESS; // AT_DEFAULT
}
}
QString NetworkUtil::FixAddress ( const QString& strAddress )
{
// remove all spaces from the address string
return strAddress.simplified().replace ( " ", "" );
}
// Instrument picture data base ------------------------------------------------
CVector<CInstPictures::CInstPictProps>& CInstPictures::GetTable ( const bool bReGenerateTable )
{
// make sure we generate the table only once
static bool TableIsInitialized = false;
static CVector<CInstPictProps> vecDataBase;
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 ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Electric Guitar" ), ":/png/instr/res/instruments/eguitar.png", IC_PLUCKING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Acoustic Guitar" ), ":/png/instr/res/instruments/aguitar.png", IC_PLUCKING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Bass Guitar" ), ":/png/instr/res/instruments/bassguitar.png", IC_PLUCKING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Keyboard" ), ":/png/instr/res/instruments/keyboard.png", IC_KEYBOARD_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Synthesizer" ), ":/png/instr/res/instruments/synthesizer.png", IC_KEYBOARD_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Grand Piano" ), ":/png/instr/res/instruments/grandpiano.png", IC_KEYBOARD_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Accordion" ), ":/png/instr/res/instruments/accordeon.png", IC_KEYBOARD_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Vocal" ), ":/png/instr/res/instruments/vocal.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Microphone" ), ":/png/instr/res/instruments/microphone.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Harmonica" ), ":/png/instr/res/instruments/harmonica.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Trumpet" ), ":/png/instr/res/instruments/trumpet.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Trombone" ), ":/png/instr/res/instruments/trombone.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "French Horn" ), ":/png/instr/res/instruments/frenchhorn.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Tuba" ), ":/png/instr/res/instruments/tuba.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Saxophone" ), ":/png/instr/res/instruments/saxophone.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Clarinet" ), ":/png/instr/res/instruments/clarinet.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Flute" ), ":/png/instr/res/instruments/flute.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Violin" ), ":/png/instr/res/instruments/violin.png", IC_STRING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Cello" ), ":/png/instr/res/instruments/cello.png", IC_STRING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Double Bass" ), ":/png/instr/res/instruments/doublebass.png", IC_STRING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Recorder" ), ":/png/instr/res/instruments/recorder.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Streamer" ), ":/png/instr/res/instruments/streamer.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Listener" ), ":/png/instr/res/instruments/listener.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Guitar+Vocal" ), ":/png/instr/res/instruments/guitarvocal.png", IC_MULTIPLE_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Keyboard+Vocal" ), ":/png/instr/res/instruments/keyboardvocal.png", IC_MULTIPLE_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Bodhran" ), ":/png/instr/res/instruments/bodhran.png", IC_PERCUSSION_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Bassoon" ), ":/png/instr/res/instruments/bassoon.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Oboe" ), ":/png/instr/res/instruments/oboe.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Harp" ), ":/png/instr/res/instruments/harp.png", IC_STRING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Viola" ), ":/png/instr/res/instruments/viola.png", IC_STRING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Congas" ), ":/png/instr/res/instruments/congas.png", IC_PERCUSSION_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Bongo" ), ":/png/instr/res/instruments/bongo.png", IC_PERCUSSION_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Vocal Bass" ), ":/png/instr/res/instruments/vocalbass.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Vocal Tenor" ), ":/png/instr/res/instruments/vocaltenor.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Vocal Alto" ), ":/png/instr/res/instruments/vocalalto.png", IC_OTHER_INSTRUMENT ) );
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 ) );
// now the table is initialized
TableIsInitialized = true;
}
return vecDataBase;
}
bool CInstPictures::IsInstIndexInRange ( const int iIdx )
{
// check if index is in valid range
return ( iIdx >= 0 ) && ( iIdx < GetTable().Size() );
}
QString CInstPictures::GetResourceReference ( const int iInstrument )
{
// range check
if ( IsInstIndexInRange ( iInstrument ) )
{
// return the string of the resource reference for accessing the picture
return GetTable()[iInstrument].strResourceReference;
}
else
{
return "";
}
}
QString CInstPictures::GetName ( const int iInstrument )
{
// range check
if ( IsInstIndexInRange ( iInstrument ) )
{
// return the name of the instrument
return GetTable()[iInstrument].strName;
}
else
{
return "";
}
}
CInstPictures::EInstCategory CInstPictures::GetCategory ( const int iInstrument )
{
// range check
if ( IsInstIndexInRange ( iInstrument ) )
{
// return the name of the instrument
return GetTable()[iInstrument].eInstCategory;
}
else
{
return IC_OTHER_INSTRUMENT;
}
}
// Locale management class -----------------------------------------------------
QString CLocale::GetCountryFlagIconsResourceReference ( const QLocale::Country eCountry )
{
QString strReturn = "";
// special flag for none
if ( eCountry == QLocale::AnyCountry )
{
strReturn = ":/png/flags/res/flags/flagnone.png";
}
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<int> ( eCountry ) )
{
case 1: strISO3166 = "af"; break;
case 2: strISO3166 = "al"; break;
case 3: strISO3166 = "dz"; break;
case 5: strISO3166 = "ad"; break;
case 6: strISO3166 = "ao"; break;
case 10: strISO3166 = "ar"; break;
case 11: strISO3166 = "am"; break;
case 12: strISO3166 = "aw"; break;
case 13: strISO3166 = "au"; break;
case 14: strISO3166 = "at"; break;
case 15: strISO3166 = "az"; break;
case 17: strISO3166 = "bh"; break;
case 18: strISO3166 = "bd"; break;
case 20: strISO3166 = "by"; break;
case 21: strISO3166 = "be"; break;
case 23: strISO3166 = "bj"; break;
case 25: strISO3166 = "bt"; break;
case 26: strISO3166 = "bo"; break;
case 27: strISO3166 = "ba"; break;
case 28: strISO3166 = "bw"; break;
case 30: strISO3166 = "br"; break;
case 32: strISO3166 = "bn"; break;
case 33: strISO3166 = "bg"; break;
case 34: strISO3166 = "bf"; break;
case 35: strISO3166 = "bi"; break;
case 36: strISO3166 = "kh"; break;
case 37: strISO3166 = "cm"; break;
case 38: strISO3166 = "ca"; break;
case 39: strISO3166 = "cv"; break;
case 41: strISO3166 = "cf"; break;
case 42: strISO3166 = "td"; break;
case 43: strISO3166 = "cl"; break;
case 44: strISO3166 = "cn"; break;
case 47: strISO3166 = "co"; break;
case 48: strISO3166 = "km"; break;
case 49: strISO3166 = "cd"; break;
case 50: strISO3166 = "cg"; break;
case 52: strISO3166 = "cr"; break;
case 53: strISO3166 = "ci"; break;
case 54: strISO3166 = "hr"; break;
case 55: strISO3166 = "cu"; break;
case 56: strISO3166 = "cy"; break;
case 57: strISO3166 = "cz"; break;
case 58: strISO3166 = "dk"; break;
case 59: strISO3166 = "dj"; break;
case 61: strISO3166 = "do"; break;
case 62: strISO3166 = "tl"; break;
case 63: strISO3166 = "ec"; break;
case 64: strISO3166 = "eg"; break;
case 65: strISO3166 = "sv"; break;
case 66: strISO3166 = "gq"; break;
case 67: strISO3166 = "er"; break;
case 68: strISO3166 = "ee"; break;
case 69: strISO3166 = "et"; break;
case 71: strISO3166 = "fo"; break;
case 73: strISO3166 = "fi"; break;
case 74: strISO3166 = "fr"; break;
case 76: strISO3166 = "gf"; break;
case 77: strISO3166 = "pf"; break;
case 79: strISO3166 = "ga"; break;
case 81: strISO3166 = "ge"; break;
case 82: strISO3166 = "de"; break;
case 83: strISO3166 = "gh"; break;
case 85: strISO3166 = "gr"; break;
case 86: strISO3166 = "gl"; break;
case 88: strISO3166 = "gp"; break;
case 90: strISO3166 = "gt"; break;
case 91: strISO3166 = "gn"; break;
case 92: strISO3166 = "gw"; break;
case 93: strISO3166 = "gy"; break;
case 96: strISO3166 = "hn"; break;
case 97: strISO3166 = "hk"; break;
case 98: strISO3166 = "hu"; break;
case 99: strISO3166 = "is"; break;
case 100: strISO3166 = "in"; break;
case 101: strISO3166 = "id"; break;
case 102: strISO3166 = "ir"; break;
case 103: strISO3166 = "iq"; break;
case 104: strISO3166 = "ie"; break;
case 105: strISO3166 = "il"; break;
case 106: strISO3166 = "it"; break;
case 108: strISO3166 = "jp"; break;
case 109: strISO3166 = "jo"; break;
case 110: strISO3166 = "kz"; break;
case 111: strISO3166 = "ke"; break;
case 113: strISO3166 = "kp"; break;
case 114: strISO3166 = "kr"; break;
case 115: strISO3166 = "kw"; break;
case 116: strISO3166 = "kg"; break;
case 117: strISO3166 = "la"; break;
case 118: strISO3166 = "lv"; break;
case 119: strISO3166 = "lb"; break;
case 120: strISO3166 = "ls"; break;
case 122: strISO3166 = "ly"; break;
case 123: strISO3166 = "li"; break;
case 124: strISO3166 = "lt"; break;
case 125: strISO3166 = "lu"; break;
case 126: strISO3166 = "mo"; break;
case 127: strISO3166 = "mk"; break;
case 128: strISO3166 = "mg"; break;
case 130: strISO3166 = "my"; break;
case 132: strISO3166 = "ml"; break;
case 133: strISO3166 = "mt"; break;
case 135: strISO3166 = "mq"; break;
case 136: strISO3166 = "mr"; break;
case 137: strISO3166 = "mu"; break;
case 138: strISO3166 = "yt"; break;
case 139: strISO3166 = "mx"; break;
case 141: strISO3166 = "md"; break;
case 142: strISO3166 = "mc"; break;
case 143: strISO3166 = "mn"; break;
case 145: strISO3166 = "ma"; break;
case 146: strISO3166 = "mz"; break;
case 147: strISO3166 = "mm"; break;
case 148: strISO3166 = "na"; break;
case 150: strISO3166 = "np"; break;
case 151: strISO3166 = "nl"; break;
case 153: strISO3166 = "nc"; break;
case 154: strISO3166 = "nz"; break;
case 155: strISO3166 = "ni"; break;
case 156: strISO3166 = "ne"; break;
case 157: strISO3166 = "ng"; break;
case 161: strISO3166 = "no"; break;
case 162: strISO3166 = "om"; break;
case 163: strISO3166 = "pk"; break;
case 165: strISO3166 = "ps"; break;
case 166: strISO3166 = "pa"; break;
case 167: strISO3166 = "pg"; break;
case 168: strISO3166 = "py"; break;
case 169: strISO3166 = "pe"; break;
case 170: strISO3166 = "ph"; break;
case 172: strISO3166 = "pl"; break;
case 173: strISO3166 = "pt"; break;
case 174: strISO3166 = "pr"; break;
case 175: strISO3166 = "qa"; break;
case 176: strISO3166 = "re"; break;
case 177: strISO3166 = "ro"; break;
case 178: strISO3166 = "ru"; break;
case 179: strISO3166 = "rw"; break;
case 184: strISO3166 = "sm"; break;
case 185: strISO3166 = "st"; break;
case 186: strISO3166 = "sa"; break;
case 187: strISO3166 = "sn"; break;
case 188: strISO3166 = "sc"; break;
case 189: strISO3166 = "sl"; break;
case 190: strISO3166 = "sg"; break;
case 191: strISO3166 = "sk"; break;
case 192: strISO3166 = "si"; break;
case 194: strISO3166 = "so"; break;
case 195: strISO3166 = "za"; break;
case 197: strISO3166 = "es"; break;
case 198: strISO3166 = "lk"; break;
case 201: strISO3166 = "sd"; break;
case 202: strISO3166 = "sr"; break;
case 204: strISO3166 = "sz"; break;
case 205: strISO3166 = "se"; break;
case 206: strISO3166 = "ch"; break;
case 207: strISO3166 = "sy"; break;
case 208: strISO3166 = "tw"; break;
case 209: strISO3166 = "tj"; break;
case 210: strISO3166 = "tz"; break;
case 211: strISO3166 = "th"; break;
case 212: strISO3166 = "tg"; break;
case 214: strISO3166 = "to"; break;
case 216: strISO3166 = "tn"; break;
case 217: strISO3166 = "tr"; break;
case 221: strISO3166 = "ug"; break;
case 222: strISO3166 = "ua"; break;
case 223: strISO3166 = "ae"; break;
case 224: strISO3166 = "gb"; break;
case 225: strISO3166 = "us"; break;
case 227: strISO3166 = "uy"; break;
case 228: strISO3166 = "uz"; break;
case 231: strISO3166 = "ve"; break;
case 232: strISO3166 = "vn"; break;
case 236: strISO3166 = "eh"; break;
case 237: strISO3166 = "ye"; break;
case 239: strISO3166 = "zm"; break;
case 240: strISO3166 = "zw"; break;
case 242: strISO3166 = "me"; break;
case 243: strISO3166 = "rs"; break;
case 248: strISO3166 = "ax"; break;
}
strReturn = ":/png/flags/res/flags/" + strISO3166 + ".png";
// check if file actually exists, if not then invalidate reference
if ( !QFile::exists ( strReturn ) )
{
strReturn = "";
}
// AT LEAST QT 4.8 IS REQUIRED:
/*
// There is no direct query of the country code in Qt, therefore we use a
// workaround: Get the matching locales properties and split the name of
// that since the second part is the country code
QList<QLocale> vCurLocaleList = QLocale::matchingLocales ( QLocale::AnyLanguage,
QLocale::AnyScript,
eCountry );
// check if the matching locales query was successful
if ( vCurLocaleList.size() > 0 )
{
QStringList vstrLocParts = vCurLocaleList.at ( 0 ).name().split("_");
// the second split contains the name we need
if ( vstrLocParts.size() > 1 )
{
strReturn =
":/png/flags/res/flags/" + vstrLocParts.at ( 1 ).toLower() + ".png";
// check if file actually exists, if not then invalidate reference
if ( !QFile::exists ( strReturn ) )
{
strReturn = "";
}
//else
//{
//// TEST generate table
//static FILE* pFile = fopen ( "test.dat", "w" );
//fprintf ( pFile, " case %d: strISO3166 = \"%s\"; break;\n",
// static_cast<int> ( eCountry ), vstrLocParts.at ( 1 ).toLower().toStdString().c_str() );
//fflush ( pFile );
//}
}
}
*/
}
return strReturn;
}
ECSAddType CLocale::GetCentralServerAddressType ( const QLocale::Country eCountry )
{
// 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 )
{
case QLocale::UnitedStates:
case QLocale::Canada:
case QLocale::Mexico:
case QLocale::Greenland:
return AT_ALL_GENRES;
default:
return AT_DEFAULT;
}
}
QMap<QString, QString> CLocale::GetAvailableTranslations()
{
QMap<QString, QString> 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() )
{
// 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<QString, QString> CLocale::FindSysLangTransFileName ( const QMap<QString, QString>& TranslMap )
{
QPair<QString, QString> 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<QString, QString> 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 );
}
}
// Console writer factory ------------------------------------------------------
QTextStream* ConsoleWriterFactory::get()
{
if ( ptsConsole == nullptr )
{
#if _WIN32
if ( !AttachConsole ( ATTACH_PARENT_PROCESS ) )
{
// Not run from console, dump logging to nowhere
static QString conout;
ptsConsole = new QTextStream ( &conout );
}
else
{
freopen ( "CONOUT$", "w", stdout );
ptsConsole = new QTextStream ( stdout );
}
#else
ptsConsole = new QTextStream ( stdout );
#endif
}
return ptsConsole;
}
/******************************************************************************\
* Global Functions Implementation *
\******************************************************************************/
void DebugError ( const QString& pchErDescr,
const QString& pchPar1Descr,
const double dPar1,
const QString& pchPar2Descr,
const double dPar2 )
{
QFile File ( "DebugError.dat" );
if ( File.open ( QIODevice::Append ) )
{
// 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();
}
printf ( "\nDebug error! For more information see test/DebugError.dat\n" );
exit ( 1 );
}
QString GetVersionAndNameStr ( const bool bWithHtml )
{
QString strVersionText = "";
// name, short description and GPL hint
if ( bWithHtml )
{
strVersionText += "<b>";
}
else
{
strVersionText += " *** ";
}
strVersionText += APP_NAME + QCoreApplication::tr ( ", Version " ) + VERSION;
if ( bWithHtml )
{
strVersionText += "</b><br>";
}
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;
}