Merge branch 'master' into feature/recording-stage-one
This commit is contained in:
110
include/AudioBusHandle.h
Normal file
110
include/AudioBusHandle.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
77
include/FileRevealer.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
77
include/KeyboardShortcuts.h
Normal file
77
include/KeyboardShortcuts.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#include "Midi.h"
|
||||
#include "panning_constants.h"
|
||||
#include "panning.h"
|
||||
#include "volume.h"
|
||||
|
||||
namespace lmms
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -50,6 +50,8 @@ protected:
|
||||
void contextMenuEvent(QContextMenuEvent* event) override;
|
||||
|
||||
private:
|
||||
void enterValue();
|
||||
|
||||
TrackView * m_tv;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
void setInactiveGraphic( const QPixmap & _pm, bool _update = true );
|
||||
|
||||
QSize sizeHint() const override;
|
||||
QSize minimumSizeHint() const override;
|
||||
|
||||
signals:
|
||||
void doubleClicked();
|
||||
|
||||
@@ -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*>;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
} ;
|
||||
|
||||
@@ -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
143
include/SampleThumbnail.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -128,6 +128,8 @@ private:
|
||||
|
||||
QScrollBar * m_leftRightScroll;
|
||||
|
||||
void adjustLeftRightScoll(int value);
|
||||
|
||||
LcdSpinBox * m_tempoSpinBox;
|
||||
|
||||
TimeLineWidget * m_timeLine;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -168,8 +168,6 @@ public slots:
|
||||
protected:
|
||||
static const int DEFAULT_PIXELS_PER_BAR = 128;
|
||||
|
||||
void resizeEvent( QResizeEvent * ) override;
|
||||
|
||||
TimePos m_currentPosition;
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 )
|
||||
|
||||
Reference in New Issue
Block a user