Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

### 3.7.0dev <- NOTE: the release version number will be 3.7.1 ###

- Added Pan-Delay code by Detlef Hennings, www.eclim.de

- Automatic channel fader adjustment simplifies mixer setup by using the channel level meters (#1071).
(contributed by @JohannesBrx)

Expand Down
3 changes: 3 additions & 0 deletions src/global.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ LED bar: lbr
#define SYSTEM_FRAME_SIZE_SAMPLES 64
#define DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ( 2 * SYSTEM_FRAME_SIZE_SAMPLES )

// additional buffer for delay panning
#define MAX_DELAY_PANNING_SAMPLES 64

// default server address and port numbers
#define DEFAULT_SERVER_ADDRESS "anygenre1.jamulus.io"
#define DEFAULT_PORT_NUMBER 22124
Expand Down
14 changes: 14 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ int main ( int argc, char** argv )
bool bMuteStream = false;
bool bMuteMeInPersonalMix = false;
bool bDisableRecording = false;
bool bDelayPan = false;
bool bNoAutoJackConnect = false;
bool bUseTranslation = true;
bool bCustomPortNumberGiven = false;
Expand Down Expand Up @@ -381,6 +382,17 @@ int main ( int argc, char** argv )
continue;
}

// Enable delay panning on startup ----------------------------------------
if ( GetFlagArgument ( argv,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need two cli flags? Wouldn't one be enough?

Copy link
Copy Markdown
Contributor Author

@Hk1020 Hk1020 Mar 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes one would be enough. I realized this too after I made the PR. Two options would be useful should we change the default in a later release. We’d still need to decide on the default. I’d like on as default in which case you need the —nodelaypan option. With off as default you want the -P/—delayedpan option.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If switching it on means more processing power, I would not enable it by default (there is and was a lot going on with optimizing server performance and adding something which needs more power is counter productive). You can always switch it on of course.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember that Volker mentioned that this delay pan might not be a better experience for everybody, for example bands. So I also wouldn't enable it per default.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe someone with a band could try it?

i,
"-P",
"--delaypan" ) )
{
bDelayPan = true;
qInfo() << "- starting with delay panning";
CommandLineOptions << "--delaypan";
continue;
}

// Central server ------------------------------------------------------
if ( GetStringArgument ( argc,
Expand Down Expand Up @@ -742,6 +754,7 @@ int main ( int argc, char** argv )
bUseDoubleSystemFrameSize,
bUseMultithreading,
bDisableRecording,
bDelayPan,
eLicenceType );

#ifndef HEADLESS
Expand Down Expand Up @@ -845,6 +858,7 @@ QString UsageArguments ( char **argv )
" -m, --htmlstatus enable HTML status file, set file name\n"
" -o, --serverinfo infos of this server in the format:\n"
" [name];[city];[country as QLocale ID]\n"
" -P, --delaypan start with delay panning enabled\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"
Expand Down
125 changes: 95 additions & 30 deletions src/server.cpp
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ CServer::CServer ( const int iNewMaxNumChan,
const bool bNUseDoubleSystemFrameSize,
const bool bNUseMultithreading,
const bool bDisableRecording,
const bool bNDelayPan,
const ELicenceType eNLicenceType ) :
bUseDoubleSystemFrameSize ( bNUseDoubleSystemFrameSize ),
bUseMultithreading ( bNUseMultithreading ),
Expand All @@ -252,6 +253,7 @@ CServer::CServer ( const int iNewMaxNumChan,
&ConnLessProtocol ),
bDisableRecording ( bDisableRecording ),
bAutoRunMinimized ( false ),
bDelayPan ( bNDelayPan ),
eLicenceType ( eNLicenceType ),
bDisconnectAllClientsOnQuit ( bNDisconnectAllClientsOnQuit ),
pSignalHandler ( CSignalHandler::getSingletonP() )
Expand Down Expand Up @@ -330,7 +332,6 @@ CServer::CServer ( const int iNewMaxNumChan,
iServerFrameSizeSamples = SYSTEM_FRAME_SIZE_SAMPLES;
}


// To avoid audio clitches, in the entire realtime timer audio processing
// routine including the ProcessData no memory must be allocated. Since we
// do not know the required sizes for the vectors, we allocate memory for
Expand All @@ -341,6 +342,7 @@ CServer::CServer ( const int iNewMaxNumChan,
vecvecfGains.Init ( iMaxNumChannels );
vecvecfPannings.Init ( iMaxNumChannels );
vecvecsData.Init ( iMaxNumChannels );
vecvecsData2.Init ( iMaxNumChannels );
vecvecsSendData.Init ( iMaxNumChannels );
vecvecfIntermediateProcBuf.Init ( iMaxNumChannels );
vecvecbyCodedData.Init ( iMaxNumChannels );
Expand All @@ -357,6 +359,7 @@ CServer::CServer ( const int iNewMaxNumChan,

// 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 */ );
vecvecsData2[i].Init ( 2 /* stereo */ * DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES /* worst case buffer size */ );
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this also needed if delayed panning is off? If not, do we need to initialize it (does it have any performance impact)? (also applies to line 345).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Detlef Hennings is working on being able to operate the delay panning in different modes. This is about the distribution of the singers in the virtual space.

Therefore it would be good to keep the parameter '-P panmode' free for this and similar developments and to use for delay panning only --DelayPan (or similar) for delay panning.

Furthermore, delay panning is so important for singers that it should be possible to activate it via a checkbox.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Furthermore, delay panning is so important for singers that it should be possible to activate it via a checkbox.

I think this is already part of this PR: https://github.com/jamulussoftware/jamulus/blob/0c6b3fbfc261b1b078bbd43ed939107deb07132e/src/serverdlgbase.ui

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Furthermore, delay panning is so important for singers that it should be possible to activate it via a checkbox.

I think this is already part of this PR: https://github.com/jamulussoftware/jamulus/blob/0c6b3fbfc261b1b078bbd43ed939107deb07132e/src/serverdlgbase.ui

Right now this is a server setting. I'd also prefer this to be a client setting but it isn't yet. If it is we wouldn't need the command line option.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would probably need a new protocol message.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thought about it once more: it is a good idea after all to make this a client setting. Thanks for asking!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There would need to be:

  • a message from the server saying "I support delay pan if you want it"
  • a message from the client saying "I would like delay pan"
  • a client method to send the message
  • a clientUI mechanism to request the feature only if the server supports it (which would need to take server version and the specific message into account). (I'm not sure if this should be in settings, as it's a user preference, or a on the server mixer, as the choice might vary depending on the jam session.)

I also think maybe get the server support finished and released "as is" before adding the client side. I definitely think it's worth having, though.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as our 9 month experience with vocal ensembles and choirs goes, once the participants have heard the impression of delay panning, they would never want to go back to volume panning. And with acoustic instrumentalists it seems to be similar. I suppose that the preference will mainly depend on the music style. For instance, choir and classical music servers could preferably use delay panning and rock music servers might use volume panning (well, I assume that). Then, at least for a first implementation, the pan type decision can be made on the server side. And public servers may announce which panning type they provide.
If really required, an override option from the client side could be added in a later Jamulus version. But this would complicate server algorithms, because then they need to handle mixed delay and volume panning processing.

Copy link
Copy Markdown
Contributor Author

@Hk1020 Hk1020 Mar 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I‘d also like to get this done. The sooner the better. People would benefit immediately. The client part can come later and this should go out right away (3.7.0). But it is not on me to decide. I too can’t imagine anyone wanting to go back to volume panning.

I‘d been entertaining the idea to provide a client setting in a later PR. The grounds are laid and there isn’t much to add to the server logic except the protocol bit. Mixing is per client already so we only need to make bDelayPan a vector, similar to fPan.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is, because we want to be certain about the quality and fully understand operation of the code that goes into a release, every additional feature causes further delay in the release of 3.7.0, which already is later than we would have liked. That is not to express any doubt about the code quality or its usefulness, it just takes time.

It will definitely be fully reviewed for inclusion in 3.7.1, and hopefully that release will not be too far into the future.


// (note that we only allocate iMaxNumChannels buffers for the send
// and coded data because of the OMP implementation)
Expand Down Expand Up @@ -934,6 +937,16 @@ static CTimingMeas JitterMeas ( 1000, "test2.dat" ); JitterMeas.Measure(); // TE
FutureSynchronizer.waitForFinished();
FutureSynchronizer.clearFutures();
}
if ( bDelayPan )
{
for ( int i = 0; i < iNumClients; i++ )
{
for ( int j = 0; j < 2 * (iServerFrameSizeSamples); j++ )
{
vecvecsData2[i][j] = vecvecsData[i][j];
}
}
}
}
else
{
Expand Down Expand Up @@ -1196,60 +1209,112 @@ void CServer::MixEncodeTransmitData ( const int iChanCnt,
else
{
// Stereo target channel -----------------------------------------------

const int maxPanDelay = MAX_DELAY_PANNING_SAMPLES;

int iLpan, iRpan, iPan;

for ( j = 0; j < iNumClients; j++ )
{
// get a reference to the audio data and gain/pan of the current client
const CVector<int16_t>& vecsData = vecvecsData[j];
const CVector<int16_t>& vecsData2 = vecvecsData2[j];

const float fGain = vecvecfGains[iChanCnt][j];
const float fPan = vecvecfPannings[iChanCnt][j];
const float fPan = bDelayPan ? 0.5f : vecvecfPannings[iChanCnt][j];
Comment thread
pljones marked this conversation as resolved.
const int iPanDel = lround( (float)( 2 * maxPanDelay - 2 ) * ( vecvecfPannings[iChanCnt][j] - 0.5f ) );
const int iPanDelL = ( iPanDel > 0 ) ? iPanDel : 0;
const int iPanDelR = ( iPanDel < 0 ) ? -iPanDel : 0;

Copy link
Copy Markdown
Contributor Author

@Hk1020 Hk1020 Mar 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now have with bDelayPan = true:

    const float             fGain    = vecvecfGains[iChanCnt][j];
    const float             fPan     = bDelayPan ? 0.5f : vecvecfPannings[iChanCnt][j];

fPan = 0.5

    const int iPanDel = lround( (float)(2 * maxPanDelay - 2) * (vecvecfPannings[iChanCnt][j] - 0.5f) );

    const int iPanDelL = ( iPanDel > 0 ) ? iPanDel : 0;
    const int iPanDelR = ( iPanDel < 0 ) ? -iPanDel : 0;

    const float fGainL = MathUtils::GetLeftPan ( fPan, false ) * fGain;

GetLeftPan is 1 for fPan = 0.5 (see util.h), i.e, fGainL = fGain

    const float fGainR = MathUtils::GetRightPan ( fPan, false ) * fGain;

Same here, fGainR = fGain.

So we could do away with fGainL and fGainR completely and just use fGain (if bDelayPan==true). Or what am I missing?

If I am right the rest of the code could shrink a lot.

// calculate combined gain/pan for each stereo channel where we define
// the panning that center equals full gain for both channels
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 ( ( fGainL == 1.0f ) && ( fGainR == 1.0f ) )
Comment on lines -1211 to -1212
Copy link
Copy Markdown
Member

@hoffie hoffie Mar 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code seems to get lost and was annotated to be a speed optimization. I can't tell how important it was. Saving some multiplications sounds really minor (if the cost is two additional equality checks), but as this is part of the performance-critical code path we may want to double-check.

Edit: Just noticed that this seems to have been done deliberately to simplify the logic?

if ( vecNumAudioChannels[j] == 1 )
{
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 )
{
// mono: copy same mono data in both out stereo audio channels
for ( i = 0, k = 0; i < iServerFrameSizeSamples; i++, k += 2 )
// left/right channel
if ( bDelayPan )
{
// left/right channel
vecfIntermProcBuf[k] += vecsData[i];
vecfIntermProcBuf[k + 1] += vecsData[i];
// pan address shift

// left channel
iLpan = i - iPanDelL;
if ( iLpan < 0 )
{
// get from second
iLpan = iLpan + iServerFrameSizeSamples;
vecfIntermProcBuf[k] += vecsData2[iLpan] * fGainL;
}
else
{
vecfIntermProcBuf[k] += vecsData[iLpan] * fGainL;
}

// right channel
iRpan = i - iPanDelR;
if ( iRpan < 0 )
{
// get from second
iRpan = iRpan + iServerFrameSizeSamples;
vecfIntermProcBuf[k + 1] += vecsData2[iRpan] * fGainR;
}
else
{
vecfIntermProcBuf[k + 1] += vecsData[iRpan] * fGainR;
}
}
}
else
{
// stereo
for ( i = 0; i < ( 2 * iServerFrameSizeSamples ); i++ )
else
{
vecfIntermProcBuf[i] += vecsData[i];
vecfIntermProcBuf[k] += vecsData[i] * fGainL;
vecfIntermProcBuf[k + 1] += vecsData[i] * fGainR;
}
}
}
else
{
if ( vecNumAudioChannels[j] == 1 )
// stereo
for ( i = 0; i < ( 2 * iServerFrameSizeSamples ); i++ )
{
// mono: copy same mono data in both out stereo audio channels
for ( i = 0, k = 0; i < iServerFrameSizeSamples; i++, k += 2 )
// left/right channel
if ( bDelayPan )
{
// left/right channel
vecfIntermProcBuf[k] += vecsData[i] * fGainL;
vecfIntermProcBuf[k + 1] += vecsData[i] * fGainR;
// pan address shift
if ( (i & 1) == 0 )
{
iPan = i - 2 * iPanDelL; // if even : left channel
}
else
{
iPan = i - 2 * iPanDelR; // if odd : right channel
}
// interleaved channels
if ( iPan < 0 )
{
// get from second
iPan = iPan + 2 * iServerFrameSizeSamples;
vecfIntermProcBuf[i] += vecsData2[iPan] * fGain;
}
else
{
vecfIntermProcBuf[i] += vecsData[iPan] * fGain;
}
}
}
else
{
// stereo
for ( i = 0; i < ( 2 * iServerFrameSizeSamples ); i += 2 )
else
{
// left/right channel
vecfIntermProcBuf[i] += vecsData[i] * fGainL;
vecfIntermProcBuf[i + 1] += vecsData[i + 1] * fGainR;
if ( (i & 1) == 0 )
{
// if even : left channel
vecfIntermProcBuf[i] += vecsData[i] * fGainL;
}
else
{
// if odd : right channel
vecfIntermProcBuf[i] += vecsData[i] * fGainR;
}
}
}
}
Expand Down
13 changes: 12 additions & 1 deletion src/server.h
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/******************************************************************************\
* Copyright (c) 2004-2020
* Copyright (c) 2004-2021
*
* Author(s):
* Volker Fischer
Expand Down Expand Up @@ -183,6 +183,7 @@ class CServer :
const bool bNUseDoubleSystemFrameSize,
const bool bNUseMultithreading,
const bool bDisableRecording,
const bool bNDelayPan,
const ELicenceType eNLicenceType );

virtual ~CServer();
Expand Down Expand Up @@ -221,6 +222,9 @@ class CServer :

void CreateAndSendRecorderStateForAllConChannels();

// delay panning
void SetEnableDelayPanning ( bool bDelayPanningOn ) { bDelayPan = bDelayPanningOn; }
bool IsDelayPanningEnabled() { return bDelayPan; }

// Server list management --------------------------------------------------
void UpdateServerList() { ServerListManager.Update(); }
Expand Down Expand Up @@ -270,6 +274,9 @@ class CServer :
void SetAutoRunMinimized ( const bool NAuRuMin ) { bAutoRunMinimized = NAuRuMin; }
bool GetAutoRunMinimized() { return bAutoRunMinimized; }

int GetClientNumAudioChannels ( const int iChanNum )
{ return vecChannels[iChanNum].GetNumAudioChannels(); }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are stray spaces here (which Github doesn't show?).

protected:
// access functions for actual channels
bool IsConnected ( const int iChanNum ) { return vecChannels[iChanNum].IsConnected(); }
Expand Down Expand Up @@ -358,6 +365,7 @@ class CServer :
CVector<CVector<float> > vecvecfGains;
CVector<CVector<float> > vecvecfPannings;
CVector<CVector<int16_t> > vecvecsData;
CVector<CVector<int16_t> > vecvecsData2;
CVector<int> vecNumAudioChannels;
CVector<int> vecNumFrameSizeConvBlocks;
CVector<int> vecUseDoubleSysFraSizeConvBuf;
Expand Down Expand Up @@ -394,6 +402,9 @@ class CServer :
// GUI settings
bool bAutoRunMinimized;

// for delay panning
bool bDelayPan;

// messaging
QString strWelcomeMessage;
ELicenceType eLicenceType;
Expand Down
25 changes: 22 additions & 3 deletions src/serverdlg.cpp
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -219,11 +219,12 @@ CServerDlg::CServerDlg ( CServer* pNServP,
}

// set up list view for connected clients
lvwClients->setColumnWidth ( 0, 170 );
lvwClients->setColumnWidth ( 1, 200 );
lvwClients->setColumnWidth ( 0, 170 ); // 170 // IP:port
lvwClients->setColumnWidth ( 1, 200 ); // 200 // Name
lvwClients->setColumnWidth ( 2, 120 ); // 60 // Buf-Frames
lvwClients->setColumnWidth ( 3, 50 ); // Channels
lvwClients->clear();


// TEST workaround for resize problem of window after iconize in task bar
lvwClients->setMinimumWidth ( 170 + 130 + 60 + 205 );
lvwClients->setMinimumHeight ( 140 );
Expand Down Expand Up @@ -315,6 +316,16 @@ lvwClients->setMinimumHeight ( 140 );
ModifyAutoStartEntry ( bCurAutoStartMinState );
#endif

// update delay panning check box
if ( pServer->IsDelayPanningEnabled() )
{
chbEnableDelayPanning->setCheckState(Qt::Checked);
}
else
{
chbEnableDelayPanning->setCheckState(Qt::Unchecked);
}

// Recorder controls
chbEnableRecorder->setChecked ( pServer->GetRecordingEnabled() );
edtCurrentSessionDir->setText ( "" );
Expand Down Expand Up @@ -382,6 +393,10 @@ lvwClients->setMinimumHeight ( 140 );
QObject::connect ( chbEnableRecorder, &QCheckBox::stateChanged,
this, &CServerDlg::OnEnableRecorderStateChanged );

// delay panning
QObject::connect ( chbEnableDelayPanning, &QCheckBox::stateChanged,
this, &CServerDlg::OnEnableDelayPanningStateChanged );

// line edits
QObject::connect ( edtCentralServerAddress, &QLineEdit::editingFinished,
this, &CServerDlg::OnCentralServerAddressEditingFinished );
Expand Down Expand Up @@ -674,6 +689,10 @@ void CServerDlg::OnTimer()
vecpListViewItems[i]->setText ( 2,
QString().setNum ( veciJitBufNumFrames[i] ) );

// show num of audio channels
int iNumAudioChs = pServer->GetClientNumAudioChannels ( i );
vecpListViewItems[i]->setText ( 3, QString().setNum ( iNumAudioChs ) );

vecpListViewItems[i]->setHidden ( false );
}
else
Expand Down
1 change: 1 addition & 0 deletions src/serverdlg.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public slots:
void OnSysTrayActivated ( QSystemTrayIcon::ActivationReason ActReason );
void OnWelcomeMessageChanged() { pServer->SetWelcomeMessage ( tedWelcomeMessage->toPlainText() ); }
void OnLanguageChanged ( QString strLanguage ) { pSettings->strLanguage = strLanguage; }
void OnEnableDelayPanningStateChanged ( int value ) { pServer->SetEnableDelayPanning ( Qt::CheckState::Checked == value ); }
void OnNewRecordingClicked() { pServer->RequestNewRecording(); }
void OnRecordingDirClicked();
void OnClearRecordingDirClicked();
Expand Down
Loading