From 3ae13ae45e82bc0ad2d526ca91940e800db70800 Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 10 Mar 2024 23:06:46 -0400 Subject: [PATCH] Apply master gain outside audio devices (#7135) --- include/AudioDevice.h | 7 +------ include/AudioFileFlac.h | 4 +--- include/AudioFileMP3.h | 4 +--- include/AudioFileOgg.h | 4 +--- include/AudioFileWave.h | 4 +--- include/AudioSampleRecorder.h | 4 +--- include/MixHelpers.h | 2 ++ src/core/AudioEngine.cpp | 3 +++ src/core/MixHelpers.cpp | 9 +++++++++ src/core/audio/AudioAlsa.cpp | 5 +---- src/core/audio/AudioDevice.cpp | 17 +++++------------ src/core/audio/AudioFileFlac.cpp | 6 +++--- src/core/audio/AudioFileMP3.cpp | 9 +++------ src/core/audio/AudioFileOgg.cpp | 11 +++-------- src/core/audio/AudioFileWave.cpp | 13 +++---------- src/core/audio/AudioJack.cpp | 3 +-- src/core/audio/AudioOss.cpp | 2 +- src/core/audio/AudioPortAudio.cpp | 6 +----- src/core/audio/AudioPulseAudio.cpp | 5 +---- src/core/audio/AudioSampleRecorder.cpp | 6 +----- src/core/audio/AudioSdl.cpp | 12 +----------- src/core/audio/AudioSndio.cpp | 3 +-- src/core/audio/AudioSoundIo.cpp | 4 +--- src/gui/MixerView.cpp | 4 ---- src/gui/widgets/Oscilloscope.cpp | 6 +++--- 25 files changed, 49 insertions(+), 104 deletions(-) diff --git a/include/AudioDevice.h b/include/AudioDevice.h index d1a9617cd..c6ee46efc 100644 --- a/include/AudioDevice.h +++ b/include/AudioDevice.h @@ -96,11 +96,7 @@ public: protected: // subclasses can re-implement this for being used in conjunction with // processNextBuffer() - virtual void writeBuffer( const surroundSampleFrame * /* _buf*/, - const fpp_t /*_frames*/, - const float /*_master_gain*/ ) - { - } + virtual void writeBuffer(const surroundSampleFrame* /* _buf*/, const fpp_t /*_frames*/) {} // called by according driver for fetching new sound-data fpp_t getNextBuffer( surroundSampleFrame * _ab ); @@ -109,7 +105,6 @@ protected: // returns num of bytes in outbuf int convertToS16( const surroundSampleFrame * _ab, const fpp_t _frames, - const float _master_gain, int_sample_t * _output_buffer, const bool _convert_endian = false ); diff --git a/include/AudioFileFlac.h b/include/AudioFileFlac.h index 944e30478..9432f4231 100644 --- a/include/AudioFileFlac.h +++ b/include/AudioFileFlac.h @@ -65,9 +65,7 @@ private: SF_INFO m_sfinfo; SNDFILE* m_sf; - void writeBuffer(surroundSampleFrame const* _ab, - fpp_t const frames, - float master_gain) override; + void writeBuffer(surroundSampleFrame const* _ab, fpp_t const frames) override; bool startEncoding(); void finishEncoding(); diff --git a/include/AudioFileMP3.h b/include/AudioFileMP3.h index 4289ad211..013c93a3e 100644 --- a/include/AudioFileMP3.h +++ b/include/AudioFileMP3.h @@ -58,9 +58,7 @@ public: } protected: - void writeBuffer( const surroundSampleFrame * /* _buf*/, - const fpp_t /*_frames*/, - const float /*_master_gain*/ ) override; + void writeBuffer(const surroundSampleFrame* /* _buf*/, const fpp_t /*_frames*/) override; private: void flushRemainingBuffers(); diff --git a/include/AudioFileOgg.h b/include/AudioFileOgg.h index 77be8ca1c..fc3ce25b4 100644 --- a/include/AudioFileOgg.h +++ b/include/AudioFileOgg.h @@ -58,9 +58,7 @@ public: private: - void writeBuffer( const surroundSampleFrame * _ab, - const fpp_t _frames, - const float _master_gain ) override; + void writeBuffer(const surroundSampleFrame* _ab, const fpp_t _frames) override; bool startEncoding(); void finishEncoding(); diff --git a/include/AudioFileWave.h b/include/AudioFileWave.h index c186aaaa7..22b124f93 100644 --- a/include/AudioFileWave.h +++ b/include/AudioFileWave.h @@ -56,9 +56,7 @@ public: private: - void writeBuffer( const surroundSampleFrame * _ab, - const fpp_t _frames, - float _master_gain ) override; + void writeBuffer(const surroundSampleFrame* _ab, const fpp_t _frames) override; bool startEncoding(); void finishEncoding(); diff --git a/include/AudioSampleRecorder.h b/include/AudioSampleRecorder.h index d481cc16c..a3e776881 100644 --- a/include/AudioSampleRecorder.h +++ b/include/AudioSampleRecorder.h @@ -48,9 +48,7 @@ public: std::shared_ptr createSampleBuffer(); private: - void writeBuffer( const surroundSampleFrame * _ab, - const fpp_t _frames, - const float _master_gain ) override; + void writeBuffer(const surroundSampleFrame* _ab, const fpp_t _frames) override; using BufferList = QList>; BufferList m_buffers; diff --git a/include/MixHelpers.h b/include/MixHelpers.h index 6458c65fc..dde17dd02 100644 --- a/include/MixHelpers.h +++ b/include/MixHelpers.h @@ -45,6 +45,8 @@ bool sanitize( sampleFrame * src, int frames ); /*! \brief Add samples from src to dst */ void add( sampleFrame* dst, const sampleFrame* src, int frames ); +/*! \brief Multiply samples from `dst` by `coeff` */ +void multiply(sampleFrame* dst, float coeff, int frames); /*! \brief Add samples from src multiplied by coeffSrc to dst */ void addMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames ); diff --git a/src/core/AudioEngine.cpp b/src/core/AudioEngine.cpp index 47b42e11b..04e3c7c7c 100644 --- a/src/core/AudioEngine.cpp +++ b/src/core/AudioEngine.cpp @@ -24,6 +24,7 @@ #include "AudioEngine.h" +#include "MixHelpers.h" #include "denormals.h" #include "lmmsconfig.h" @@ -433,6 +434,8 @@ void AudioEngine::renderStageMix() Mixer *mixer = Engine::mixer(); mixer->masterMix(m_outputBufferWrite); + MixHelpers::multiply(m_outputBufferWrite, m_masterGain, m_framesPerPeriod); + emit nextAudioBuffer(m_outputBufferRead); // and trigger LFOs diff --git a/src/core/MixHelpers.cpp b/src/core/MixHelpers.cpp index bc55419e9..209640b70 100644 --- a/src/core/MixHelpers.cpp +++ b/src/core/MixHelpers.cpp @@ -178,6 +178,15 @@ struct AddSwappedMultipliedOp const float m_coeff; }; +void multiply(sampleFrame* dst, float coeff, int frames) +{ + for (int i = 0; i < frames; ++i) + { + dst[i][0] *= coeff; + dst[i][1] *= coeff; + } +} + void addSwappedMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames ) { run<>( dst, src, frames, AddSwappedMultipliedOp(coeffSrc) ); diff --git a/src/core/audio/AudioAlsa.cpp b/src/core/audio/AudioAlsa.cpp index 6e17ad0fe..201a967a3 100644 --- a/src/core/audio/AudioAlsa.cpp +++ b/src/core/audio/AudioAlsa.cpp @@ -323,10 +323,7 @@ void AudioAlsa::run() } outbuf_size = frames * channels(); - convertToS16( temp, frames, - audioEngine()->masterGain(), - outbuf, - m_convertEndian ); + convertToS16(temp, frames, outbuf, m_convertEndian); } int min_len = std::min(len, outbuf_size - outbuf_pos); memcpy( ptr, outbuf + outbuf_pos, diff --git a/src/core/audio/AudioDevice.cpp b/src/core/audio/AudioDevice.cpp index 5fb58a1b0..58ba3932e 100644 --- a/src/core/audio/AudioDevice.cpp +++ b/src/core/audio/AudioDevice.cpp @@ -66,10 +66,7 @@ AudioDevice::~AudioDevice() void AudioDevice::processNextBuffer() { const fpp_t frames = getNextBuffer( m_buffer ); - if( frames ) - { - writeBuffer( m_buffer, frames, audioEngine()->masterGain() ); - } + if (frames) { writeBuffer(m_buffer, frames); } else { m_inProcess = false; @@ -211,7 +208,6 @@ fpp_t AudioDevice::resample( const surroundSampleFrame * _src, int AudioDevice::convertToS16( const surroundSampleFrame * _ab, const fpp_t _frames, - const float _master_gain, int_sample_t * _output_buffer, const bool _convert_endian ) { @@ -222,8 +218,8 @@ int AudioDevice::convertToS16( const surroundSampleFrame * _ab, { for( ch_cnt_t chnl = 0; chnl < channels(); ++chnl ) { - temp = static_cast( AudioEngine::clip( _ab[frame][chnl] * _master_gain ) * OUTPUT_SAMPLE_MULTIPLIER ); - + temp = static_cast(AudioEngine::clip(_ab[frame][chnl]) * OUTPUT_SAMPLE_MULTIPLIER); + ( _output_buffer + frame * channels() )[chnl] = ( temp & 0x00ff ) << 8 | ( temp & 0xff00 ) >> 8; @@ -236,11 +232,8 @@ int AudioDevice::convertToS16( const surroundSampleFrame * _ab, { for( ch_cnt_t chnl = 0; chnl < channels(); ++chnl ) { - ( _output_buffer + frame * channels() )[chnl] = - static_cast( - AudioEngine::clip( _ab[frame][chnl] * - _master_gain ) * - OUTPUT_SAMPLE_MULTIPLIER ); + (_output_buffer + frame * channels())[chnl] + = static_cast(AudioEngine::clip(_ab[frame][chnl]) * OUTPUT_SAMPLE_MULTIPLIER); } } } diff --git a/src/core/audio/AudioFileFlac.cpp b/src/core/audio/AudioFileFlac.cpp index af71003d1..097fbdd89 100644 --- a/src/core/audio/AudioFileFlac.cpp +++ b/src/core/audio/AudioFileFlac.cpp @@ -89,7 +89,7 @@ bool AudioFileFlac::startEncoding() return true; } -void AudioFileFlac::writeBuffer(surroundSampleFrame const* _ab, fpp_t const frames, float master_gain) +void AudioFileFlac::writeBuffer(surroundSampleFrame const* _ab, fpp_t const frames) { OutputSettings::BitDepth depth = getOutputSettings().getBitDepth(); float clipvalue = std::nextafterf( -1.0f, 0.0f ); @@ -104,7 +104,7 @@ void AudioFileFlac::writeBuffer(surroundSampleFrame const* _ab, fpp_t const fram // Clip the negative side to just above -1.0 in order to prevent it from changing sign // Upstream issue: https://github.com/erikd/libsndfile/issues/309 // When this commit is reverted libsndfile-1.0.29 must be made a requirement for FLAC - buf[frame*channels() + channel] = std::max(clipvalue, _ab[frame][channel] * master_gain); + buf[frame*channels() + channel] = std::max(clipvalue, _ab[frame][channel]); } } sf_writef_float(m_sf, static_cast(buf.data()), frames); @@ -112,7 +112,7 @@ void AudioFileFlac::writeBuffer(surroundSampleFrame const* _ab, fpp_t const fram else // integer PCM encoding { auto buf = std::vector(frames * channels()); - convertToS16(_ab, frames, master_gain, buf.data(), !isLittleEndian()); + convertToS16(_ab, frames, buf.data(), !isLittleEndian()); sf_writef_short(m_sf, static_cast(buf.data()), frames); } diff --git a/src/core/audio/AudioFileMP3.cpp b/src/core/audio/AudioFileMP3.cpp index ef0677152..2141fabfc 100644 --- a/src/core/audio/AudioFileMP3.cpp +++ b/src/core/audio/AudioFileMP3.cpp @@ -53,21 +53,18 @@ AudioFileMP3::~AudioFileMP3() tearDownEncoder(); } -void AudioFileMP3::writeBuffer( const surroundSampleFrame * _buf, - const fpp_t _frames, - const float _master_gain ) +void AudioFileMP3::writeBuffer(const surroundSampleFrame* _buf, const fpp_t _frames) { if (_frames < 1) { return; } - // TODO Why isn't the gain applied by the driver but inside the device? std::vector interleavedDataBuffer(_frames * 2); for (fpp_t i = 0; i < _frames; ++i) { - interleavedDataBuffer[2*i] = _buf[i][0] * _master_gain; - interleavedDataBuffer[2*i + 1] = _buf[i][1] * _master_gain; + interleavedDataBuffer[2*i] = _buf[i][0]; + interleavedDataBuffer[2*i + 1] = _buf[i][1]; } size_t minimumBufferSize = 1.25 * _frames + 7200; diff --git a/src/core/audio/AudioFileOgg.cpp b/src/core/audio/AudioFileOgg.cpp index d61e27da8..9d5f0c809 100644 --- a/src/core/audio/AudioFileOgg.cpp +++ b/src/core/audio/AudioFileOgg.cpp @@ -185,12 +185,7 @@ bool AudioFileOgg::startEncoding() return true; } - - - -void AudioFileOgg::writeBuffer( const surroundSampleFrame * _ab, - const fpp_t _frames, - const float _master_gain ) +void AudioFileOgg::writeBuffer(const surroundSampleFrame* _ab, const fpp_t _frames) { int eos = 0; @@ -201,7 +196,7 @@ void AudioFileOgg::writeBuffer( const surroundSampleFrame * _ab, { for( ch_cnt_t chnl = 0; chnl < channels(); ++chnl ) { - buffer[chnl][frame] = _ab[frame][chnl] * _master_gain; + buffer[chnl][frame] = _ab[frame][chnl]; } } @@ -258,7 +253,7 @@ void AudioFileOgg::finishEncoding() if( m_ok ) { // just for flushing buffers... - writeBuffer( nullptr, 0, 0.0f ); + writeBuffer(nullptr, 0); // clean up ogg_stream_clear( &m_os ); diff --git a/src/core/audio/AudioFileWave.cpp b/src/core/audio/AudioFileWave.cpp index 9c51437ff..612b98982 100644 --- a/src/core/audio/AudioFileWave.cpp +++ b/src/core/audio/AudioFileWave.cpp @@ -93,12 +93,7 @@ bool AudioFileWave::startEncoding() return true; } - - - -void AudioFileWave::writeBuffer( const surroundSampleFrame * _ab, - const fpp_t _frames, - const float _master_gain ) +void AudioFileWave::writeBuffer(const surroundSampleFrame* _ab, const fpp_t _frames) { OutputSettings::BitDepth bitDepth = getOutputSettings().getBitDepth(); @@ -109,8 +104,7 @@ void AudioFileWave::writeBuffer( const surroundSampleFrame * _ab, { for( ch_cnt_t chnl = 0; chnl < channels(); ++chnl ) { - buf[frame*channels()+chnl] = _ab[frame][chnl] * - _master_gain; + buf[frame * channels() + chnl] = _ab[frame][chnl]; } } sf_writef_float( m_sf, buf, _frames ); @@ -119,8 +113,7 @@ void AudioFileWave::writeBuffer( const surroundSampleFrame * _ab, else { auto buf = new int_sample_t[_frames * channels()]; - convertToS16( _ab, _frames, _master_gain, buf, - !isLittleEndian() ); + convertToS16(_ab, _frames, buf, !isLittleEndian()); sf_writef_short( m_sf, buf, _frames ); delete[] buf; diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index a4fd2c095..61d7814ed 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -344,13 +344,12 @@ int AudioJack::processCallback(jack_nframes_t nframes) while (done < nframes && !m_stopped) { jack_nframes_t todo = std::min(nframes - done, m_framesToDoInCurBuf - m_framesDoneInCurBuf); - const float gain = audioEngine()->masterGain(); for (int c = 0; c < channels(); ++c) { jack_default_audio_sample_t* o = m_tempOutBufs[c]; for (jack_nframes_t frame = 0; frame < todo; ++frame) { - o[done + frame] = m_outBuf[m_framesDoneInCurBuf + frame][c] * gain; + o[done + frame] = m_outBuf[m_framesDoneInCurBuf + frame][c]; } } done += todo; diff --git a/src/core/audio/AudioOss.cpp b/src/core/audio/AudioOss.cpp index 8fedd3b2b..bd6d46c95 100644 --- a/src/core/audio/AudioOss.cpp +++ b/src/core/audio/AudioOss.cpp @@ -303,7 +303,7 @@ void AudioOss::run() break; } - int bytes = convertToS16( temp, frames, audioEngine()->masterGain(), outbuf, m_convertEndian ); + int bytes = convertToS16(temp, frames, outbuf, m_convertEndian); if( write( m_audioFD, outbuf, bytes ) != bytes ) { break; diff --git a/src/core/audio/AudioPortAudio.cpp b/src/core/audio/AudioPortAudio.cpp index 3684a79a8..f303545d2 100644 --- a/src/core/audio/AudioPortAudio.cpp +++ b/src/core/audio/AudioPortAudio.cpp @@ -298,15 +298,11 @@ int AudioPortAudio::process_callback( const int min_len = std::min(static_cast(_framesPerBuffer), m_outBufSize - m_outBufPos); - float master_gain = audioEngine()->masterGain(); - for( fpp_t frame = 0; frame < min_len; ++frame ) { for( ch_cnt_t chnl = 0; chnl < channels(); ++chnl ) { - ( _outputBuffer + frame * channels() )[chnl] = - AudioEngine::clip( m_outBuf[frame][chnl] * - master_gain ); + (_outputBuffer + frame * channels())[chnl] = AudioEngine::clip(m_outBuf[frame][chnl]); } } diff --git a/src/core/audio/AudioPulseAudio.cpp b/src/core/audio/AudioPulseAudio.cpp index 3ca8764cc..a0c5ccaf9 100644 --- a/src/core/audio/AudioPulseAudio.cpp +++ b/src/core/audio/AudioPulseAudio.cpp @@ -278,10 +278,7 @@ void AudioPulseAudio::streamWriteCallback( pa_stream *s, size_t length ) m_quit = true; break; } - int bytes = convertToS16( temp, frames, - audioEngine()->masterGain(), - pcmbuf, - m_convertEndian ); + int bytes = convertToS16(temp, frames, pcmbuf, m_convertEndian); if( bytes > 0 ) { pa_stream_write( m_s, pcmbuf, bytes, nullptr, 0, diff --git a/src/core/audio/AudioSampleRecorder.cpp b/src/core/audio/AudioSampleRecorder.cpp index b5bbf5a8f..c9448b89e 100644 --- a/src/core/audio/AudioSampleRecorder.cpp +++ b/src/core/audio/AudioSampleRecorder.cpp @@ -85,11 +85,7 @@ std::shared_ptr AudioSampleRecorder::createSampleBuffer() return std::make_shared(std::move(bigBuffer), sampleRate()); } - - - -void AudioSampleRecorder::writeBuffer( const surroundSampleFrame * _ab, - const fpp_t _frames, const float ) +void AudioSampleRecorder::writeBuffer(const surroundSampleFrame* _ab, const fpp_t _frames) { auto buf = new sampleFrame[_frames]; for( fpp_t frame = 0; frame < _frames; ++frame ) diff --git a/src/core/audio/AudioSdl.cpp b/src/core/audio/AudioSdl.cpp index 12aa97d63..679912c50 100644 --- a/src/core/audio/AudioSdl.cpp +++ b/src/core/audio/AudioSdl.cpp @@ -261,13 +261,6 @@ void AudioSdl::sdlAudioCallback( Uint8 * _buf, int _len ) m_currentBufferFramesCount - m_currentBufferFramePos); - const float gain = audioEngine()->masterGain(); - for (uint f = 0; f < min_frames_count; f++) - { - (m_outBuf + m_currentBufferFramePos)[f][0] *= gain; - (m_outBuf + m_currentBufferFramePos)[f][1] *= gain; - } - memcpy( _buf, m_outBuf + m_currentBufferFramePos, min_frames_count*sizeof(sampleFrame) ); _buf += min_frames_count*sizeof(sampleFrame); _len -= min_frames_count*sizeof(sampleFrame); @@ -291,10 +284,7 @@ void AudioSdl::sdlAudioCallback( Uint8 * _buf, int _len ) m_convertedBufSize = frames * channels() * sizeof( int_sample_t ); - convertToS16( m_outBuf, frames, - audioEngine()->masterGain(), - (int_sample_t *)m_convertedBuf, - m_outConvertEndian ); + convertToS16(m_outBuf, frames, reinterpret_cast(m_convertedBuf), m_outConvertEndian); } const int min_len = std::min(_len, m_convertedBufSize - m_convertedBufPos); diff --git a/src/core/audio/AudioSndio.cpp b/src/core/audio/AudioSndio.cpp index bb9b249f8..d934dfb9c 100644 --- a/src/core/audio/AudioSndio.cpp +++ b/src/core/audio/AudioSndio.cpp @@ -167,8 +167,7 @@ void AudioSndio::run() break; } - uint bytes = convertToS16( temp, frames, - audioEngine()->masterGain(), outbuf, m_convertEndian ); + uint bytes = convertToS16(temp, frames, outbuf, m_convertEndian); if( sio_write( m_hdl, outbuf, bytes ) != bytes ) { break; diff --git a/src/core/audio/AudioSoundIo.cpp b/src/core/audio/AudioSoundIo.cpp index 36a1929df..6e8a03e38 100644 --- a/src/core/audio/AudioSoundIo.cpp +++ b/src/core/audio/AudioSoundIo.cpp @@ -286,8 +286,6 @@ void AudioSoundIo::writeCallback(int frameCountMin, int frameCountMax) int bytesPerSample = m_outstream->bytes_per_sample; int err; - const float gain = audioEngine()->masterGain(); - int framesLeft = frameCountMax; while (framesLeft > 0) @@ -328,7 +326,7 @@ void AudioSoundIo::writeCallback(int frameCountMin, int frameCountMax) for (int channel = 0; channel < layout->channel_count; channel += 1) { - float sample = gain * m_outBuf[m_outBufFrameIndex][channel]; + float sample = m_outBuf[m_outBufFrameIndex][channel]; memcpy(areas[channel].ptr, &sample, bytesPerSample); areas[channel].ptr += areas[channel].step; } diff --git a/src/gui/MixerView.cpp b/src/gui/MixerView.cpp index 224ea2c85..8b2ecdc56 100644 --- a/src/gui/MixerView.cpp +++ b/src/gui/MixerView.cpp @@ -603,10 +603,6 @@ void MixerView::updateFaders() { Mixer * m = getMixer(); - // apply master gain - m->mixerChannel(0)->m_peakLeft *= Engine::audioEngine()->masterGain(); - m->mixerChannel(0)->m_peakRight *= Engine::audioEngine()->masterGain(); - for (int i = 0; i < m_mixerChannelViews.size(); ++i) { const float opl = m_mixerChannelViews[i]->m_fader->getPeak_L(); diff --git a/src/gui/widgets/Oscilloscope.cpp b/src/gui/widgets/Oscilloscope.cpp index bec45c162..a689f53f3 100644 --- a/src/gui/widgets/Oscilloscope.cpp +++ b/src/gui/widgets/Oscilloscope.cpp @@ -143,14 +143,14 @@ void Oscilloscope::paintEvent( QPaintEvent * ) { AudioEngine const * audioEngine = Engine::audioEngine(); - float master_output = audioEngine->masterGain(); + float masterOutput = audioEngine->masterGain(); const fpp_t frames = audioEngine->framesPerPeriod(); AudioEngine::StereoSample peakValues = audioEngine->getPeakValues(m_buffer, frames); const float max_level = qMax( peakValues.left, peakValues.right ); // Set the color of the line according to the maximum level - float const maxLevelWithAppliedMasterGain = max_level * master_output; + float const maxLevelWithAppliedMasterGain = max_level * masterOutput; p.setPen(QPen(determineLineColor(maxLevelWithAppliedMasterGain), 0.7)); p.setRenderHint( QPainter::Antialiasing ); @@ -158,7 +158,7 @@ void Oscilloscope::paintEvent( QPaintEvent * ) // now draw all that stuff int w = width() - 4; const qreal xd = static_cast(w) / frames; - const qreal half_h = -( height() - 6 ) / 3.0 * static_cast(master_output) - 1; + const qreal half_h = -(height() - 6) / 3.0 * static_cast(masterOutput) - 1; int x_base = 2; const qreal y_base = height() / 2 - 0.5;