Merge branch 'master' into feature/recording-stage-one

This commit is contained in:
Johannes Lorenz
2025-03-15 23:42:17 +01:00
969 changed files with 320268 additions and 266901 deletions

110
include/AudioBusHandle.h Normal file
View File

@@ -0,0 +1,110 @@
/*
* AudioBusHandle.h - ThreadableJob between PlayHandle and MixerChannel
*
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2025 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
*
* This file is part of LMMS - https://lmms.io
*
* 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 (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef LMMS_AUDIO_BUS_HANDLE_H
#define LMMS_AUDIO_BUS_HANDLE_H
#include <memory>
#include <QString>
#include <QMutex>
#include "PlayHandle.h"
namespace lmms
{
class EffectChain;
class FloatModel;
class BoolModel;
/**
@brief Job between @ref PlayHandle and @ref MixerChannel
A @ref ThreadableJob class located at the exit point of each @ref PlayHandle into a @ref MixerChannel
(or into an audio device, in case of @ref AudioJack, but this is not supported in AudioBusHandle yet).
It contains an optional @ref EffectChain which is e.g. visualized in the
@ref InstrumentTrackWindow or @ref SampleTrackWindow.
For processing, it adds all input play handles into an internal buffer,
processes the @ref EffectChain (if existing) on that buffer
and finally merges the buffer into its @ref MixerChannel.
*/
class AudioBusHandle : public ThreadableJob
{
public:
AudioBusHandle(const QString& name, bool hasEffectChain = true,
FloatModel* volumeModel = nullptr, FloatModel* panningModel = nullptr,
BoolModel* mutedModel = nullptr);
virtual ~AudioBusHandle();
SampleFrame* buffer() { return m_buffer; }
// indicate whether JACK & Co should provide output-buffer at ext. port
bool extOutputEnabled() const { return m_extOutputEnabled; }
void setExtOutputEnabled(bool enabled);
// next mixer-channel after this audio-bus-handle
// (-1 = none 0 = master)
mix_ch_t nextMixerChannel() const { return m_nextMixerChannel; }
void setNextMixerChannel(const mix_ch_t chnl) { m_nextMixerChannel = chnl; }
const QString& name() const { return m_name; }
void setName(const QString& newName);
EffectChain* effects() { return m_effects.get(); }
bool processEffects();
// ThreadableJob stuff
void doProcessing() override;
bool requiresProcessing() const override { return true; }
void addPlayHandle(PlayHandle* handle);
void removePlayHandle(PlayHandle* handle);
private:
volatile bool m_bufferUsage;
SampleFrame* const m_buffer;
bool m_extOutputEnabled;
mix_ch_t m_nextMixerChannel;
QString m_name;
std::unique_ptr<EffectChain> m_effects;
PlayHandleList m_playHandles;
QMutex m_playHandleLock;
FloatModel* m_volumeModel;
FloatModel* m_panningModel;
BoolModel* m_mutedModel;
friend class AudioEngine;
friend class AudioEngineWorkerThread;
};
} // namespace lmms
#endif // LMMS_AUDIO_BUS_HANDLE_H

View File

@@ -36,7 +36,7 @@ namespace lmms
{
class AudioEngine;
class AudioPort;
class AudioBusHandle;
class SampleFrame;
@@ -57,14 +57,13 @@ public:
}
// if audio-driver supports ports, classes inherting AudioPort
// if audio-driver supports ports, classes inherting AudioBusHandle
// (e.g. channel-tracks) can register themselves for making
// audio-driver able to collect their individual output and provide
// them at a specific port - currently only supported by JACK
virtual void registerPort( AudioPort * _port );
virtual void unregisterPort( AudioPort * _port );
virtual void renamePort( AudioPort * _port );
virtual void registerPort(AudioBusHandle* port);
virtual void unregisterPort(AudioBusHandle* port);
virtual void renamePort(AudioBusHandle* port);
inline bool supportsCapture() const
{
@@ -76,11 +75,6 @@ public:
return m_sampleRate;
}
ch_cnt_t channels() const
{
return m_channels;
}
void processNextBuffer();
virtual void startProcessing()
@@ -109,6 +103,11 @@ protected:
void clearS16Buffer( int_sample_t * _outbuf,
const fpp_t _frames );
ch_cnt_t channels() const
{
return m_channels;
}
inline void setSampleRate( const sample_rate_t _new_sr )
{
m_sampleRate = _new_sr;

View File

@@ -33,6 +33,7 @@
#include <memory>
#include <vector>
#include "AudioDevice.h"
#include "lmms_basics.h"
#include "SampleFrame.h"
#include "LocklessList.h"
@@ -46,18 +47,19 @@ namespace lmms
class AudioDevice;
class MidiClient;
class AudioPort;
class AudioBusHandle;
class AudioEngineWorkerThread;
const fpp_t MINIMUM_BUFFER_SIZE = 32;
const fpp_t DEFAULT_BUFFER_SIZE = 256;
constexpr fpp_t MINIMUM_BUFFER_SIZE = 32;
constexpr fpp_t DEFAULT_BUFFER_SIZE = 256;
constexpr fpp_t MAXIMUM_BUFFER_SIZE = 4096;
const int BYTES_PER_SAMPLE = sizeof( sample_t );
const int BYTES_PER_INT_SAMPLE = sizeof( int_sample_t );
const int BYTES_PER_FRAME = sizeof( SampleFrame );
constexpr int BYTES_PER_SAMPLE = sizeof(sample_t);
constexpr int BYTES_PER_INT_SAMPLE = sizeof(int_sample_t);
constexpr int BYTES_PER_FRAME = sizeof(SampleFrame);
const float OUTPUT_SAMPLE_MULTIPLIER = 32767.0f;
constexpr float OUTPUT_SAMPLE_MULTIPLIER = 32767.0f;
class LMMS_EXPORT AudioEngine : public QObject
{
@@ -172,15 +174,15 @@ public:
bool captureDeviceAvailable() const;
// audio-port-stuff
inline void addAudioPort(AudioPort * port)
// audio-bus-handle-stuff
inline void addAudioBusHandle(AudioBusHandle* busHandle)
{
requestChangeInModel();
m_audioPorts.push_back(port);
m_audioBusHandles.push_back(busHandle);
doneChangeInModel();
}
void removeAudioPort(AudioPort * port);
void removeAudioBusHandle(AudioBusHandle* busHandle);
// MIDI-client-stuff
@@ -236,9 +238,20 @@ public:
}
sample_rate_t baseSampleRate() const;
sample_rate_t outputSampleRate() const;
sample_rate_t inputSampleRate() const;
sample_rate_t baseSampleRate() const { return m_baseSampleRate; }
sample_rate_t outputSampleRate() const
{
return m_audioDev != nullptr ? m_audioDev->sampleRate() : m_baseSampleRate;
}
sample_rate_t inputSampleRate() const
{
return m_audioDev != nullptr ? m_audioDev->sampleRate() : m_baseSampleRate;
}
inline float masterGain() const
{
@@ -291,9 +304,6 @@ public:
void changeQuality(const struct qualitySettings & qs);
inline bool isMetronomeActive() const { return m_metronomeActive; }
inline void setMetronomeActive(bool value = true) { m_metronomeActive = value; }
//! Block until a change in model can be done (i.e. wait for audio thread)
void requestChangeInModel();
void doneChangeInModel();
@@ -359,19 +369,18 @@ private:
void swapBuffers();
void handleMetronome();
void clearInternal();
bool m_renderOnly;
std::vector<AudioPort *> m_audioPorts;
std::vector<AudioBusHandle*> m_audioBusHandles;
fpp_t m_framesPerPeriod;
SampleFrame* m_inputBuffer[2];
f_cnt_t m_inputBufferFrames[2];
f_cnt_t m_inputBufferSize[2];
sample_rate_t m_baseSampleRate;
int m_inputBufferRead;
int m_inputBufferWrite;
@@ -409,8 +418,6 @@ private:
AudioEngineProfiler m_profiler;
bool m_metronomeActive;
bool m_clearSignal;
std::recursive_mutex m_changeMutex;

View File

@@ -56,56 +56,16 @@ public:
return new AudioFileOgg( outputSettings, channels, successful, outputFilename, audioEngine );
}
private:
void writeBuffer(const SampleFrame* _ab, const fpp_t _frames) override;
bool startEncoding();
void finishEncoding();
inline int writePage();
inline bitrate_t nominalBitrate() const
{
return getOutputSettings().getBitRateSettings().getBitRate();
}
inline bitrate_t minBitrate() const
{
if (nominalBitrate() > 64)
{
return nominalBitrate() - 64;
}
else
{
return 64;
}
}
inline bitrate_t maxBitrate() const
{
return nominalBitrate() + 64;
}
private:
bool m_ok;
ch_cnt_t m_channels;
sample_rate_t m_rate;
uint32_t m_serialNo;
vorbis_comment * m_comments;
// encoding setup - init by init_ogg_encoding
ogg_stream_state m_os;
ogg_page m_og;
ogg_packet m_op;
vorbis_dsp_state m_vd;
vorbis_block m_vb;
vorbis_info m_vi;
} ;
vorbis_info m_vi;
vorbis_dsp_state m_vds;
vorbis_comment m_vc;
vorbis_block m_vb;
ogg_stream_state m_oss;
ogg_packet m_packet;
ogg_page m_page;
};
} // namespace lmms

View File

@@ -36,9 +36,15 @@
#include <atomic>
#include <vector>
#ifdef AUDIO_BUS_HANDLE_SUPPORT
#include <QMap>
#endif
#include "AudioDevice.h"
#include "AudioDeviceSetupWidget.h"
#ifdef AUDIO_BUS_HANDLE_SUPPORT
#include "AudioBusHandle.h"
#endif
class QLineEdit;
@@ -94,9 +100,9 @@ private:
void startProcessing() override;
void stopProcessing() override;
void registerPort(AudioPort* port) override;
void unregisterPort(AudioPort* port) override;
void renamePort(AudioPort* port) override;
void registerPort(AudioBusHandle* port) override;
void unregisterPort(AudioBusHandle* port) override;
void renamePort(AudioBusHandle* port) override;
int processCallback(jack_nframes_t nframes);
@@ -116,13 +122,13 @@ private:
f_cnt_t m_framesDoneInCurBuf;
f_cnt_t m_framesToDoInCurBuf;
#ifdef AUDIO_PORT_SUPPORT
#ifdef AUDIO_BUS_HANDLE_SUPPORT
struct StereoPort
{
jack_port_t* ports[2];
};
using JackPortMap = QMap<AudioPort*, StereoPort>;
using JackPortMap = QMap<AudioBusHandle*, StereoPort>;
JackPortMap m_portMap;
#endif

View File

@@ -1,139 +0,0 @@
/*
* AudioPort.h - base-class for objects providing sound at a port
*
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of LMMS - https://lmms.io
*
* 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 (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef LMMS_AUDIO_PORT_H
#define LMMS_AUDIO_PORT_H
#include <memory>
#include <QString>
#include <QMutex>
#include "PlayHandle.h"
namespace lmms
{
class EffectChain;
class FloatModel;
class BoolModel;
class AudioPort : public ThreadableJob
{
public:
AudioPort( const QString & _name, bool _has_effect_chain = true,
FloatModel * volumeModel = nullptr, FloatModel * panningModel = nullptr,
BoolModel * mutedModel = nullptr );
virtual ~AudioPort();
inline SampleFrame* buffer()
{
return m_portBuffer;
}
inline void lockBuffer()
{
m_portBufferLock.lock();
}
inline void unlockBuffer()
{
m_portBufferLock.unlock();
}
// indicate whether JACK & Co should provide output-buffer at ext. port
inline bool extOutputEnabled() const
{
return m_extOutputEnabled;
}
void setExtOutputEnabled( bool _enabled );
// next mixer-channel after this audio-port
// (-1 = none 0 = master)
inline mix_ch_t nextMixerChannel() const
{
return m_nextMixerChannel;
}
inline EffectChain * effects()
{
return m_effects.get();
}
void setNextMixerChannel( const mix_ch_t _chnl )
{
m_nextMixerChannel = _chnl;
}
const QString & name() const
{
return m_name;
}
void setName( const QString & _new_name );
bool processEffects();
// ThreadableJob stuff
void doProcessing() override;
bool requiresProcessing() const override
{
return true;
}
void addPlayHandle( PlayHandle * handle );
void removePlayHandle( PlayHandle * handle );
private:
volatile bool m_bufferUsage;
SampleFrame* m_portBuffer;
QMutex m_portBufferLock;
bool m_extOutputEnabled;
mix_ch_t m_nextMixerChannel;
QString m_name;
std::unique_ptr<EffectChain> m_effects;
PlayHandleList m_playHandles;
QMutex m_playHandleLock;
FloatModel * m_volumeModel;
FloatModel * m_panningModel;
BoolModel * m_mutedModel;
friend class AudioEngine;
friend class AudioEngineWorkerThread;
} ;
} // namespace lmms
#endif // LMMS_AUDIO_PORT_H

View File

@@ -38,6 +38,7 @@
#include "SampleClip.h"
#include "TimePos.h"
#include "lmms_basics.h"
#include "SampleThumbnail.h"
class QPainter;
class QPixmap;
@@ -241,6 +242,8 @@ private:
QScrollBar * m_leftRightScroll;
QScrollBar * m_topBottomScroll;
void adjustLeftRightScoll(int value);
TimePos m_currentPosition;
Action m_action;
@@ -288,6 +291,8 @@ private:
QColor m_ghostNoteColor;
QColor m_detuningNoteColor;
QColor m_ghostSampleColor;
SampleThumbnail m_sampleThumbnail;
friend class AutomationEditorWindow;

View File

@@ -50,8 +50,7 @@ public:
gui::TrackView * createView( gui::TrackContainerView* ) override;
Clip* createClip(const TimePos & pos) override;
void saveTrackSpecificSettings( QDomDocument & _doc,
QDomElement & _parent ) override;
void saveTrackSpecificSettings(QDomDocument& doc, QDomElement& parent, bool presetMode) override;
void loadTrackSpecificSettings( const QDomElement & _this ) override;
private:

View File

@@ -156,11 +156,11 @@ public:
t += 1;
const sample_t s3 = s_waveforms[ static_cast<std::size_t>(_wave) ].sampleAt( t, lookup );
const sample_t s4 = s_waveforms[ static_cast<std::size_t>(_wave) ].sampleAt( t, ( lookup + 1 ) % tlen );
const sample_t s34 = linearInterpolate( s3, s4, ip );
const sample_t s34 = std::lerp(s3, s4, ip);
const float ip2 = ( ( tlen - _wavelen ) / tlen - 0.5 ) * 2.0;
return linearInterpolate( s12, s34, ip2 );
return std::lerp(s12, s34, ip2);
*/
};

View File

@@ -31,16 +31,12 @@
#ifndef LMMS_BASIC_FILTERS_H
#define LMMS_BASIC_FILTERS_H
#ifndef __USE_XOPEN
#define __USE_XOPEN
#endif
#include <cmath>
#include <algorithm>
#include <array>
#include <cmath>
#include <numbers>
#include "lmms_basics.h"
#include "lmms_constants.h"
#include "interpolation.h"
namespace lmms
{
@@ -73,20 +69,20 @@ public:
inline void setCoeffs( float freq )
{
using namespace std::numbers;
// wc
const double wc = D_2PI * freq;
const double wc = 2 * pi * freq;
const double wc2 = wc * wc;
const double wc3 = wc2 * wc;
m_wc4 = wc2 * wc2;
// k
const double k = wc / tan( D_PI * freq / m_sampleRate );
const double k = wc / std::tan(pi * freq / m_sampleRate);
const double k2 = k * k;
const double k3 = k2 * k;
m_k4 = k2 * k2;
// a
static const double sqrt2 = sqrt( 2.0 );
const double sq_tmp1 = sqrt2 * wc3 * k;
const double sq_tmp2 = sqrt2 * wc * k3;
@@ -211,7 +207,7 @@ public:
inline float update( float s, ch_cnt_t ch )
{
if (std::abs(s) < 1.0e-10f && std::abs(m_z1[ch]) < 1.0e-10f) return 0.0f;
if (std::abs(s) < 1.0e-10f && std::abs(m_z1[ch]) < 1.0e-10f) { return 0.0f; }
return m_z1[ch] = s * m_a0 + m_z1[ch] * m_b1;
}
@@ -380,7 +376,7 @@ public:
for( int i = 0; i < 4; ++i )
{
ip += 0.25f;
sample_t x = linearInterpolate( m_last[_chnl], _in0, ip ) - m_r * m_y3[_chnl];
sample_t x = std::lerp(m_last[_chnl], _in0, ip) - m_r * m_y3[_chnl];
m_y1[_chnl] = std::clamp((x + m_oldx[_chnl]) * m_p
- m_k * m_y1[_chnl], -10.0f,
@@ -706,6 +702,7 @@ public:
inline void calcFilterCoeffs( float _freq, float _q )
{
using namespace std::numbers;
// temp coef vars
_q = std::max(_q, minQ());
@@ -718,7 +715,7 @@ public:
{
_freq = std::clamp(_freq, 50.0f, 20000.0f);
const float sr = m_sampleRatio * 0.25f;
const float f = 1.0f / ( _freq * F_2PI );
const float f = 1.0f / (_freq * 2 * pi_v<float>);
m_rca = 1.0f - sr / ( f + sr );
m_rcb = 1.0f - m_rca;
@@ -751,8 +748,8 @@ public:
const float fract = vowelf - vowel;
// interpolate between formant frequencies
const float f0 = 1.0f / ( linearInterpolate( _f[vowel+0][0], _f[vowel+1][0], fract ) * F_2PI );
const float f1 = 1.0f / ( linearInterpolate( _f[vowel+0][1], _f[vowel+1][1], fract ) * F_2PI );
const float f0 = 1.f / (std::lerp(_f[vowel+0][0], _f[vowel+1][0], fract) * 2 * pi_v<float>);
const float f1 = 1.f / (std::lerp(_f[vowel+0][1], _f[vowel+1][1], fract) * 2 * pi_v<float>);
// samplerate coeff: depends on oversampling
const float sr = m_type == FilterType::FastFormant ? m_sampleRatio : m_sampleRatio * 0.25f;
@@ -774,7 +771,7 @@ public:
// (Empirical tunning)
m_p = ( 3.6f - 3.2f * f ) * f;
m_k = 2.0f * m_p - 1;
m_r = _q * powf( F_E, ( 1 - m_p ) * 1.386249f );
m_r = _q * std::exp((1 - m_p) * 1.386249f);
if( m_doubleFilter )
{
@@ -791,7 +788,7 @@ public:
m_p = ( 3.6f - 3.2f * f ) * f;
m_k = 2.0f * m_p - 1.0f;
m_r = _q * 0.1f * powf( F_E, ( 1 - m_p ) * 1.386249f );
m_r = _q * 0.1f * std::exp((1 - m_p) * 1.386249f);
return;
}
@@ -801,7 +798,7 @@ public:
m_type == FilterType::Highpass_SV ||
m_type == FilterType::Notch_SV )
{
const float f = sinf(std::max(minFreq(), _freq) * m_sampleRatio * F_PI);
const float f = std::sin(std::max(minFreq(), _freq) * m_sampleRatio * pi_v<float>);
m_svf1 = std::min(f, 0.825f);
m_svf2 = std::min(f * 2.0f, 0.825f);
m_svq = std::max(0.0001f, 2.0f - (_q * 0.1995f));
@@ -810,9 +807,9 @@ public:
// other filters
_freq = std::clamp(_freq, minFreq(), 20000.0f);
const float omega = F_2PI * _freq * m_sampleRatio;
const float tsin = sinf( omega ) * 0.5f;
const float tcos = cosf( omega );
const float omega = 2 * pi_v<float> * _freq * m_sampleRatio;
const float tsin = std::sin(omega) * 0.5f;
const float tcos = std::cos(omega);
const float alpha = tsin / _q;

View File

@@ -24,6 +24,7 @@
#ifndef LMMS_GUI_COLOR_HELPER_H
#define LMMS_GUI_COLOR_HELPER_H
#include <cmath>
#include <QColor>
namespace lmms::gui
@@ -40,10 +41,11 @@ public:
qreal br, bg, bb, ba;
b.getRgbF(&br, &bg, &bb, &ba);
const float interH = lerp(ar, br, t);
const float interS = lerp(ag, bg, t);
const float interV = lerp(ab, bb, t);
const float interA = lerp(aa, ba, t);
const auto t2 = static_cast<qreal>(t);
const float interH = std::lerp(ar, br, t2);
const float interS = std::lerp(ag, bg, t2);
const float interV = std::lerp(ab, bb, t2);
const float interA = std::lerp(aa, ba, t2);
return QColor::fromRgbF(interH, interS, interV, interA);
}

View File

@@ -134,6 +134,7 @@ private:
void upgrade_fixCMTDelays();
void upgrade_fixBassLoopsTypo();
void findProblematicLadspaPlugins();
void upgrade_noHiddenAutomationTracks();
// List of all upgrade methods
static const std::vector<UpgradeMethod> UPGRADE_METHODS;

View File

@@ -26,9 +26,9 @@
#ifndef LMMS_DELAY_H
#define LMMS_DELAY_H
#include <cmath>
#include "lmms_basics.h"
#include "lmms_math.h"
#include "interpolation.h"
namespace lmms
{
@@ -114,7 +114,7 @@ public:
int readPos = m_position - m_delay;
if( readPos < 0 ) { readPos += m_size; }
const double y = linearInterpolate( m_buffer[readPos][ch], m_buffer[( readPos + 1 ) % m_size][ch], m_fraction );
const double y = std::lerp(m_buffer[readPos][ch], m_buffer[(readPos + 1) % m_size][ch], m_fraction);
++m_position %= m_size;
@@ -185,7 +185,7 @@ class CombFeedfwd
int readPos = m_position - m_delay;
if( readPos < 0 ) { readPos += m_size; }
const double y = linearInterpolate( m_buffer[readPos][ch], m_buffer[( readPos + 1 ) % m_size][ch], m_fraction ) + in * m_gain;
const double y = std::lerp(m_buffer[readPos][ch], m_buffer[(readPos + 1) % m_size][ch], m_fraction) + in * m_gain;
++m_position %= m_size;
@@ -262,8 +262,8 @@ class CombFeedbackDualtap
int readPos2 = m_position - m_delay2;
if( readPos2 < 0 ) { readPos2 += m_size; }
const double y = linearInterpolate( m_buffer[readPos1][ch], m_buffer[( readPos1 + 1 ) % m_size][ch], m_fraction1 ) +
linearInterpolate( m_buffer[readPos2][ch], m_buffer[( readPos2 + 1 ) % m_size][ch], m_fraction2 );
const double y = std::lerp(m_buffer[readPos1][ch], m_buffer[(readPos1 + 1) % m_size][ch], m_fraction1)
+ std::lerp(m_buffer[readPos2][ch], m_buffer[(readPos2 + 1) % m_size][ch], m_fraction2);
++m_position %= m_size;
@@ -337,7 +337,7 @@ public:
int readPos = m_position - m_delay;
if( readPos < 0 ) { readPos += m_size; }
const double y = linearInterpolate( m_buffer[readPos][ch], m_buffer[( readPos + 1 ) % m_size][ch], m_fraction ) + in * -m_gain;
const double y = std::lerp(m_buffer[readPos][ch], m_buffer[(readPos + 1) % m_size][ch], m_fraction) + in * -m_gain;
const double x = in + m_gain * y;
++m_position %= m_size;

View File

@@ -27,7 +27,10 @@
#ifndef LMMS_DEPRECATIONHELPER_H
#define LMMS_DEPRECATIONHELPER_H
#include <type_traits>
#include <QFontMetrics>
#include <QKeySequence>
#include <QWheelEvent>
namespace lmms
@@ -64,6 +67,30 @@ inline QPoint position(QWheelEvent *wheelEvent)
#endif
}
namespace detail
{
template<typename T>
inline constexpr bool IsKeyOrModifier = std::is_same_v<T, Qt::Key>
|| std::is_same_v<T, Qt::Modifier> || std::is_same_v<T, Qt::KeyboardModifier>;
} // namespace detail
/**
* @brief Combines Qt key and modifier arguments together,
* replacing `A | B` which was deprecated in C++20
* due to the enums being different types. (P1120R0)
* @param args Any number of Qt::Key, Qt::Modifier, or Qt::KeyboardModifier
* @return The combination of the given keys/modifiers as an int
*/
template<typename... Args, std::enable_if_t<(detail::IsKeyOrModifier<Args> && ...), bool> = true>
constexpr int combine(Args... args)
{
return (0 | ... | static_cast<int>(args));
}
} // namespace lmms
#endif // LMMS_DEPRECATIONHELPER_H

View File

@@ -25,8 +25,9 @@
#ifndef LMMS_DSPEFFECTLIBRARY_H
#define LMMS_DSPEFFECTLIBRARY_H
#include <numbers>
#include "lmms_math.h"
#include "lmms_constants.h"
#include "lmms_basics.h"
#include "SampleFrame.h"
@@ -289,7 +290,7 @@ namespace lmms::DspEffectLibrary
{
if( in >= m_threshold || in < -m_threshold )
{
return ( fabsf( fabsf( fmodf( in - m_threshold, m_threshold*4 ) ) - m_threshold*2 ) - m_threshold ) * m_gain;
return (std::abs(std::abs(std::fmod(in - m_threshold, m_threshold * 4)) - m_threshold * 2) - m_threshold) * m_gain;
}
return in * m_gain;
}
@@ -303,7 +304,7 @@ namespace lmms::DspEffectLibrary
sample_t nextSample( sample_t in )
{
return m_gain * ( in * ( fabsf( in )+m_threshold ) / ( in*in +( m_threshold-1 )* fabsf( in ) + 1 ) );
return m_gain * (in * (std::abs(in) + m_threshold) / (in * in + (m_threshold - 1) * std::abs(in) + 1));
}
} ;
@@ -328,10 +329,10 @@ namespace lmms::DspEffectLibrary
void nextSample( sample_t& inLeft, sample_t& inRight )
{
const float toRad = F_PI / 180;
constexpr float toRad = std::numbers::pi_v<float> / 180.f;
const sample_t tmp = inLeft;
inLeft += inRight * sinf( m_wideCoeff * ( .5 * toRad ) );
inRight -= tmp * sinf( m_wideCoeff * ( .5 * toRad ) );
inLeft += inRight * std::sin(m_wideCoeff * toRad * .5f);
inRight -= tmp * std::sin(m_wideCoeff * toRad * .5f);
}
private:

View File

@@ -98,6 +98,7 @@ public:
m_originalPluginData( originalPluginData )
{
setName();
setDontRun(true);
}
~DummyEffect() override = default;
@@ -107,9 +108,9 @@ public:
return &m_controls;
}
bool processAudioBuffer( SampleFrame*, const fpp_t ) override
ProcessStatus processImpl(SampleFrame*, const fpp_t) override
{
return false;
return ProcessStatus::Sleep;
}
const QDomElement& originalPluginData() const

View File

@@ -56,7 +56,7 @@ protected:
DropToolBar * addDropToolBar(Qt::ToolBarArea whereToAdd, QString const & windowTitle);
DropToolBar * addDropToolBar(QWidget * parent, Qt::ToolBarArea whereToAdd, QString const & windowTitle);
void closeEvent( QCloseEvent * _ce ) override;
void closeEvent(QCloseEvent * event) override;
protected slots:
virtual void play() {}
virtual void record() {}

View File

@@ -63,9 +63,8 @@ public:
return "effect";
}
virtual bool processAudioBuffer( SampleFrame* _buf,
const fpp_t _frames ) = 0;
//! Returns true if audio was processed and should continue being processed
bool processAudioBuffer(SampleFrame* buf, const fpp_t frames);
inline ch_cnt_t processorCount() const
{
@@ -174,14 +173,29 @@ public:
protected:
/**
Effects should call this at the end of audio processing
enum class ProcessStatus
{
//! Unconditionally continue processing
Continue,
//! Calculate the RMS out sum and call `checkGate` to determine whether to stop processing
ContinueIfNotQuiet,
//! Do not continue processing
Sleep
};
/**
* The main audio processing method that runs when plugin is not asleep
*/
virtual ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) = 0;
/**
* Optional method that runs when plugin is sleeping (not enabled,
* not running, not in the Okay state, or in the Don't Run state)
*/
virtual void processBypassedImpl() {}
If the setting "Keep effects running even without input" is disabled,
after "decay" ms of a signal below "gate", the effect is turned off
and won't be processed again until it receives new audio input
*/
void checkGate( double _out_sum );
gui::PluginView* instantiateView( QWidget * ) override;
@@ -212,6 +226,14 @@ protected:
private:
/**
If the setting "Keep effects running even without input" is disabled,
after "decay" ms of a signal below "gate", the effect is turned off
and won't be processed again until it receives new audio input
*/
void checkGate(double outSum);
EffectChain * m_parent;
void resample( int _i, const SampleFrame* _src_buf,
sample_rate_t _src_sr,

View File

@@ -41,6 +41,12 @@ namespace gui
class EnvelopeGraph : public QWidget, public ModelView
{
Q_OBJECT
Q_PROPERTY(QColor noAmountColor MEMBER m_noAmountColor)
Q_PROPERTY(QColor fullAmountColor MEMBER m_fullAmountColor)
Q_PROPERTY(QColor markerFillColor MEMBER m_markerFillColor)
Q_PROPERTY(QColor markerOutlineColor MEMBER m_markerOutlineColor)
public:
enum class ScalingMode
{
@@ -68,6 +74,11 @@ private:
EnvelopeAndLfoParameters* m_params = nullptr;
ScalingMode m_scaling = ScalingMode::Dynamic;
QColor m_noAmountColor;
QColor m_fullAmountColor;
QColor m_markerFillColor;
QColor m_markerOutlineColor;
};
} // namespace gui

View File

@@ -74,8 +74,8 @@ public:
Q_PROPERTY(bool renderUnityLine READ getRenderUnityLine WRITE setRenderUnityLine)
Q_PROPERTY(QColor unityMarker MEMBER m_unityMarker)
Fader(FloatModel* model, const QString& name, QWidget* parent);
Fader(FloatModel* model, const QString& name, QWidget* parent, const QPixmap& knob);
Fader(FloatModel* model, const QString& name, QWidget* parent, bool modelIsLinear = true);
Fader(FloatModel* model, const QString& name, QWidget* parent, const QPixmap& knob, bool modelIsLinear = true);
~Fader() override = default;
void setPeak_L(float fPeak);
@@ -93,6 +93,17 @@ public:
inline bool getRenderUnityLine() const { return m_renderUnityLine; }
inline void setRenderUnityLine(bool value = true) { m_renderUnityLine = value; }
enum class AdjustmentDirection
{
Up,
Down
};
void adjust(const Qt::KeyboardModifiers & modifiers, AdjustmentDirection direction);
void adjustByDecibelDelta(float value);
void adjustByDialog();
void setDisplayConversion(bool b)
{
m_conversionFactor = b ? 100.0 : 1.0;
@@ -118,18 +129,34 @@ private:
void paintEvent(QPaintEvent* ev) override;
void paintLevels(QPaintEvent* ev, QPainter& painter, bool linear = false);
void paintFaderTicks(QPainter& painter);
int knobPosY() const
{
float fRange = model()->maxValue() - model()->minValue();
float realVal = model()->value() - model()->minValue();
float determineAdjustmentDelta(const Qt::KeyboardModifiers & modifiers) const;
void adjustModelByDBDelta(float value);
return height() - ((height() - m_knob.height()) * (realVal / fRange));
}
int calculateKnobPosYFromModel() const;
void setVolumeByLocalPixelValue(int y);
/**
* @brief Computes the scaled ratio between the maximum dB value supported by the model and the minimum
* dB value that's supported by the fader from the given actual dB value.
*
* If the provided input value lies inside the aforementioned interval then the result will be
* a value between 0 (value == minimum value) and 1 (value == maximum model value).
* If you look at the graphical representation of the fader then 0 represents a point at the bottom
* of the fader and 1 a point at the top of the fader.
* The ratio is scaled by an internal exponent which is an implementation detail that cannot be
* changed for now.
*/
float computeScaledRatio(float dBValue) const;
void setPeak(float fPeak, float& targetPeak, float& persistentPeak, QElapsedTimer& lastPeakTimer);
void updateTextFloat();
void modelValueChanged();
QString getModelValueAsDbString() const;
bool modelIsLinear() const { return m_modelIsLinear; }
// Private members
private:
@@ -145,10 +172,16 @@ private:
QPixmap m_knob {embed::getIconPixmap("fader_knob")};
bool m_levelsDisplayedInDBFS {true};
/**
* @brief Stores the offset to the knob center when the user drags the fader knob
*
* This is needed to make it feel like the users drag the knob without it
* jumping immediately to the click position.
*/
int m_knobCenterOffset {0};
int m_moveStartPoint {-1};
float m_startValue {0.};
bool m_levelsDisplayedInDBFS {true};
bool m_modelIsLinear {false};
static SimpleTextFloat* s_textFloat;

View File

@@ -195,8 +195,6 @@ private slots:
bool openInNewSampleTrack( lmms::gui::FileItem* item );
void sendToActiveInstrumentTrack( lmms::gui::FileItem* item );
void updateDirectory( QTreeWidgetItem * item );
void openContainingFolder( lmms::gui::FileItem* item );
} ;

77
include/FileRevealer.h Normal file
View File

@@ -0,0 +1,77 @@
/*
* FileRevealer.h - include file for FileRevealer
*
* Copyright (c) 2025 Andrew Wiltshire <aw1lt / at/ proton/ dot/me >
*
* This file is part of LMMS - https://lmms.io
*
* 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 (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef LMMS_FILE_REVEALER_H
#define LMMS_FILE_REVEALER_H
#include <QFileInfo>
namespace lmms {
/**
* @class FileRevealer
* @brief A utility class for revealing files and directories in the system's file manager.
*/
class FileRevealer
{
public:
/**
* @brief Retrieves the default file manager for the current platform.
* @return The name or command of the default file manager.
*/
static const QString& getDefaultFileManager();
/**
* @brief Opens the directory containing the specified file or folder in the file manager.
* @param item The QFileInfo object representing the file or folder.
*/
static void openDir(const QFileInfo item);
/**
* @brief Checks whether the file manager supports selecting a specific file.
* @return True if selection is supported, otherwise false.
*/
static const QStringList& getSelectCommand();
/**
* @brief Opens the file manager and selects the specified file if supported.
* @param item The QFileInfo object representing the file to reveal.
*/
static void reveal(const QFileInfo item);
private:
static bool s_canSelect;
protected:
/**
* @brief Determines if the given command supports the argument
* @param command The name of the file manager to check.
* @param arg The command line argument to parse for
* @return True if the file command the argument, otherwise false.
*/
static bool supportsArg(const QString& command, const QString& arg);
};
} // namespace lmms
#endif // LMMS_FILE_REVEALER_H

View File

@@ -1,5 +1,5 @@
/*
* gui_templates.h - GUI-specific templates
* FontHelper.h - Header function to help with fonts
*
* Copyright (c) 2005-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
@@ -22,12 +22,16 @@
*
*/
#ifndef LMMS_GUI_TEMPLATES_H
#define LMMS_GUI_TEMPLATES_H
#ifndef LMMS_FONT_HELPER_H
#define LMMS_FONT_HELPER_H
#include <QApplication>
#include <QFont>
constexpr int DEFAULT_FONT_SIZE = 12;
constexpr int SMALL_FONT_SIZE = 10;
constexpr int LARGE_FONT_SIZE = 14;
namespace lmms::gui
{
@@ -40,4 +44,4 @@ inline QFont adjustedToPixelSize(QFont font, int size)
} // namespace lmms::gui
#endif // LMMS_GUI_TEMPLATES_H
#endif // LMMS_FONT_HELPER_H

View File

@@ -182,7 +182,7 @@ public:
public slots:
//! Set range of y values
void setRange( float _min, float _max );
void setRange(float ymin, float ymax);
void setLength( int _size );
//! Update one sample

View File

@@ -26,13 +26,13 @@
#define LMMS_INSTRUMENT_SOUND_SHAPING_H
#include "ComboBoxModel.h"
#include "EnvelopeAndLfoParameters.h"
namespace lmms
{
class InstrumentTrack;
class EnvelopeAndLfoParameters;
class NotePlayHandle;
class SampleFrame;
@@ -52,14 +52,19 @@ public:
void processAudioBuffer( SampleFrame* _ab, const fpp_t _frames,
NotePlayHandle * _n );
enum class Target
{
Volume,
Cut,
Resonance,
Count
} ;
constexpr static auto NumTargets = static_cast<std::size_t>(Target::Count);
const EnvelopeAndLfoParameters& getVolumeParameters() const { return m_volumeParameters; }
EnvelopeAndLfoParameters& getVolumeParameters() { return m_volumeParameters; }
const EnvelopeAndLfoParameters& getCutoffParameters() const { return m_cutoffParameters; }
EnvelopeAndLfoParameters& getCutoffParameters() { return m_cutoffParameters; }
const EnvelopeAndLfoParameters& getResonanceParameters() const { return m_resonanceParameters; }
EnvelopeAndLfoParameters& getResonanceParameters() { return m_resonanceParameters; }
BoolModel& getFilterEnabledModel() { return m_filterEnabledModel; }
ComboBoxModel& getFilterModel() { return m_filterModel; }
FloatModel& getFilterCutModel() { return m_filterCutModel; }
FloatModel& getFilterResModel() { return m_filterResModel; }
f_cnt_t envFrames( const bool _only_vol = false ) const;
f_cnt_t releaseFrames() const;
@@ -74,22 +79,23 @@ public:
return "eldata";
}
private:
QString getVolumeNodeName() const;
QString getCutoffNodeName() const;
QString getResonanceNodeName() const;
private:
EnvelopeAndLfoParameters * m_envLfoParameters[NumTargets];
InstrumentTrack * m_instrumentTrack;
EnvelopeAndLfoParameters m_volumeParameters;
EnvelopeAndLfoParameters m_cutoffParameters;
EnvelopeAndLfoParameters m_resonanceParameters;
BoolModel m_filterEnabledModel;
ComboBoxModel m_filterModel;
FloatModel m_filterCutModel;
FloatModel m_filterResModel;
static const char *const targetNames[NumTargets][3];
friend class gui::InstrumentSoundShapingView;
} ;
};
} // namespace lmms

View File

@@ -58,7 +58,10 @@ private:
InstrumentSoundShaping * m_ss = nullptr;
TabWidget * m_targetsTabWidget;
EnvelopeAndLfoView * m_envLfoViews[InstrumentSoundShaping::NumTargets];
EnvelopeAndLfoView* m_volumeView;
EnvelopeAndLfoView* m_cutoffView;
EnvelopeAndLfoView* m_resonanceView;
// filter-stuff
GroupBox * m_filterGroupBox;
@@ -67,8 +70,7 @@ private:
Knob * m_filterResKnob;
QLabel* m_singleStreamInfoLabel;
} ;
};
} // namespace lmms::gui

View File

@@ -28,7 +28,7 @@
#include <limits>
#include "AudioPort.h"
#include "AudioBusHandle.h"
#include "InstrumentFunctions.h"
#include "InstrumentSoundShaping.h"
#include "Microtuner.h"
@@ -133,8 +133,7 @@ public:
// called by track
void saveTrackSpecificSettings( QDomDocument & _doc,
QDomElement & _parent ) override;
void saveTrackSpecificSettings(QDomDocument& doc, QDomElement& parent, bool presetMode) override;
void loadTrackSpecificSettings( const QDomElement & _this ) override;
using Track::setJournalling;
@@ -145,9 +144,9 @@ public:
const Plugin::Descriptor::SubPluginFeatures::Key* key = nullptr,
bool keyFromDnd = false);
AudioPort * audioPort()
AudioBusHandle* audioBusHandle()
{
return &m_audioPort;
return &m_audioBusHandle;
}
MidiPort * midiPort()
@@ -294,7 +293,7 @@ private:
FloatModel m_volumeModel;
FloatModel m_panningModel;
AudioPort m_audioPort;
AudioBusHandle m_audioBusHandle;
FloatModel m_pitchModel;
IntModel m_pitchRangeModel;

View File

@@ -28,11 +28,14 @@
#include <QWidget>
#include "ModelView.h"
#include "PixmapButton.h"
#include "SerializingObject.h"
#include "PluginView.h"
class QLabel;
class QLineEdit;
class QWidget;
class QMdiSubWindow;
namespace lmms
{
@@ -67,6 +70,9 @@ public:
InstrumentTrackWindow( InstrumentTrackView * _tv );
~InstrumentTrackWindow() override;
void resizeEvent(QResizeEvent* event) override;
// parent for all internal tab-widgets
TabWidget * tabWidgetParent()
{
@@ -130,6 +136,9 @@ private:
//! required to keep the old look when using a variable sized tab widget
void adjustTabSize(QWidget *w);
QMdiSubWindow* findSubWindowInParents();
void updateSubWindow();
InstrumentTrack * m_track;
InstrumentTrackView * m_itv;
@@ -139,6 +148,8 @@ private:
Knob * m_volumeKnob;
Knob * m_panningKnob;
Knob * m_pitchKnob;
PixmapButton *m_muteBtn;
PixmapButton *m_soloBtn;
QLabel * m_pitchLabel;
LcdSpinBox* m_pitchRangeSpinBox;
QLabel * m_pitchRangeLabel;
@@ -152,6 +163,7 @@ private:
InstrumentSoundShapingView * m_ssView;
InstrumentFunctionNoteStackingView* m_noteStackingView;
InstrumentFunctionArpeggioView* m_arpeggioView;
QWidget* m_instrumentFunctionsView; // container of note stacking and arpeggio
InstrumentMidiIOView * m_midiView;
EffectRackView * m_effectView;
InstrumentTuningView *m_tuningView;

View File

@@ -0,0 +1,77 @@
/*
* KeyboardShortcuts.h - Cross-platform handling of keyboard modifier keys
*
* Copyright (c) 2025 Michael Gregorius
* Copyright (c) 2025 Tres Finocchiaro <tres.finocchiaro/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* 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 (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef LMMS_KEYBOARDSHORTCUTS_H
#define LMMS_KEYBOARDSHORTCUTS_H
#include "lmmsconfig.h"
#include "qnamespace.h"
namespace lmms
{
// Qt on macOS maps:
// - ControlModifier --> Command keys
// - MetaModifier value --> Control keys
// - Qt::AltModifier --> Option keys
//
// Our UI hints need to be adjusted to accommodate for this
constexpr const char* UI_CTRL_KEY =
#ifdef LMMS_BUILD_APPLE
"";
#else
"Ctrl";
#endif
constexpr const char* UI_ALT_KEY =
#ifdef LMMS_BUILD_APPLE
"Option";
#else
"Alt";
#endif
// UI hint for copying OR linking a UI component
// this MUST be consistent with KBD_COPY_MODIFIER
constexpr const char* UI_COPY_KEY =
#ifdef LMMS_BUILD_APPLE
UI_ALT_KEY;
#else
UI_CTRL_KEY;
#endif
// Shortcut for copying OR linking a UI component
// this MUST be consistent with UI_COPY_KEY
constexpr Qt::KeyboardModifier KBD_COPY_MODIFIER =
#ifdef LMMS_BUILD_APPLE
Qt::AltModifier;
#else
Qt::ControlModifier;
#endif
} // namespace lmms
#endif // LMMS_KEYBOARDSHORTCUTS_H

View File

@@ -41,6 +41,10 @@ namespace gui
class LfoGraph : public QWidget, public ModelView
{
Q_OBJECT
Q_PROPERTY(QColor noAmountColor MEMBER m_noAmountColor)
Q_PROPERTY(QColor fullAmountColor MEMBER m_fullAmountColor)
public:
LfoGraph(QWidget* parent);
@@ -56,6 +60,8 @@ private:
QPixmap m_lfoGraph = embed::getIconPixmap("lfo_graph");
float m_randomGraph {0.};
QColor m_noAmountColor;
QColor m_fullAmountColor;
};
} // namespace gui

View File

@@ -26,6 +26,7 @@
#ifndef LMMS_GUI_LMMS_STYLE_H
#define LMMS_GUI_LMMS_STYLE_H
#include <QFileSystemWatcher>
#include <QProxyStyle>
@@ -60,6 +61,7 @@ public:
private:
QImage colorizeXpm( const char * const * xpm, const QBrush& fill ) const;
void hoverColors( bool sunken, bool hover, bool active, QColor& color, QColor& blend ) const;
QFileSystemWatcher m_styleReloader;
};

View File

@@ -29,6 +29,7 @@
#include <QTimer>
#include <QList>
#include <QMainWindow>
#include <QMdiArea>
#include "ConfigManager.h"
@@ -57,7 +58,7 @@ class MainWindow : public QMainWindow
public:
QMdiArea* workspace()
{
return m_workspace;
return static_cast<QMdiArea*>(m_workspace);
}
QWidget* toolBar()
@@ -72,6 +73,8 @@ public:
LMMS_EXPORT SubWindow* addWindowedWidget(QWidget *w, Qt::WindowFlags windowFlags = QFlag(0));
void refocus();
///
/// \brief Asks whether changes made to the project are to be saved.
///
@@ -195,14 +198,28 @@ private:
void finalize();
void toggleWindow( QWidget *window, bool forceShow = false );
void refocus();
void exportProject(bool multiExport = false);
void handleSaveResult(QString const & filename, bool songSavedSuccessfully);
bool guiSaveProject();
bool guiSaveProjectAs( const QString & filename );
QMdiArea * m_workspace;
class MovableQMdiArea : public QMdiArea
{
public:
MovableQMdiArea(QWidget* parent = nullptr);
~MovableQMdiArea() {}
protected:
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
private:
bool m_isBeingMoved;
int m_lastX;
int m_lastY;
};
MovableQMdiArea * m_workspace;
QWidget * m_toolBar;
QGridLayout * m_toolBarLayout;
@@ -257,7 +274,6 @@ signals:
} ;
} // namespace gui
} // namespace lmms

View File

@@ -1,8 +1,7 @@
/*
* panning_constants.h - declaration of some constants, concerning the
* panning of a note
* Metronome.h
*
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2024 saker
*
* This file is part of LMMS - https://lmms.io
*
@@ -23,19 +22,22 @@
*
*/
#ifndef LMMS_PANNING_CONSTANTS_H
#define LMMS_PANNING_CONSTANTS_H
#ifndef LMMS_METRONOME_H
#define LMMS_METRONOME_H
namespace lmms
#include <cstddef>
namespace lmms {
class Metronome
{
public:
bool active() const { return m_active; }
void setActive(bool active) { m_active = active; }
void processTick(int currentTick, int ticksPerBar, int beatsPerBar, size_t bufferOffset);
constexpr panning_t PanningRight = ( 0 + 100 );
constexpr panning_t PanningLeft = - PanningRight;
constexpr panning_t PanningCenter = 0;
constexpr panning_t DefaultPanning = PanningCenter;
private:
bool m_active = false;
};
} // namespace lmms
#endif // LMMS_PANNING_CONSTANTS_H
#endif // LMMS_METRONOME_H

View File

@@ -82,6 +82,9 @@ public:
// Split the list of notes on the given position
void splitNotes(const NoteVector& notes, TimePos pos);
// Split the list of notes along a line
void splitNotesAlongLine(const NoteVector notes, TimePos pos1, int key1, TimePos pos2, int key2, bool deleteShortEnds);
// clip-type stuff
inline Type type() const
{

View File

@@ -27,7 +27,7 @@
#include <cstdlib>
#include "Midi.h"
#include "panning_constants.h"
#include "panning.h"
#include "volume.h"
namespace lmms

View File

@@ -63,7 +63,6 @@ class MixerChannel : public ThreadableJob
FloatModel m_volumeModel;
QString m_name;
QMutex m_lock;
int m_channelIndex; // what channel index are we
bool m_queued; // are we queued up for rendering yet?
bool m_muted; // are we muted? updated per period so we don't have to call m_muteModel.value() twice
@@ -73,8 +72,15 @@ class MixerChannel : public ThreadableJob
// pointers to other channels that send to this one
MixerRouteVector m_receives;
int index() const { return m_channelIndex; }
void setIndex(int index) { m_channelIndex = index; }
bool isMaster() { return m_channelIndex == 0; }
bool requiresProcessing() const override { return true; }
void unmuteForSolo();
void unmuteSenderForSolo();
void unmuteReceiverForSolo();
auto color() const -> const std::optional<QColor>& { return m_color; }
void setColor(const std::optional<QColor>& color) { m_color = color; }
@@ -85,7 +91,7 @@ class MixerChannel : public ThreadableJob
private:
void doProcessing() override;
int m_channelIndex;
std::optional<QColor> m_color;
};
@@ -98,12 +104,12 @@ public:
mix_ch_t senderIndex() const
{
return m_from->m_channelIndex;
return m_from->index();
}
mix_ch_t receiverIndex() const
{
return m_to->m_channelIndex;
return m_to->index();
}
FloatModel * amount()

View File

@@ -50,6 +50,8 @@ protected:
void contextMenuEvent(QContextMenuEvent* event) override;
private:
void enterValue();
TrackView * m_tv;
};

View File

@@ -1,7 +1,7 @@
/*
* MixerChannelView.h - the mixer channel view
* MixerChannelView.h
*
* Copyright (c) 2022 saker <sakertooth@gmail.com>
* Copyright (c) 2024 saker
*
* This file is part of LMMS - https://lmms.io
*
@@ -22,8 +22,15 @@
*
*/
#ifndef MIXER_CHANNEL_VIEW_H
#define MIXER_CHANNEL_VIEW_H
#ifndef LMMS_GUI_MIXER_CHANNEL_VIEW_H
#define LMMS_GUI_MIXER_CHANNEL_VIEW_H
#include <QGraphicsView>
#include <QLabel>
#include <QLineEdit>
#include <QPixmap>
#include <QStackedWidget>
#include <QWidget>
#include "EffectRackView.h"
#include "Fader.h"
@@ -32,113 +39,99 @@
#include "PixmapButton.h"
#include "SendButtonIndicator.h"
#include <QGraphicsView>
#include <QLabel>
#include <QLineEdit>
#include <QPixmap>
#include <QWidget>
namespace lmms
{
class MixerChannel;
namespace lmms {
class MixerChannel;
}
namespace lmms::gui
namespace lmms::gui {
class PeakIndicator;
class MixerChannelView : public QWidget
{
class PeakIndicator;
Q_OBJECT
Q_PROPERTY(QBrush backgroundActive READ backgroundActive WRITE setBackgroundActive)
Q_PROPERTY(QColor strokeOuterActive READ strokeOuterActive WRITE setStrokeOuterActive)
Q_PROPERTY(QColor strokeOuterInactive READ strokeOuterInactive WRITE setStrokeOuterInactive)
Q_PROPERTY(QColor strokeInnerActive READ strokeInnerActive WRITE setStrokeInnerActive)
Q_PROPERTY(QColor strokeInnerInactive READ strokeInnerInactive WRITE setStrokeInnerInactive)
public:
MixerChannelView(QWidget* parent, MixerView* mixerView, int channelIndex);
void paintEvent(QPaintEvent* event) override;
void contextMenuEvent(QContextMenuEvent*) override;
void mousePressEvent(QMouseEvent*) override;
void mouseDoubleClickEvent(QMouseEvent*) override;
bool eventFilter(QObject* dist, QEvent* event) override;
constexpr int MIXER_CHANNEL_INNER_BORDER_SIZE = 3;
constexpr int MIXER_CHANNEL_OUTER_BORDER_SIZE = 1;
void reset();
int channelIndex() const { return m_channelIndex; }
void setChannelIndex(int index);
class MixerChannelView : public QWidget
{
Q_OBJECT
Q_PROPERTY(QBrush backgroundActive READ backgroundActive WRITE setBackgroundActive)
Q_PROPERTY(QColor strokeOuterActive READ strokeOuterActive WRITE setStrokeOuterActive)
Q_PROPERTY(QColor strokeOuterInactive READ strokeOuterInactive WRITE setStrokeOuterInactive)
Q_PROPERTY(QColor strokeInnerActive READ strokeInnerActive WRITE setStrokeInnerActive)
Q_PROPERTY(QColor strokeInnerInactive READ strokeInnerInactive WRITE setStrokeInnerInactive)
public:
enum class SendReceiveState
{
None, SendToThis, ReceiveFromThis
};
QBrush backgroundActive() const { return m_backgroundActive; }
void setBackgroundActive(const QBrush& c) { m_backgroundActive = c; }
MixerChannelView(QWidget* parent, MixerView* mixerView, int channelIndex);
void paintEvent(QPaintEvent* event) override;
void contextMenuEvent(QContextMenuEvent*) override;
void mousePressEvent(QMouseEvent*) override;
void mouseDoubleClickEvent(QMouseEvent*) override;
bool eventFilter(QObject* dist, QEvent* event) override;
QColor strokeOuterActive() const { return m_strokeOuterActive; }
void setStrokeOuterActive(const QColor& c) { m_strokeOuterActive = c; }
int channelIndex() const;
void setChannelIndex(int index);
QColor strokeOuterInactive() const { return m_strokeOuterInactive; }
void setStrokeOuterInactive(const QColor& c) { m_strokeOuterInactive = c; }
SendReceiveState sendReceiveState() const;
void setSendReceiveState(const SendReceiveState& state);
QColor strokeInnerActive() const { return m_strokeInnerActive; }
void setStrokeInnerActive(const QColor& c) { m_strokeInnerActive = c; }
QBrush backgroundActive() const;
void setBackgroundActive(const QBrush& c);
QColor strokeInnerInactive() const { return m_strokeInnerInactive; }
void setStrokeInnerInactive(const QColor& c) { m_strokeInnerInactive = c; }
QColor strokeOuterActive() const;
void setStrokeOuterActive(const QColor& c);
Fader* fader() const { return m_fader; }
QColor strokeOuterInactive() const;
void setStrokeOuterInactive(const QColor& c);
public slots:
void renameChannel();
void resetColor();
void selectColor();
void randomizeColor();
QColor strokeInnerActive() const;
void setStrokeInnerActive(const QColor& c);
private slots:
void renameFinished();
void removeChannel();
void removeUnusedChannels();
void moveChannelLeft();
void moveChannelRight();
QColor strokeInnerInactive() const;
void setStrokeInnerInactive(const QColor& c);
private:
bool confirmRemoval(int index);
QString elideName(const QString& name);
MixerChannel* mixerChannel() const;
auto isMasterChannel() const -> bool { return m_channelIndex == 0; }
void reset();
private:
SendButtonIndicator* m_sendButton;
QLabel* m_receiveArrow;
QStackedWidget* m_receiveArrowOrSendButton;
int m_receiveArrowStackedIndex = -1;
int m_sendButtonStackedIndex = -1;
public slots:
void renameChannel();
void resetColor();
void selectColor();
void randomizeColor();
Knob* m_sendKnob;
LcdWidget* m_channelNumberLcd;
QLineEdit* m_renameLineEdit;
QGraphicsView* m_renameLineEditView;
QLabel* m_sendArrow;
PixmapButton* m_muteButton;
PixmapButton* m_soloButton;
PeakIndicator* m_peakIndicator = nullptr;
Fader* m_fader;
EffectRackView* m_effectRackView;
MixerView* m_mixerView;
int m_channelIndex = 0;
bool m_inRename = false;
private slots:
void renameFinished();
void removeChannel();
void removeUnusedChannels();
void moveChannelLeft();
void moveChannelRight();
QBrush m_backgroundActive;
QColor m_strokeOuterActive;
QColor m_strokeOuterInactive;
QColor m_strokeInnerActive;
QColor m_strokeInnerInactive;
private:
bool confirmRemoval(int index);
QString elideName(const QString& name);
MixerChannel* mixerChannel() const;
auto isMasterChannel() const -> bool { return m_channelIndex == 0; }
private:
SendButtonIndicator* m_sendButton;
Knob* m_sendKnob;
LcdWidget* m_channelNumberLcd;
QLineEdit* m_renameLineEdit;
QGraphicsView* m_renameLineEditView;
QLabel* m_sendArrow;
QLabel* m_receiveArrow;
PixmapButton* m_muteButton;
PixmapButton* m_soloButton;
PeakIndicator* m_peakIndicator = nullptr;
Fader* m_fader;
EffectRackView* m_effectRackView;
MixerView* m_mixerView;
SendReceiveState m_sendReceiveState = SendReceiveState::None;
int m_channelIndex = 0;
bool m_inRename = false;
QBrush m_backgroundActive;
QColor m_strokeOuterActive;
QColor m_strokeOuterInactive;
QColor m_strokeInnerActive;
QColor m_strokeInnerInactive;
friend class MixerView;
};
friend class MixerView;
};
} // namespace lmms::gui
#endif
#endif // LMMS_GUI_MIXER_CHANNEL_VIEW_H

View File

@@ -30,10 +30,10 @@
#include <fftw3.h>
#include <memory>
#include <cstdlib>
#include "interpolation.h"
#include <cmath>
#include "Engine.h"
#include "lmms_constants.h"
#include "lmms_math.h"
#include "lmmsconfig.h"
#include "AudioEngine.h"
#include "OscillatorConstants.h"
@@ -114,7 +114,7 @@ public:
// now follow the wave-shape-routines...
static inline sample_t sinSample( const float _sample )
{
return sinf( _sample * F_2PI );
return std::sin(_sample * 2 * std::numbers::pi_v<float>);
}
static inline sample_t triangleSample( const float _sample )
@@ -163,7 +163,7 @@ public:
static inline sample_t noiseSample( const float )
{
return 1.0f - rand() * 2.0f / RAND_MAX;
return 1.0f - rand() * 2.0f / static_cast<float>(RAND_MAX);
}
static sample_t userWaveSample(const SampleBuffer* buffer, const float sample)
@@ -173,7 +173,7 @@ public:
const auto frame = absFraction(sample) * frames;
const auto f1 = static_cast<f_cnt_t>(frame);
return linearInterpolate(buffer->data()[f1][0], buffer->data()[(f1 + 1) % frames][0], fraction(frame));
return std::lerp(buffer->data()[f1][0], buffer->data()[(f1 + 1) % frames][0], fraction(frame));
}
struct wtSampleControl {
@@ -202,24 +202,25 @@ public:
{
assert(table != nullptr);
wtSampleControl control = getWtSampleControl(sample);
return linearInterpolate(table[control.band][control.f1],
table[control.band][control.f2], fraction(control.frame));
return std::lerp(table[control.band][control.f1], table[control.band][control.f2], fraction(control.frame));
}
sample_t wtSample(const OscillatorConstants::waveform_t* table, const float sample) const
{
assert(table != nullptr);
wtSampleControl control = getWtSampleControl(sample);
return linearInterpolate((*table)[control.band][control.f1],
(*table)[control.band][control.f2], fraction(control.frame));
return std::lerp(
(*table)[control.band][control.f1],
(*table)[control.band][control.f2],
fraction(control.frame)
);
}
inline sample_t wtSample(sample_t **table, const float sample) const
{
assert(table != nullptr);
wtSampleControl control = getWtSampleControl(sample);
return linearInterpolate(table[control.band][control.f1],
table[control.band][control.f2], fraction(control.frame));
return std::lerp(table[control.band][control.f1], table[control.band][control.f2], fraction(control.frame));
}
static inline int waveTableBandFromFreq(float freq)
@@ -237,7 +238,7 @@ public:
static inline float freqFromWaveTableBand(int band)
{
return 440.0f * std::pow(2.0f, (band * OscillatorConstants::SEMITONES_PER_TABLE - 69.0f) / 12.0f);
return 440.0f * std::exp2((band * OscillatorConstants::SEMITONES_PER_TABLE - 69.0f) / 12.0f);
}
private:

View File

@@ -49,50 +49,26 @@ public:
Mono
};
class BitRateSettings
{
public:
BitRateSettings(bitrate_t bitRate, bool isVariableBitRate) :
m_bitRate(bitRate),
m_isVariableBitRate(isVariableBitRate)
{}
bool isVariableBitRate() const { return m_isVariableBitRate; }
void setVariableBitrate(bool variableBitRate = true) { m_isVariableBitRate = variableBitRate; }
bitrate_t getBitRate() const { return m_bitRate; }
void setBitRate(bitrate_t bitRate) { m_bitRate = bitRate; }
private:
bitrate_t m_bitRate;
bool m_isVariableBitRate;
};
public:
OutputSettings( sample_rate_t sampleRate,
BitRateSettings const & bitRateSettings,
BitDepth bitDepth,
StereoMode stereoMode ) :
m_sampleRate(sampleRate),
m_bitRateSettings(bitRateSettings),
m_bitDepth(bitDepth),
m_stereoMode(stereoMode),
m_compressionLevel(0.625) // 5/8
OutputSettings(sample_rate_t sampleRate, bitrate_t bitRate, BitDepth bitDepth, StereoMode stereoMode)
: m_sampleRate(sampleRate)
, m_bitRate(bitRate)
, m_bitDepth(bitDepth)
, m_stereoMode(stereoMode)
, m_compressionLevel(0.625) // 5/8
{
}
OutputSettings( sample_rate_t sampleRate,
BitRateSettings const & bitRateSettings,
BitDepth bitDepth ) :
OutputSettings(sampleRate, bitRateSettings, bitDepth, StereoMode::Stereo )
OutputSettings(sample_rate_t sampleRate, bitrate_t bitRate, BitDepth bitDepth)
: OutputSettings(sampleRate, bitRate, bitDepth, StereoMode::Stereo)
{
}
sample_rate_t getSampleRate() const { return m_sampleRate; }
void setSampleRate(sample_rate_t sampleRate) { m_sampleRate = sampleRate; }
BitRateSettings const & getBitRateSettings() const { return m_bitRateSettings; }
void setBitRateSettings(BitRateSettings const & bitRateSettings) { m_bitRateSettings = bitRateSettings; }
bitrate_t bitrate() const { return m_bitRate; }
void setBitrate(bitrate_t bitrate) { m_bitRate = bitrate; }
BitDepth getBitDepth() const { return m_bitDepth; }
void setBitDepth(BitDepth bitDepth) { m_bitDepth = bitDepth; }
@@ -109,7 +85,7 @@ public:
private:
sample_rate_t m_sampleRate;
BitRateSettings m_bitRateSettings;
bitrate_t m_bitRate;
BitDepth m_bitDepth;
StereoMode m_stereoMode;
double m_compressionLevel;

View File

@@ -57,8 +57,7 @@ public:
gui::TrackView * createView( gui::TrackContainerView* tcv ) override;
Clip* createClip(const TimePos & pos) override;
void saveTrackSpecificSettings( QDomDocument & _doc,
QDomElement & _parent ) override;
void saveTrackSpecificSettings(QDomDocument& doc, QDomElement& parent, bool presetMode) override;
void loadTrackSpecificSettings( const QDomElement & _this ) override;
static PatternTrack* findPatternTrack(int pattern_num);

View File

@@ -78,8 +78,7 @@ private:
static int m_loadCount;
static bool m_buggedFile;
float m_attackCoeff;
float m_decayCoeff;
float m_coeff;
bool m_coeffNeedsUpdate;
} ;

View File

@@ -74,6 +74,7 @@ class PianoRoll : public QWidget
Q_PROPERTY(QColor noteModeColor MEMBER m_noteModeColor)
Q_PROPERTY(QColor noteColor MEMBER m_noteColor)
Q_PROPERTY(QColor stepNoteColor MEMBER m_stepNoteColor)
Q_PROPERTY(QColor currentStepNoteColor MEMBER m_currentStepNoteColor)
Q_PROPERTY(QColor ghostNoteColor MEMBER m_ghostNoteColor)
Q_PROPERTY(QColor noteTextColor MEMBER m_noteTextColor)
Q_PROPERTY(QColor ghostNoteTextColor MEMBER m_ghostNoteTextColor)
@@ -374,6 +375,8 @@ private:
QScrollBar * m_leftRightScroll;
QScrollBar * m_topBottomScroll;
void adjustLeftRightScoll(int value);
TimePos m_currentPosition;
bool m_recording;
bool m_doAutoQuantization{false};
@@ -453,9 +456,14 @@ private:
// did we start a mouseclick with shift pressed
bool m_startedWithShift;
// Variable that holds the position in ticks for the knife action
int m_knifeTickPos;
void updateKnifePos(QMouseEvent* me);
// Variables that hold the start and end position for the knife line
TimePos m_knifeStartTickPos;
int m_knifeStartKey;
TimePos m_knifeEndTickPos;
int m_knifeEndKey;
bool m_knifeDown;
void updateKnifePos(QMouseEvent* me, bool initial);
friend class PianoRollWindow;
@@ -469,6 +477,7 @@ private:
QColor m_noteModeColor;
QColor m_noteColor;
QColor m_stepNoteColor;
QColor m_currentStepNoteColor;
QColor m_noteTextColor;
QColor m_ghostNoteColor;
QColor m_ghostNoteTextColor;

View File

@@ -45,6 +45,7 @@ public:
void setInactiveGraphic( const QPixmap & _pm, bool _update = true );
QSize sizeHint() const override;
QSize minimumSizeHint() const override;
signals:
void doubleClicked();

View File

@@ -40,7 +40,7 @@ namespace lmms
{
class Track;
class AudioPort;
class AudioBusHandle;
class SampleFrame;
class LMMS_EXPORT PlayHandle : public ThreadableJob
@@ -65,7 +65,7 @@ public:
m_offset = p.m_offset;
m_affinity = p.m_affinity;
m_usesBuffer = p.m_usesBuffer;
m_audioPort = p.m_audioPort;
m_audioBusHandle = p.m_audioBusHandle;
return *this;
}
@@ -134,14 +134,14 @@ public:
m_usesBuffer = b;
}
AudioPort * audioPort()
AudioBusHandle* audioBusHandle()
{
return m_audioPort;
return m_audioBusHandle;
}
void setAudioPort( AudioPort * port )
void setAudioBusHandle(AudioBusHandle* busHandle)
{
m_audioPort = port;
m_audioBusHandle = busHandle;
}
void releaseBuffer();
@@ -156,7 +156,7 @@ private:
SampleFrame* m_playHandleBuffer;
bool m_bufferReleased;
bool m_usesBuffer;
AudioPort * m_audioPort;
AudioBusHandle* m_audioBusHandle;
} ;
using PlayHandleList = QList<PlayHandle*>;

View File

@@ -61,6 +61,7 @@ public:
~PluginFactory() = default;
static void setupSearchPaths();
static QList<QRegularExpression> getExcludePatterns(const char* envVar);
/// Returns the singleton instance of PluginFactory. You won't need to call
/// this directly, use pluginFactory instead.
@@ -104,6 +105,8 @@ private:
QHash<QString, QString> m_errors;
static std::unique_ptr<PluginFactory> s_instance;
static void filterPlugins(QSet<QFileInfo>& files);
};
//Short-hand function

View File

@@ -27,23 +27,22 @@
#include <QWidget>
#include "Plugin.h"
#include "ModelView.h"
#include "Plugin.h"
namespace lmms::gui
{
namespace lmms::gui {
class LMMS_EXPORT PluginView : public QWidget, public ModelView
class LMMS_EXPORT PluginView : public QWidget, public ModelView
{
public:
PluginView( Plugin * _plugin, QWidget * _parent ) :
QWidget( _parent ),
ModelView( _plugin, this )
PluginView(Plugin* _plugin, QWidget* _parent)
: QWidget(_parent)
, ModelView(_plugin, this)
{
}
} ;
virtual bool isResizable() const { return false; }
};
} // namespace lmms::gui

View File

@@ -25,7 +25,8 @@
#ifndef LMMS_QUADRATURE_LFO_H
#define LMMS_QUADRATURE_LFO_H
#include "lmms_math.h"
#include <numbers>
#include <cmath>
namespace lmms
{
@@ -37,7 +38,7 @@ public:
QuadratureLfo( int sampleRate ) :
m_frequency(0),
m_phase(0),
m_offset(D_PI / 2)
m_offset(std::numbers::pi * 0.5)
{
setSampleRate(sampleRate);
}
@@ -64,7 +65,7 @@ public:
inline void setSampleRate ( int samplerate )
{
m_samplerate = samplerate;
m_twoPiOverSr = F_2PI / samplerate;
m_twoPiOverSr = 2 * std::numbers::pi_v<float> / samplerate;
m_increment = m_frequency * m_twoPiOverSr;
}
@@ -77,14 +78,10 @@ public:
void tick( float *l, float *r )
{
*l = sinf( m_phase );
*r = sinf( m_phase + m_offset );
*l = std::sin(m_phase);
*r = std::sin(m_phase + m_offset);
m_phase += m_increment;
while (m_phase >= D_2PI)
{
m_phase -= D_2PI;
}
m_phase = std::fmod(m_phase, 2 * std::numbers::pi);
}
private:

View File

@@ -71,7 +71,6 @@
#include <QProcess>
#include <QThread>
#include <QString>
#include <QUuid>
#ifndef SYNC_WITH_SHM_FIFO
#include <poll.h>
@@ -125,7 +124,7 @@ public:
m_master( true ),
m_lockDepth( 0 )
{
m_data.create(QUuid::createUuid().toString().toStdString());
m_data.create();
m_data->startPtr = m_data->endPtr = 0;
static int k = 0;
m_data->dataSem.semKey = ( getpid()<<10 ) + ++k;
@@ -398,7 +397,7 @@ public:
message & addInt( int _i )
{
char buf[32];
sprintf( buf, "%d", _i );
std::snprintf(buf, 32, "%d", _i);
data.emplace_back( buf );
return *this;
}
@@ -406,7 +405,7 @@ public:
message & addFloat( float _f )
{
char buf[32];
sprintf( buf, "%f", _f );
std::snprintf(buf, 32, "%f", _f);
data.emplace_back( buf );
return *this;
}

View File

@@ -309,7 +309,7 @@ bool RemotePluginClient::processMessage( const message & _m )
default:
{
char buf[64];
sprintf( buf, "undefined message: %d\n", (int) _m.id );
std::snprintf(buf, 64, "undefined message: %d\n", _m.id);
debugMessage( buf );
break;
}

View File

@@ -84,7 +84,7 @@ public:
m_sum -= m_buffer[ m_pos ];
m_sum += m_buffer[ m_pos ] = in * in;
++m_pos %= m_size;
return sqrtf( m_sum * m_sizef );
return std::sqrt(m_sum * m_sizef);
}
private:

View File

@@ -27,6 +27,8 @@
#include "ClipView.h"
#include "SampleThumbnail.h"
namespace lmms
{
@@ -64,6 +66,7 @@ protected:
private:
SampleClip * m_clip;
SampleThumbnail m_sampleThumbnail;
QPixmap m_paintPixmap;
bool splitClip( const TimePos pos ) override;
} ;

View File

@@ -38,13 +38,13 @@ namespace lmms
class PatternTrack;
class SampleClip;
class Track;
class AudioPort;
class AudioBusHandle;
class LMMS_EXPORT SamplePlayHandle : public PlayHandle
{
public:
SamplePlayHandle(Sample* sample, bool ownAudioPort = true);
SamplePlayHandle(Sample* sample, bool ownAudioBusHandle = true);
SamplePlayHandle( const QString& sampleFile );
SamplePlayHandle( SampleClip* clip );
~SamplePlayHandle() override;
@@ -88,7 +88,7 @@ private:
f_cnt_t m_frame;
Sample::PlaybackState m_state;
const bool m_ownAudioPort;
const bool m_ownAudioBusHandle;
FloatModel m_defaultVolumeModel;
FloatModel * m_volumeModel;

143
include/SampleThumbnail.h Normal file
View File

@@ -0,0 +1,143 @@
/*
* SampleThumbnail.h
*
* Copyright (c) 2024 Khoi Dau <casboi86@gmail.com>
* Copyright (c) 2024 Sotonye Atemie <sakertooth@gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* 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 (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef LMMS_SAMPLE_THUMBNAIL_H
#define LMMS_SAMPLE_THUMBNAIL_H
#include <QDateTime>
#include <QPainter>
#include <QRect>
#include <memory>
#include "Sample.h"
#include "lmms_export.h"
namespace lmms {
/**
Allows for visualizing sample data.
On construction, thumbnails will be generated
at logarathmic intervals of downsampling. Those cached thumbnails will then be further downsampled on the fly and
transformed in various ways to create the desired waveform.
Given that we are dealing with far less data to generate
the visualization however (i.e., we are not reading from original sample data when drawing), this provides a
significant performance boost that wouldn't be possible otherwise.
*/
class LMMS_EXPORT SampleThumbnail
{
public:
struct VisualizeParameters
{
QRect sampleRect; //!< A rectangle that covers the entire range of samples.
QRect drawRect; //!< Specifies the location in `sampleRect` where the waveform will be drawn. Equals
//!< `sampleRect` when null.
QRect viewportRect; //!< Clips `drawRect`. Equals `drawRect` when null.
float amplification = 1.0f; //!< The amount of amplification to apply to the waveform.
float sampleStart = 0.0f; //!< Where the sample begins for drawing.
float sampleEnd = 1.0f; //!< Where the sample ends for drawing.
bool reversed = false; //!< Determines if the waveform is drawn in reverse or not.
};
SampleThumbnail() = default;
SampleThumbnail(const Sample& sample);
void visualize(VisualizeParameters parameters, QPainter& painter) const;
private:
class Thumbnail
{
public:
struct Peak
{
Peak() = default;
Peak(float min, float max)
: min(min)
, max(max)
{
}
Peak(const SampleFrame& frame)
: min(std::min(frame.left(), frame.right()))
, max(std::max(frame.left(), frame.right()))
{
}
Peak operator+(const Peak& other) const { return Peak(std::min(min, other.min), std::max(max, other.max)); }
Peak operator+(const SampleFrame& frame) const { return *this + Peak{frame}; }
float min = std::numeric_limits<float>::max();
float max = std::numeric_limits<float>::min();
};
Thumbnail() = default;
Thumbnail(std::vector<Peak> peaks, double samplesPerPeak);
Thumbnail(const float* buffer, size_t size, size_t width);
Thumbnail zoomOut(float factor) const;
Peak& operator[](size_t index) { return m_peaks[index]; }
const Peak& operator[](size_t index) const { return m_peaks[index]; }
int width() const { return m_peaks.size(); }
double samplesPerPeak() const { return m_samplesPerPeak; }
private:
std::vector<Peak> m_peaks;
double m_samplesPerPeak = 0.0;
};
struct SampleThumbnailEntry
{
QString filePath;
QDateTime lastModified;
friend bool operator==(const SampleThumbnailEntry& first, const SampleThumbnailEntry& second)
{
return first.filePath == second.filePath && first.lastModified == second.lastModified;
}
};
struct Hash
{
std::size_t operator()(const SampleThumbnailEntry& entry) const noexcept { return qHash(entry.filePath); }
};
using ThumbnailCache = std::vector<Thumbnail>;
std::shared_ptr<ThumbnailCache> m_thumbnailCache = std::make_shared<ThumbnailCache>();
inline static std::unordered_map<SampleThumbnailEntry, std::shared_ptr<ThumbnailCache>, Hash> s_sampleThumbnailCacheMap;
};
} // namespace lmms
#endif // LMMS_SAMPLE_THUMBNAIL_H

View File

@@ -25,7 +25,7 @@
#ifndef LMMS_SAMPLE_TRACK_H
#define LMMS_SAMPLE_TRACK_H
#include "AudioPort.h"
#include "AudioBusHandle.h"
#include "Track.h"
@@ -54,8 +54,7 @@ public:
Clip* createClip(const TimePos & pos) override;
void saveTrackSpecificSettings( QDomDocument & _doc,
QDomElement & _parent ) override;
void saveTrackSpecificSettings(QDomDocument& doc, QDomElement& parent, bool presetMode) override;
void loadTrackSpecificSettings( const QDomElement & _this ) override;
inline IntModel * mixerChannelModel()
@@ -63,9 +62,9 @@ public:
return &m_mixerChannelModel;
}
inline AudioPort * audioPort()
inline AudioBusHandle* audioBusHandle()
{
return &m_audioPort;
return &m_audioBusHandle;
}
QString nodeName() const override
@@ -96,7 +95,7 @@ private:
FloatModel m_volumeModel;
FloatModel m_panningModel;
IntModel m_mixerChannelModel;
AudioPort m_audioPort;
AudioBusHandle m_audioBusHandle;
bool m_isPlaying;

View File

@@ -28,6 +28,7 @@
#include <QWidget>
#include "ModelView.h"
#include "PixmapButton.h"
#include "SampleTrack.h"
#include "SerializingObject.h"
@@ -90,6 +91,8 @@ private:
QLineEdit * m_nameLineEdit;
Knob * m_volumeKnob;
Knob * m_panningKnob;
PixmapButton *m_muteBtn;
PixmapButton *m_soloBtn;
MixerChannelLcdSpinBox * m_mixerChannelNumber;
EffectRackView * m_effectRack;

View File

@@ -72,10 +72,10 @@ protected slots:
private slots:
// General settings widget.
void toggleDisplaydBFS(bool enabled);
void toggleTooltips(bool enabled);
void toggleDisplayWaveform(bool enabled);
void toggleNoteLabels(bool enabled);
void toggleShowFaderTicks(bool enabled);
void toggleCompactTrackButtons(bool enabled);
void toggleOneInstrumentTrackWindow(bool enabled);
void toggleSideBarOnRight(bool enabled);
@@ -134,10 +134,10 @@ private:
TabBar * m_tabBar;
// General settings widgets.
bool m_displaydBFS;
bool m_tooltips;
bool m_displayWaveform;
bool m_printNoteLabels;
bool m_showFaderTicks;
bool m_compactTrackButtons;
bool m_oneInstrumentTrackWindow;
bool m_sideBarOnRight;

View File

@@ -44,6 +44,7 @@ public:
SharedMemoryData() noexcept;
SharedMemoryData(std::string&& key, bool readOnly);
SharedMemoryData(std::string&& key, std::size_t size, bool readOnly);
SharedMemoryData(std::size_t size, bool readOnly);
~SharedMemoryData();
SharedMemoryData(SharedMemoryData&& other) noexcept;
@@ -64,6 +65,7 @@ public:
const std::string& key() const noexcept { return m_key; }
void* get() const noexcept { return m_ptr; }
std::size_t size_bytes() const noexcept;
private:
std::string m_key;
@@ -95,6 +97,11 @@ public:
m_data = detail::SharedMemoryData{std::move(key), sizeof(T), std::is_const_v<T>};
}
void create()
{
m_data = detail::SharedMemoryData{sizeof(T), std::is_const_v<T>};
}
void detach() noexcept
{
m_data = detail::SharedMemoryData{};
@@ -103,6 +110,9 @@ public:
const std::string& key() const noexcept { return m_data.key(); }
T* get() const noexcept { return static_cast<T*>(m_data.get()); }
std::size_t size() const noexcept { return get() ? 1 : 0; }
std::size_t size_bytes() const noexcept { return get() ? sizeof(T) : 0; }
T* operator->() const noexcept { return get(); }
T& operator*() const noexcept { return *get(); }
explicit operator bool() const noexcept { return get() != nullptr; }
@@ -132,6 +142,11 @@ public:
m_data = detail::SharedMemoryData{std::move(key), size * sizeof(T), std::is_const_v<T>};
}
void create(std::size_t size)
{
m_data = detail::SharedMemoryData{size * sizeof(T), std::is_const_v<T>};
}
void detach() noexcept
{
m_data = detail::SharedMemoryData{};
@@ -140,6 +155,9 @@ public:
const std::string& key() const noexcept { return m_data.key(); }
T* get() const noexcept { return static_cast<T*>(m_data.get()); }
std::size_t size() const noexcept { return m_data.size_bytes() / sizeof(T); }
std::size_t size_bytes() const noexcept { return m_data.size_bytes(); }
T& operator[](std::size_t index) const noexcept { return get()[index]; }
explicit operator bool() const noexcept { return get() != nullptr; }

View File

@@ -33,6 +33,7 @@
#include "AudioEngine.h"
#include "Controller.h"
#include "Metronome.h"
#include "lmms_constants.h"
#include "MeterModel.h"
#include "Timeline.h"
@@ -375,6 +376,8 @@ public:
const std::string& syncKey() const noexcept { return m_vstSyncController.sharedMemoryKey(); }
Metronome& metronome() { return m_metronome; }
public slots:
void playSong();
void record();
@@ -448,6 +451,7 @@ private:
void restoreKeymapStates(const QDomElement &element);
void processAutomations(const TrackList& tracks, TimePos timeStart, fpp_t frames);
void processMetronome(size_t bufferOffset);
void setModified(bool value);
@@ -513,6 +517,8 @@ private:
AutomatedValueMap m_oldAutomatedValues;
Metronome m_metronome;
friend class Engine;
friend class gui::SongEditor;
friend class gui::ControllerRackView;

View File

@@ -128,6 +128,8 @@ private:
QScrollBar * m_leftRightScroll;
void adjustLeftRightScoll(int value);
LcdSpinBox * m_tempoSpinBox;
TimeLineWidget * m_timeLine;

View File

@@ -66,11 +66,6 @@ class StepRecorder : public QObject
return m_isRecording;
}
QColor curStepNoteColor() const
{
return QColor(245,3,139); // radiant pink
}
private slots:
void removeNotesReleasedForTooLong();

View File

@@ -71,6 +71,10 @@ public:
int titleBarHeight() const;
int frameWidth() const;
// TODO Needed to update the title bar when replacing instruments.
// Update works automatically if QMdiSubWindows are used.
void updateTitleBar();
protected:
// hook the QWidget move/resize events to update the tracked geometry
void moveEvent( QMoveEvent * event ) override;
@@ -78,6 +82,8 @@ protected:
void paintEvent( QPaintEvent * pe ) override;
void changeEvent( QEvent * event ) override;
QPushButton* addTitleButton(const std::string& iconName, const QString& toolTip);
signals:
void focusLost();

View File

@@ -26,6 +26,8 @@
#ifndef LMMS_TIME_POS_H
#define LMMS_TIME_POS_H
#include <algorithm>
#include <cassert>
#include "lmms_export.h"
#include "lmms_basics.h"
@@ -51,8 +53,8 @@ class LMMS_EXPORT TimeSig
public:
TimeSig( int num, int denom );
TimeSig( const MeterModel &model );
int numerator() const;
int denominator() const;
int numerator() const { return m_num; }
int denominator() const { return m_denom; }
private:
int m_num;
int m_denom;
@@ -69,42 +71,72 @@ public:
TimePos( const tick_t ticks = 0 );
TimePos quantize(float) const;
TimePos toAbsoluteBar() const;
TimePos toAbsoluteBar() const { return getBar() * s_ticksPerBar; }
TimePos& operator+=( const TimePos& time );
TimePos& operator-=( const TimePos& time );
TimePos& operator+=(const TimePos& time)
{
m_ticks += time.m_ticks;
return *this;
}
TimePos& operator-=(const TimePos& time)
{
m_ticks -= time.m_ticks;
return *this;
}
// return the bar, rounded down and 0-based
bar_t getBar() const;
bar_t getBar() const { return m_ticks / s_ticksPerBar; }
// return the bar, rounded up and 0-based
bar_t nextFullBar() const;
bar_t nextFullBar() const { return (m_ticks + (s_ticksPerBar - 1)) / s_ticksPerBar; }
void setTicks( tick_t ticks );
tick_t getTicks() const;
void setTicks(tick_t ticks) { m_ticks = ticks; }
tick_t getTicks() const { return m_ticks; }
operator int() const;
operator int() const { return m_ticks; }
tick_t ticksPerBeat(const TimeSig& sig) const { return ticksPerBar(sig) / sig.numerator(); }
tick_t ticksPerBeat( const TimeSig &sig ) const;
// Remainder ticks after bar is removed
tick_t getTickWithinBar( const TimeSig &sig ) const;
tick_t getTickWithinBar(const TimeSig& sig) const { return m_ticks % ticksPerBar(sig); }
// Returns the beat position inside the bar, 0-based
tick_t getBeatWithinBar( const TimeSig &sig ) const;
tick_t getBeatWithinBar(const TimeSig& sig) const { return getTickWithinBar(sig) / ticksPerBeat(sig); }
// Remainder ticks after bar and beat are removed
tick_t getTickWithinBeat( const TimeSig &sig ) const;
tick_t getTickWithinBeat(const TimeSig& sig) const { return getTickWithinBar(sig) % ticksPerBeat(sig); }
// calculate number of frame that are needed this time
f_cnt_t frames( const float framesPerTick ) const;
f_cnt_t frames(const float framesPerTick) const
{
// Before, step notes used to have negative length. This
// assert is a safeguard against negative length being
// introduced again (now using Note Types instead #5902)
assert(m_ticks >= 0);
return static_cast<f_cnt_t>(m_ticks * framesPerTick);
}
double getTimeInMilliseconds( bpm_t beatsPerMinute ) const;
double getTimeInMilliseconds(bpm_t beatsPerMinute) const { return ticksToMilliseconds(getTicks(), beatsPerMinute); }
static TimePos fromFrames( const f_cnt_t frames, const float framesPerTick );
static tick_t ticksPerBar();
static tick_t ticksPerBar( const TimeSig &sig );
static int stepsPerBar();
static void setTicksPerBar( tick_t tpt );
static TimePos stepPosition( int step );
static double ticksToMilliseconds( tick_t ticks, bpm_t beatsPerMinute );
static double ticksToMilliseconds( double ticks, bpm_t beatsPerMinute );
static TimePos fromFrames(const f_cnt_t frames, const float framesPerTick)
{
return TimePos(static_cast<int>(frames / framesPerTick));
}
static tick_t ticksPerBar() { return s_ticksPerBar; }
static tick_t ticksPerBar(const TimeSig& sig) { return DefaultTicksPerBar * sig.numerator() / sig.denominator(); }
static int stepsPerBar() { return std::max(1, ticksPerBar() / DefaultBeatsPerBar); }
static void setTicksPerBar(tick_t ticks) { s_ticksPerBar = ticks; }
static TimePos stepPosition(int step) { return step * ticksPerBar() / stepsPerBar(); }
static double ticksToMilliseconds(tick_t ticks, bpm_t beatsPerMinute)
{
return ticksToMilliseconds(static_cast<double>(ticks), beatsPerMinute);
}
static double ticksToMilliseconds(double ticks, bpm_t beatsPerMinute) { return (ticks * 1250) / beatsPerMinute; }
private:
tick_t m_ticks;

View File

@@ -107,19 +107,17 @@ public:
virtual gui::TrackView * createView( gui::TrackContainerView * view ) = 0;
virtual Clip * createClip( const TimePos & pos ) = 0;
virtual void saveTrackSpecificSettings( QDomDocument & doc,
QDomElement & parent ) = 0;
virtual void saveTrackSpecificSettings(QDomDocument& doc, QDomElement& parent, bool presetMode) = 0;
virtual void loadTrackSpecificSettings( const QDomElement & element ) = 0;
// Saving and loading of presets which do not necessarily contain all the track information
void savePreset(QDomDocument & doc, QDomElement & element);
void loadPreset(const QDomElement & element);
// Saving and loading of full tracks
void saveSettings( QDomDocument & doc, QDomElement & element ) override;
void loadSettings( const QDomElement & element ) override;
void setSimpleSerializing()
{
m_simpleSerializingMode = true;
}
// -- for usage by Clip only ---------------
Clip * addClip( Clip * clip );
void removeClip( Clip * clip );
@@ -209,6 +207,10 @@ public slots:
void toggleSolo();
private:
void saveTrack(QDomDocument& doc, QDomElement& element, bool presetMode);
void loadTrack(const QDomElement& element, bool presetMode);
private:
TrackContainer* m_trackContainer;
Type m_type;
@@ -217,13 +219,11 @@ private:
protected:
BoolModel m_mutedModel;
BoolModel m_soloModel;
private:
BoolModel m_soloModel;
bool m_mutedBeforeSolo;
bool m_simpleSerializingMode;
clipVector m_clips;
QMutex m_processingLock;

View File

@@ -168,8 +168,6 @@ public slots:
protected:
static const int DEFAULT_PIXELS_PER_BAR = 128;
void resizeEvent( QResizeEvent * ) override;
TimePos m_currentPosition;

View File

@@ -1,7 +1,7 @@
/*
* SampleWaveform.h
* TrackGrip.h - Grip that can be used to move tracks
*
* Copyright (c) 2023 saker <sakertooth@gmail.com>
* Copyright (c) 2024- Michael Gregorius
*
* This file is part of LMMS - https://lmms.io
*
@@ -22,28 +22,47 @@
*
*/
#ifndef LMMS_GUI_SAMPLE_WAVEFORM_H
#define LMMS_GUI_SAMPLE_WAVEFORM_H
#ifndef LMMS_GUI_TRACK_GRIP_H
#define LMMS_GUI_TRACK_GRIP_H
#include <QPainter>
#include <QWidget>
#include "Sample.h"
#include "lmms_export.h"
namespace lmms::gui {
class LMMS_EXPORT SampleWaveform
class QPixmap;
namespace lmms
{
class Track;
namespace gui
{
class TrackGrip : public QWidget
{
Q_OBJECT
public:
struct Parameters
{
const SampleFrame* buffer;
size_t size;
float amplification;
bool reversed;
};
TrackGrip(Track* track, QWidget* parent = 0);
~TrackGrip() override = default;
static void visualize(Parameters parameters, QPainter& painter, const QRect& rect);
signals:
void grabbed();
void released();
protected:
void mousePressEvent(QMouseEvent*) override;
void mouseReleaseEvent(QMouseEvent*) override;
void paintEvent(QPaintEvent*) override;
private:
Track* m_track = nullptr;
bool m_isGrabbed = false;
static QPixmap* s_grabbedPixmap;
static QPixmap* s_releasedPixmap;
};
} // namespace lmms::gui
#endif // LMMS_GUI_SAMPLE_WAVEFORM_H
} // namespace gui
} // namespace lmms
#endif // LMMS_GUI_TRACK_GRIP_H

View File

@@ -33,6 +33,7 @@ namespace lmms::gui
{
class PixmapButton;
class TrackGrip;
class TrackView;
class TrackOperationsWidget : public QWidget
@@ -42,6 +43,7 @@ public:
TrackOperationsWidget( TrackView * parent );
~TrackOperationsWidget() override = default;
TrackGrip* getTrackGrip() const { return m_trackGrip; }
protected:
void mousePressEvent( QMouseEvent * me ) override;
@@ -65,6 +67,7 @@ private slots:
private:
TrackView * m_trackView;
TrackGrip* m_trackGrip;
QPushButton * m_trackOps;
PixmapButton * m_muteBtn;
PixmapButton * m_soloBtn;

View File

@@ -48,12 +48,12 @@ class FadeButton;
class TrackContainerView;
const int DEFAULT_SETTINGS_WIDGET_WIDTH = 256;
const int DEFAULT_SETTINGS_WIDGET_WIDTH = 260;
const int TRACK_OP_WIDTH = 78;
// This shaves 150-ish pixels off track buttons,
// ruled from config: ui.compacttrackbuttons
const int DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT = 128;
const int TRACK_OP_WIDTH_COMPACT = 62;
const int DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT = 136;
const int TRACK_OP_WIDTH_COMPACT = TRACK_OP_WIDTH;
class TrackView : public QWidget, public ModelView, public JournallingObject
@@ -171,7 +171,8 @@ private:
private slots:
void createClipView( lmms::Clip * clip );
void muteChanged();
void onTrackGripGrabbed();
void onTrackGripReleased();
} ;

View File

@@ -25,13 +25,8 @@
#ifndef LMMS_INTERPOLATION_H
#define LMMS_INTERPOLATION_H
#ifndef __USE_XOPEN
#define __USE_XOPEN
#endif
#include <cmath>
#include "lmms_constants.h"
#include "lmms_math.h"
#include <numbers>
namespace lmms
{
@@ -69,27 +64,21 @@ inline float hermiteInterpolate( float x0, float x1, float x2, float x3,
inline float cubicInterpolate( float v0, float v1, float v2, float v3, float x )
{
float frsq = x*x;
float frcu = frsq*v0;
float t1 = v3 + 3*v1;
float frsq = x * x;
float frcu = frsq * v0;
float t1 = v1 * 3.f + v3;
return( v1 + fastFmaf( 0.5f, frcu, x ) * ( v2 - frcu * ( 1.0f/6.0f ) -
fastFmaf( t1, ( 1.0f/6.0f ), -v0 ) * ( 1.0f/3.0f ) ) + frsq * x * ( t1 *
( 1.0f/6.0f ) - 0.5f * v2 ) + frsq * fastFmaf( 0.5f, v2, -v1 ) );
return v1 + (0.5f * frcu + x) * (v2 - frcu * (1.0f / 6.0f) -
(t1 * (1.0f / 6.0f) - v0) * (1.0f / 3.0f)) + frsq * x * (t1 *
(1.0f / 6.0f) - 0.5f * v2) + frsq * (0.5f * v2 - v1);
}
inline float cosinusInterpolate( float v0, float v1, float x )
{
const float f = ( 1.0f - cosf( x * F_PI ) ) * 0.5f;
return fastFmaf( f, v1-v0, v0 );
}
inline float linearInterpolate( float v0, float v1, float x )
{
return fastFmaf( x, v1-v0, v0 );
const float f = (1.0f - std::cos(x * std::numbers::pi_v<float>)) * 0.5f;
return f * (v1 - v0) + v0;
}
@@ -104,7 +93,7 @@ inline float optimalInterpolate( float v0, float v1, float x )
const float c2 = even * -0.004541102062639801;
const float c3 = odd * -1.57015627178718420;
return fastFmaf( fastFmaf( fastFmaf( c3, z, c2 ), z, c1 ), z, c0 );
return ((c3 * z + c2) * z + c1) * z + c0;
}
@@ -121,7 +110,7 @@ inline float optimal4pInterpolate( float v0, float v1, float v2, float v3, float
const float c2 = even1 * -0.246185007019907091 + even2 * 0.24614027139700284;
const float c3 = odd1 * -0.36030925263849456 + odd2 * 0.10174985775982505;
return fastFmaf( fastFmaf( fastFmaf( c3, z, c2 ), z, c1 ), z, c0 );
return ((c3 * z + c2) * z + c1) * z + c0;
}
@@ -132,7 +121,7 @@ inline float lagrangeInterpolate( float v0, float v1, float v2, float v3, float
const float c1 = v2 - v0 * ( 1.0f / 3.0f ) - v1 * 0.5f - v3 * ( 1.0f / 6.0f );
const float c2 = 0.5f * (v0 + v2) - v1;
const float c3 = ( 1.0f/6.0f ) * ( v3 - v0 ) + 0.5f * ( v1 - v2 );
return fastFmaf( fastFmaf( fastFmaf( c3, x, c2 ), x, c1 ), x, c0 );
return ((c3 * x + c2) * x + c1) * x + c0;
}

View File

@@ -69,15 +69,6 @@ constexpr char LADSPA_PATH_SEPERATOR =
#define LMMS_STRINGIFY(s) LMMS_STR(s)
#define LMMS_STR(PN) #PN
// Abstract away GUI CTRL key (linux/windows) vs ⌘ (apple)
constexpr const char* UI_CTRL_KEY =
#ifdef LMMS_BUILD_APPLE
"";
#else
"Ctrl";
#endif
} // namespace lmms
#endif // LMMS_TYPES_H

View File

@@ -28,43 +28,17 @@
namespace lmms
{
constexpr long double LD_PI = 3.14159265358979323846264338327950288419716939937510;
constexpr long double LD_2PI = LD_PI * 2.0;
constexpr long double LD_PI_2 = LD_PI * 0.5;
constexpr long double LD_PI_R = 1.0 / LD_PI;
constexpr long double LD_PI_SQR = LD_PI * LD_PI;
constexpr long double LD_E = 2.71828182845904523536028747135266249775724709369995;
constexpr long double LD_E_R = 1.0 / LD_E;
constexpr long double LD_SQRT_2 = 1.41421356237309504880168872420969807856967187537695;
constexpr double D_PI = (double) LD_PI;
constexpr double D_2PI = (double) LD_2PI;
constexpr double D_PI_2 = (double) LD_PI_2;
constexpr double D_PI_R = (double) LD_PI_R;
constexpr double D_PI_SQR = (double) LD_PI_SQR;
constexpr double D_E = (double) LD_E;
constexpr double D_E_R = (double) LD_E_R;
constexpr double D_SQRT_2 = (double) LD_SQRT_2;
constexpr float F_PI = (float) LD_PI;
constexpr float F_2PI = (float) LD_2PI;
constexpr float F_PI_2 = (float) LD_PI_2;
constexpr float F_PI_R = (float) LD_PI_R;
constexpr float F_PI_SQR = (float) LD_PI_SQR;
constexpr float F_E = (float) LD_E;
constexpr float F_E_R = (float) LD_E_R;
constexpr float F_SQRT_2 = (float) LD_SQRT_2;
constexpr float F_EPSILON = 1.0e-10f; // 10^-10
// Prefer using `approximatelyEqual()` from lmms_math.h rather than
// using this directly
inline constexpr float F_EPSILON = 1.0e-10f; // 10^-10
// Microtuner
constexpr unsigned int MaxScaleCount = 10; //!< number of scales per project
constexpr unsigned int MaxKeymapCount = 10; //!< number of keyboard mappings per project
inline constexpr unsigned MaxScaleCount = 10; //!< number of scales per project
inline constexpr unsigned MaxKeymapCount = 10; //!< number of keyboard mappings per project
// Frequency ranges (in Hz).
// Arbitrary low limit for logarithmic frequency scale; >1 Hz.
constexpr int LOWEST_LOG_FREQ = 5;
inline constexpr auto LOWEST_LOG_FREQ = 5;
// Full range is defined by LOWEST_LOG_FREQ and current sample rate.
enum class FrequencyRange
@@ -76,14 +50,14 @@ enum class FrequencyRange
High
};
constexpr int FRANGE_AUDIBLE_START = 20;
constexpr int FRANGE_AUDIBLE_END = 20000;
constexpr int FRANGE_BASS_START = 20;
constexpr int FRANGE_BASS_END = 300;
constexpr int FRANGE_MIDS_START = 200;
constexpr int FRANGE_MIDS_END = 5000;
constexpr int FRANGE_HIGH_START = 4000;
constexpr int FRANGE_HIGH_END = 20000;
inline constexpr auto FRANGE_AUDIBLE_START = 20;
inline constexpr auto FRANGE_AUDIBLE_END = 20000;
inline constexpr auto FRANGE_BASS_START = 20;
inline constexpr auto FRANGE_BASS_END = 300;
inline constexpr auto FRANGE_MIDS_START = 200;
inline constexpr auto FRANGE_MIDS_END = 5000;
inline constexpr auto FRANGE_HIGH_START = 4000;
inline constexpr auto FRANGE_HIGH_END = 20000;
// Amplitude ranges (in dBFS).
// Reference: full scale sine wave (-1.0 to 1.0) is 0 dB.
@@ -96,15 +70,14 @@ enum class AmplitudeRange
Silent
};
constexpr int ARANGE_EXTENDED_START = -80;
constexpr int ARANGE_EXTENDED_END = 20;
constexpr int ARANGE_AUDIBLE_START = -50;
constexpr int ARANGE_AUDIBLE_END = 0;
constexpr int ARANGE_LOUD_START = -30;
constexpr int ARANGE_LOUD_END = 0;
constexpr int ARANGE_SILENT_START = -60;
constexpr int ARANGE_SILENT_END = -10;
inline constexpr auto ARANGE_EXTENDED_START = -80;
inline constexpr auto ARANGE_EXTENDED_END = 20;
inline constexpr auto ARANGE_AUDIBLE_START = -50;
inline constexpr auto ARANGE_AUDIBLE_END = 0;
inline constexpr auto ARANGE_LOUD_START = -30;
inline constexpr auto ARANGE_LOUD_END = 0;
inline constexpr auto ARANGE_SILENT_START = -60;
inline constexpr auto ARANGE_SILENT_END = -10;
} // namespace lmms

View File

@@ -27,35 +27,42 @@
#include <QtGlobal>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <numbers>
#include <concepts>
#include "lmms_constants.h"
#include "lmmsconfig.h"
#include <cassert>
#include "lmms_constants.h"
namespace lmms
{
static inline bool approximatelyEqual(float x, float y)
// TODO C++23: Make constexpr since std::abs() will be constexpr
inline bool approximatelyEqual(float x, float y) noexcept
{
return x == y ? true : std::abs(x - y) < F_EPSILON;
return x == y || std::abs(x - y) < F_EPSILON;
}
#ifdef __INTEL_COMPILER
static inline float absFraction( const float _x )
// TODO C++23: Make constexpr since std::trunc() will be constexpr
/*!
* @brief Returns the fractional part of a float, a value between -1.0f and 1.0f.
*
* fraction( 2.3) => 0.3
* fraction(-2.3) => -0.3
*
* Note that if the return value is used as a phase of an oscillator, that the oscillator must support
* negative phases.
*/
inline auto fraction(std::floating_point auto x) noexcept
{
return( _x - floorf( _x ) );
return x - std::trunc(x);
}
static inline float fraction( const float _x )
{
return( _x - floorf( _x ) - ( _x >= 0.0f ? 0.0 : 1.0 ) );
}
#else
// TODO C++23: Make constexpr since std::floor() will be constexpr
/*!
* @brief Returns the wrapped fractional part of a float, a value between 0.0f and 1.0f.
*
@@ -66,287 +73,174 @@ static inline float fraction( const float _x )
* If the result is interpreted as a phase of an oscillator, it makes that negative phases are
* converted to positive phases.
*/
static inline float absFraction(const float x)
inline auto absFraction(std::floating_point auto x) noexcept
{
return x - std::floor(x);
}
/*!
* @brief Returns the fractional part of a float, a value between -1.0f and 1.0f.
*
* fraction( 2.3) => 0.3
* fraction(-2.3) => -0.3
*
* Note that if the return value is used as a phase of an oscillator, that the oscillator must support
* negative phases.
*/
static inline float fraction( const float _x )
{
return( _x - static_cast<int>( _x ) );
}
#if 0
// SSE3-version
static inline float absFraction( float _x )
{
unsigned int tmp;
asm(
"fld %%st\n\t"
"fisttp %1\n\t"
"fild %1\n\t"
"ftst\n\t"
"sahf\n\t"
"jae 1f\n\t"
"fld1\n\t"
"fsubrp %%st, %%st(1)\n\t"
"1:\n\t"
"fsubrp %%st, %%st(1)"
: "+t"( _x ), "=m"( tmp )
:
: "st(1)", "cc" );
return( _x );
}
static inline float absFraction( float _x )
{
unsigned int tmp;
asm(
"fld %%st\n\t"
"fisttp %1\n\t"
"fild %1\n\t"
"fsubrp %%st, %%st(1)"
: "+t"( _x ), "=m"( tmp )
:
: "st(1)" );
return( _x );
}
#endif
#endif // __INTEL_COMPILER
constexpr int FAST_RAND_MAX = 32767;
static inline int fast_rand()
inline auto fastRand() noexcept
{
static unsigned long next = 1;
next = next * 1103515245 + 12345;
return( (unsigned)( next / 65536 ) % 32768 );
return next / 65536 % 32768;
}
static inline double fastRand( double range )
template<std::floating_point T>
inline auto fastRand(T range) noexcept
{
static const double fast_rand_ratio = 1.0 / FAST_RAND_MAX;
return fast_rand() * range * fast_rand_ratio;
constexpr T FAST_RAND_RATIO = static_cast<T>(1.0 / 32767);
return fastRand() * range * FAST_RAND_RATIO;
}
static inline float fastRandf( float range )
template<std::floating_point T>
inline auto fastRand(T from, T to) noexcept
{
static const float fast_rand_ratio = 1.0f / FAST_RAND_MAX;
return fast_rand() * range * fast_rand_ratio;
return from + fastRand(to - from);
}
//! @brief Takes advantage of fmal() function if present in hardware
static inline long double fastFmal( long double a, long double b, long double c )
//! Round `value` to `where` depending on step size
template<class T>
static void roundAt(T& value, const T& where, const T& stepSize)
{
#ifdef FP_FAST_FMAL
#ifdef __clang__
return fma( a, b, c );
#else
return fmal( a, b, c );
#endif
#else
return a * b + c;
#endif // FP_FAST_FMAL
}
//! @brief Takes advantage of fmaf() function if present in hardware
static inline float fastFmaf( float a, float b, float c )
{
#ifdef FP_FAST_FMAF
#ifdef __clang__
return fma( a, b, c );
#else
return fmaf( a, b, c );
#endif
#else
return a * b + c;
#endif // FP_FAST_FMAF
}
//! @brief Takes advantage of fma() function if present in hardware
static inline double fastFma( double a, double b, double c )
{
#ifdef FP_FAST_FMA
return fma( a, b, c );
#else
return a * b + c;
#endif
}
// source: http://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/
static inline double fastPow( double a, double b )
{
union
if (std::abs(value - where) < F_EPSILON * std::abs(stepSize))
{
double d;
int32_t x[2];
} u = { a };
u.x[1] = static_cast<int32_t>( b * ( u.x[1] - 1072632447 ) + 1072632447 );
u.x[0] = 0;
return u.d;
value = where;
}
}
// sinc function
static inline double sinc( double _x )
//! Source: http://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/
inline double fastPow(double a, double b)
{
return _x == 0.0 ? 1.0 : sin( F_PI * _x ) / ( F_PI * _x );
double d;
std::int32_t x[2];
std::memcpy(x, &a, sizeof(x));
x[1] = static_cast<std::int32_t>(b * (x[1] - 1072632447) + 1072632447);
x[0] = 0;
std::memcpy(&d, x, sizeof(d));
return d;
}
//! returns +1 if val >= 0, else -1
template<typename T>
constexpr T sign(T val) noexcept
{
return val >= 0 ? 1 : -1;
}
//! if val >= 0.0f, returns sqrt(val), else: -sqrt(-val)
inline float sqrt_neg(float val)
{
return std::sqrt(std::abs(val)) * sign(val);
}
//! @brief Exponential function that deals with negative bases
static inline float signedPowf( float v, float e )
inline float signedPowf(float v, float e)
{
return v < 0
? powf( -v, e ) * -1.0f
: powf( v, e );
return std::pow(std::abs(v), e) * sign(v);
}
//! @brief Scales @value from linear to logarithmic.
//! Value should be within [0,1]
static inline float logToLinearScale( float min, float max, float value )
inline float logToLinearScale(float min, float max, float value)
{
if( min < 0 )
using namespace std::numbers;
if (min < 0)
{
const float mmax = std::max(std::abs(min), std::abs(max));
const float val = value * ( max - min ) + min;
float result = signedPowf( val / mmax, F_E ) * mmax;
return std::isnan( result ) ? 0 : result;
const float val = value * (max - min) + min;
float result = signedPowf(val / mmax, e_v<float>) * mmax;
return std::isnan(result) ? 0 : result;
}
float result = powf( value, F_E ) * ( max - min ) + min;
return std::isnan( result ) ? 0 : result;
float result = std::pow(value, e_v<float>) * (max - min) + min;
return std::isnan(result) ? 0 : result;
}
//! @brief Scales value from logarithmic to linear. Value should be in min-max range.
static inline float linearToLogScale( float min, float max, float value )
inline float linearToLogScale(float min, float max, float value)
{
static const float EXP = 1.0f / F_E;
constexpr auto inv_e = static_cast<float>(1.0 / std::numbers::e);
const float valueLimited = std::clamp(value, min, max);
const float val = ( valueLimited - min ) / ( max - min );
if( min < 0 )
const float val = (valueLimited - min) / (max - min);
if (min < 0)
{
const float mmax = std::max(std::abs(min), std::abs(max));
float result = signedPowf( valueLimited / mmax, EXP ) * mmax;
return std::isnan( result ) ? 0 : result;
float result = signedPowf(valueLimited / mmax, inv_e) * mmax;
return std::isnan(result) ? 0 : result;
}
float result = powf( val, EXP ) * ( max - min ) + min;
return std::isnan( result ) ? 0 : result;
float result = std::pow(val, inv_e) * (max - min) + min;
return std::isnan(result) ? 0 : result;
}
//! @brief Converts linear amplitude (0-1.0) to dBFS scale. Handles zeroes as -inf.
//! @param amp Linear amplitude, where 1.0 = 0dBFS.
//! @return Amplitude in dBFS. -inf for 0 amplitude.
static inline float safeAmpToDbfs( float amp )
// TODO C++26: Make constexpr since std::exp() will be constexpr
template<std::floating_point T>
inline auto fastPow10f(T x)
{
return amp == 0.0f
? -INFINITY
: log10f( amp ) * 20.0f;
return std::exp(std::numbers::ln10_v<T> * x);
}
//! @brief Converts dBFS-scale to linear amplitude with 0dBFS = 1.0. Handles infinity as zero.
//! @param dbfs The dBFS value to convert: all infinites are treated as -inf and result in 0
//! @return Linear amplitude
static inline float safeDbfsToAmp( float dbfs )
// TODO C++26: Make constexpr since std::exp() will be constexpr
inline auto fastPow10f(std::integral auto x)
{
return std::isinf( dbfs )
? 0.0f
: std::pow(10.f, dbfs * 0.05f );
return std::exp(std::numbers::ln10_v<float> * x);
}
// TODO C++26: Make constexpr since std::log() will be constexpr
inline auto fastLog10f(float x)
{
constexpr auto inv_ln10 = static_cast<float>(1.0 / std::numbers::ln10);
return std::log(x) * inv_ln10;
}
//! @brief Converts linear amplitude (>0-1.0) to dBFS scale.
//! @param amp Linear amplitude, where 1.0 = 0dBFS. ** Must be larger than zero! **
//! @return Amplitude in dBFS.
static inline float ampToDbfs(float amp)
inline float ampToDbfs(float amp)
{
return log10f(amp) * 20.0f;
return fastLog10f(amp) * 20.0f;
}
//! @brief Converts dBFS-scale to linear amplitude with 0dBFS = 1.0
//! @param dbfs The dBFS value to convert. ** Must be a real number - not inf/nan! **
//! @return Linear amplitude
static inline float dbfsToAmp(float dbfs)
inline float dbfsToAmp(float dbfs)
{
return std::pow(10.f, dbfs * 0.05f);
return fastPow10f(dbfs * 0.05f);
}
//! returns 1.0f if val >= 0.0f, -1.0 else
static inline float sign( float val )
{
return val >= 0.0f ? 1.0f : -1.0f;
}
//! if val >= 0.0f, returns sqrtf(val), else: -sqrtf(-val)
static inline float sqrt_neg( float val )
//! @brief Converts linear amplitude (0-1.0) to dBFS scale. Handles zeroes as -inf.
//! @param amp Linear amplitude, where 1.0 = 0dBFS.
//! @return Amplitude in dBFS. -inf for 0 amplitude.
inline float safeAmpToDbfs(float amp)
{
return sqrtf( fabs( val ) ) * sign( val );
return amp == 0.0f ? -INFINITY : ampToDbfs(amp);
}
// fast approximation of square root
static inline float fastSqrt( float n )
//! @brief Converts dBFS-scale to linear amplitude with 0dBFS = 1.0. Handles infinity as zero.
//! @param dbfs The dBFS value to convert: all infinites are treated as -inf and result in 0
//! @return Linear amplitude
inline float safeDbfsToAmp(float dbfs)
{
union
{
int32_t i;
float f;
} u;
u.f = n;
u.i = ( u.i + ( 127 << 23 ) ) >> 1;
return u.f;
return std::isinf(dbfs) ? 0.0f : dbfsToAmp(dbfs);
}
//! returns value furthest from zero
template<class T>
static inline T absMax( T a, T b )
{
return std::abs(a) > std::abs(b) ? a : b;
}
//! returns value nearest to zero
template<class T>
static inline T absMin( T a, T b )
{
return std::abs(a) < std::abs(b) ? a : b;
}
//! Returns the linear interpolation of the two values
template<class T, class F>
constexpr T lerp(T a, T b, F t)
{
return (1. - t) * a + t * b;
}
// TODO C++20: use std::formatted_size
// @brief Calculate number of digits which LcdSpinBox would show for a given number
// @note Once we upgrade to C++20, we could probably use std::formatted_size
static inline int numDigitsAsInt(float f)
inline int numDigitsAsInt(float f)
{
// use rounding:
// LcdSpinBox sometimes uses roundf(), sometimes cast rounding
// LcdSpinBox sometimes uses std::round(), sometimes cast rounding
// we use rounding to be on the "safe side"
const float rounded = roundf(f);
int asInt = static_cast<int>(rounded);
int asInt = static_cast<int>(std::round(f));
int digits = 1; // always at least 1
if(asInt < 0)
{
@@ -354,11 +248,11 @@ static inline int numDigitsAsInt(float f)
asInt = -asInt;
}
// "asInt" is positive from now
int32_t power = 1;
for(int32_t i = 1; i<10; ++i)
int power = 1;
for (int i = 1; i < 10; ++i)
{
power *= 10;
if(static_cast<int32_t>(asInt) >= power) { ++digits; } // 2 digits for >=10, 3 for >=100
if (asInt >= power) { ++digits; } // 2 digits for >=10, 3 for >=100
else { break; }
}
return digits;

View File

@@ -27,7 +27,6 @@
#define LMMS_PANNING_H
#include "lmms_basics.h"
#include "panning_constants.h"
#include "Midi.h"
#include "volume.h"
@@ -36,6 +35,10 @@
namespace lmms
{
inline constexpr panning_t PanningRight = 100;
inline constexpr panning_t PanningLeft = -PanningRight;
inline constexpr panning_t PanningCenter = 0;
inline constexpr panning_t DefaultPanning = PanningCenter;
inline StereoVolumeVector panningToVolumeVector( panning_t _p,
float _scale = 1.0f )