Skip to content
Closed
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
37 changes: 32 additions & 5 deletions src/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include "client.h"

#include "compressor.h"

/* Implementation *************************************************************/
CClient::CClient ( const quint16 iPortNumber,
Expand Down Expand Up @@ -54,6 +55,8 @@ CClient::CClient ( const quint16 iPortNumber,
iSndCrdFrameSizeFactor ( FRAME_SIZE_FACTOR_DEFAULT ),
bSndCrdConversionBufferRequired ( false ),
iSndCardMonoBlockSizeSamConvBuff ( 0 ),
fAudioInputPeak ( 0 ),
fAudioOutputPeak ( 0 ),
bFraSiFactPrefSupported ( false ),
bFraSiFactDefSupported ( false ),
bFraSiFactSafeSupported ( false ),
Expand Down Expand Up @@ -1024,13 +1027,30 @@ void CClient::ProcessAudioDataIntern ( CVector<float>& vecfStereoSndCrd )

for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 )
{
// clip samples for stereo pan mode
vecfStereoSndCrd[i] = ClipFloat (
fGainL * vecfStereoSndCrd[j] + fGainR * vecfStereoSndCrd[j + 1] );
vecfStereoSndCrd[i] =
fGainL * vecfStereoSndCrd[j] + fGainR * vecfStereoSndCrd[j + 1];
}
}
}

// apply local audio input compressor
if ( eAudioChannelConf == CC_STEREO )
{
for ( i = 0; i < iMonoBlockSizeSam; i++ )
{
stereoCompressor(SYSTEM_SAMPLE_RATE_HZ, fAudioInputPeak,
vecfStereoSndCrd[2 * i], vecfStereoSndCrd[2 * i + 1]);
}
}
else
{
for ( i = 0; i < iMonoBlockSizeSam; i++ )
{
monoCompressor(SYSTEM_SAMPLE_RATE_HZ, fAudioInputPeak,
vecfStereoSndCrd[i]);
}
}

// Support for mono-in/stereo-out mode: Per definition this mode works in
// full stereo mode at the transmission level. The only thing which is done
// is to mix both sound card inputs together and then put this signal on
Expand Down Expand Up @@ -1122,8 +1142,8 @@ void CClient::ProcessAudioDataIntern ( CVector<float>& vecfStereoSndCrd )
{
for ( i = 0; i < iStereoBlockSizeSam; i++ )
{
vecfStereoSndCrd[i] = ClipFloat (
vecfStereoSndCrd[i] + vecfStereoSndCrdMuteStream[i] * fMuteOutStreamGain );
vecfStereoSndCrd[i] =
vecfStereoSndCrd[i] + vecfStereoSndCrdMuteStream[i] * fMuteOutStreamGain;
}
}

Expand All @@ -1147,6 +1167,13 @@ void CClient::ProcessAudioDataIntern ( CVector<float>& vecfStereoSndCrd )
vecfStereoSndCrd.Reset ( 0 );
}

// apply local audio output compressor
for ( i = 0; i < iMonoBlockSizeSam; i++ )
{
stereoCompressor(SYSTEM_SAMPLE_RATE_HZ, fAudioOutputPeak,
vecfStereoSndCrd[2 * i], vecfStereoSndCrd[2 * i + 1]);
}

// update socket buffer size
Channel.UpdateSocketBufferSize();

Expand Down
2 changes: 2 additions & 0 deletions src/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@ class CClient : public QObject
CVector<float> vecDataConvBuf;
CVector<float> vecfStereoSndCrdMuteStream;
CVector<float> vecZeros;
float fAudioInputPeak;
float fAudioOutputPeak;

bool bFraSiFactPrefSupported;
bool bFraSiFactDefSupported;
Expand Down
91 changes: 91 additions & 0 deletions src/compressor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
ZynAddSubFX - a software synthesizer

Compressor.h - simple audio compressor macros
Copyright (C) 2016 Hans Petter Selasky

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.
*/

#pragma once

static inline bool
floatIsValid(const float x)
{
const float __r = x * 0.0f;
return (__r == 0.0f || __r == -0.0f);
}

#define stereoCompressor(div,pv,l,r) do { \
/* \
* Don't max the output range to avoid \
* overflowing sample rate conversion and \
* equalizer filters in the DSP's output \
* path. Keep one 10th, 1dB, reserved. \
*/ \
static constexpr float __limit = \
1.0f - (1.0f / 10.0f); \
float __peak; \
\
/* sanity checks */ \
__peak = (pv); \
if (!floatIsValid(__peak)) \
__peak = 0.0; \
if (!floatIsValid(l)) \
(l) = 0.0; \
if (!floatIsValid(r)) \
(r) = 0.0; \
/* compute maximum */ \
if ((l) < -__peak) \
__peak = -(l); \
else if ((l) > __peak) \
__peak = (l); \
if ((r) < -__peak) \
__peak = -(r); \
else if ((r) > __peak) \
__peak = (r); \
/* compressor */ \
if (__peak > __limit) { \
(l) /= __peak; \
(r) /= __peak; \
(l) *= __limit; \
(r) *= __limit; \
__peak -= __peak / (div); \
} \
(pv) = __peak; \
} while (0)

#define monoCompressor(div,pv,l) do { \
/* \
* Don't max the output range to avoid \
* overflowing sample rate conversion and \
* equalizer filters in the DSP's output \
* path. Keep one 10th, 1dB, reserved. \
*/ \
static constexpr float __limit = \
1.0f - (1.0f / 10.0f); \
float __peak; \
\
/* sanity checks */ \
__peak = (pv); \
if (!floatIsValid(__peak)) \
__peak = 0.0; \
if (!floatIsValid(l)) \
(l) = 0.0; \
/* compute maximum */ \
if ((l) < -__peak) \
__peak = -(l); \
else if ((l) > __peak) \
__peak = (l); \
/* compressor */ \
if (__peak > __limit) { \
(l) /= __peak; \
(l) *= __limit; \
__peak -= __peak / (div); \
} \
(pv) = __peak; \
} while (0)

26 changes: 10 additions & 16 deletions src/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include "server.h"

#include "compressor.h"

// CHighPrecisionTimer implementation ******************************************
#ifdef _WIN32
Expand Down Expand Up @@ -339,8 +340,8 @@ CServer::CServer ( const int iNewMaxNumChan,
vecvecfGains.Init ( iMaxNumChannels );
vecvecfPannings.Init ( iMaxNumChannels );
vecvecfData.Init ( iMaxNumChannels );
vecvecfSendData.Init ( iMaxNumChannels );
vecvecfIntermediateProcBuf.Init ( iMaxNumChannels );
vecfOutputAudioPeak.Init ( iMaxNumChannels, 0 );
vecvecbyCodedData.Init ( iMaxNumChannels );
vecNumAudioChannels.Init ( iMaxNumChannels );
vecNumFrameSizeConvBlocks.Init ( iMaxNumChannels );
Expand All @@ -356,10 +357,6 @@ CServer::CServer ( const int iNewMaxNumChan,
// we always use stereo audio buffers (which is the worst case)
vecvecfData[i].Init ( 2 /* stereo */ * DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES /* worst case buffer size */ );

// (note that we only allocate iMaxNumChannels buffers for the send
// and coded data because of the OMP implementation)
vecvecfSendData[i].Init ( 2 /* stereo */ * DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES /* worst case buffer size */ );

// 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 */ );

Expand Down Expand Up @@ -1120,7 +1117,7 @@ void CServer::MixEncodeTransmitData ( const int iChanCnt,
{
int i, j, k, iUnused;
CVector<float>& vecfIntermProcBuf = vecvecfIntermediateProcBuf[iChanCnt]; // use reference for faster access
CVector<float>& vecfSendData = vecvecfSendData[iChanCnt]; // use reference for faster access
float &fAudioPeak = vecfOutputAudioPeak[iChanCnt];

// get actual ID of current channel
const int iCurChanID = vecChanIDsCurConChan[iChanCnt];
Expand Down Expand Up @@ -1183,11 +1180,9 @@ void CServer::MixEncodeTransmitData ( const int iChanCnt,
// When adding multiple sound sources together
// the resulting signal level may exceed the maximum
// audio range which is from -1.0f to 1.0f inclusivly.
// Clip the intermediate sound buffer to be within
// the expected range
for ( i = 0; i < iServerFrameSizeSamples; i++ )
{
vecfSendData[i] = ClipFloat ( vecfIntermProcBuf[i] );
monoCompressor(SYSTEM_SAMPLE_RATE_HZ, fAudioPeak, vecfIntermProcBuf[i]);
}
}
else
Expand Down Expand Up @@ -1255,11 +1250,10 @@ void CServer::MixEncodeTransmitData ( const int iChanCnt,
// When adding multiple sound sources together
// the resulting signal level may exceed the maximum
// audio range which is from -1.0f to 1.0f inclusivly.
// Clip the intermediate sound buffer to be within
// the expected range
for ( i = 0; i < ( 2 * iServerFrameSizeSamples ); i++ )
for ( i = 0; i < iServerFrameSizeSamples; i++ )
{
vecfSendData[i] = ClipFloat ( vecfIntermProcBuf[i] );
stereoCompressor(SYSTEM_SAMPLE_RATE_HZ, fAudioPeak,
vecfIntermProcBuf[2 * i], vecfIntermProcBuf[2 * i + 1]);
}
}

Expand Down Expand Up @@ -1303,12 +1297,12 @@ void CServer::MixEncodeTransmitData ( const int iChanCnt,
// 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 ( vecfSendData, SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt] ) )
DoubleFrameSizeConvBufOut[iCurChanID].Put ( vecfIntermProcBuf, SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt] ) )
{
if ( vecUseDoubleSysFraSizeConvBuf[iChanCnt] != 0 )
{
// get the large frame from the conversion buffer
DoubleFrameSizeConvBufOut[iCurChanID].GetAll ( vecfSendData, DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt] );
DoubleFrameSizeConvBufOut[iCurChanID].GetAll ( vecfIntermProcBuf, DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt] );
}

for ( int iB = 0; iB < vecNumFrameSizeConvBlocks[iChanCnt]; iB++ )
Expand All @@ -1321,7 +1315,7 @@ void CServer::MixEncodeTransmitData ( const int iChanCnt,
opus_custom_encoder_ctl ( pCurOpusEncoder, OPUS_SET_BITRATE ( CalcBitRateBitsPerSecFromCodedBytes ( iCeltNumCodedBytes, iClientFrameSizeSamples ) ) );

iUnused = opus_custom_encode_float ( pCurOpusEncoder,
&vecfSendData[iB * SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt]],
&vecfIntermProcBuf[iB * SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[iChanCnt]],
iClientFrameSizeSamples,
&vecvecbyCodedData[iChanCnt][0],
iCeltNumCodedBytes );
Expand Down
2 changes: 1 addition & 1 deletion src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,8 @@ class CServer :
CVector<int> vecNumFrameSizeConvBlocks;
CVector<int> vecUseDoubleSysFraSizeConvBuf;
CVector<EAudComprType> vecAudioComprType;
CVector<CVector<float> > vecvecfSendData;
CVector<CVector<float> > vecvecfIntermediateProcBuf;
CVector<float> vecfOutputAudioPeak;
CVector<CVector<uint8_t> > vecvecbyCodedData;

// Channel levels
Expand Down