Merge branch 'master' into refac/memory

This commit is contained in:
Hyunjin Song
2024-10-03 17:05:29 +09:00
2625 changed files with 674162 additions and 293334 deletions

View File

@@ -22,14 +22,15 @@
*
*/
#ifndef ABOUT_DIALOG_H
#define ABOUT_DIALOG_H
#ifndef LMMS_GUI_ABOUT_DIALOG_H
#define LMMS_GUI_ABOUT_DIALOG_H
#include <QDialog>
#include "ui_about_dialog.h"
namespace lmms::gui
{
class AboutDialog : public QDialog, public Ui::AboutDialog
{
@@ -38,6 +39,6 @@ public:
} ;
} // namespace lmms::gui
#endif
#endif // LMMS_GUI_ABOUT_DIALOG_H

View File

@@ -22,12 +22,14 @@
*
*/
#ifndef ACTION_GROUP_H
#define ACTION_GROUP_H
#ifndef LMMS_GUI_ACTION_GROUP_H
#define LMMS_GUI_ACTION_GROUP_H
#include <QActionGroup>
namespace lmms::gui
{
/// \brief Convenience subclass of QActionGroup
///
/// This class provides the same functionality as QActionGroup, but in addition
@@ -54,4 +56,6 @@ private:
QList<QAction*> m_actions;
};
#endif
} // namespace lmms::gui
#endif // LMMS_GUI_ACTION_GROUP_H

388
include/ArrayVector.h Normal file
View File

@@ -0,0 +1,388 @@
/*
* ArrayVector.h
*
* Copyright (c) 2023 Dominic Clark
*
* 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_ARRAY_VECTOR_H
#define LMMS_ARRAY_VECTOR_H
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <iterator>
#include <memory>
#include <new>
#include <stdexcept>
#include <utility>
#include <type_traits>
namespace lmms {
namespace detail {
template<typename T, typename = void>
constexpr bool is_input_iterator_v = false;
template<typename T>
constexpr bool is_input_iterator_v<T, std::void_t<typename std::iterator_traits<T>::iterator_category>> =
std::is_convertible_v<typename std::iterator_traits<T>::iterator_category, std::input_iterator_tag>;
} // namespace detail
/**
* A container that stores up to a maximum of `N` elements of type `T` directly
* within itself, rather than separately on the heap. Useful when a dynamically
* resizeable container is needed for use in real-time code. Can be thought of
* as a hybrid between `std::array` and `std::vector`. The interface follows
* that of `std::vector` - see standard C++ documentation.
*/
template<typename T, std::size_t N>
class ArrayVector
{
public:
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using value_type = T;
using reference = T&;
using const_reference = const T&;
using pointer = T*;
using const_pointer = const T*;
using iterator = pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
ArrayVector() = default;
ArrayVector(const ArrayVector& other) noexcept(std::is_nothrow_copy_constructible_v<T>) :
m_size{other.m_size}
{
std::uninitialized_copy(other.begin(), other.end(), begin());
}
ArrayVector(ArrayVector&& other) noexcept(std::is_nothrow_move_constructible_v<T>) :
m_size{other.m_size}
{
std::uninitialized_move(other.begin(), other.end(), begin());
other.clear();
}
ArrayVector(size_type count, const T& value) noexcept(std::is_nothrow_copy_constructible_v<T>) :
m_size{count}
{
assert(count <= N);
std::uninitialized_fill_n(begin(), count, value);
}
explicit ArrayVector(size_type count) noexcept(std::is_nothrow_default_constructible_v<T>) :
m_size{count}
{
assert(count <= N);
std::uninitialized_value_construct_n(begin(), count);
}
template<typename It, std::enable_if_t<detail::is_input_iterator_v<It>, int> = 0>
ArrayVector(It first, It last)
{
// Can't check the size first as the iterator may not be multipass
const auto end = std::uninitialized_copy(first, last, begin());
m_size = end - begin();
assert(m_size <= N);
}
ArrayVector(std::initializer_list<T> il) noexcept(std::is_nothrow_copy_constructible_v<T>) :
m_size{il.size()}
{
assert(il.size() <= N);
std::uninitialized_copy(il.begin(), il.end(), begin());
}
~ArrayVector() { std::destroy(begin(), end()); }
ArrayVector& operator=(const ArrayVector& other)
noexcept(std::is_nothrow_copy_assignable_v<T> && std::is_nothrow_copy_constructible_v<T>)
{
if (this != &other) {
const auto toAssign = std::min(other.size(), size());
const auto assignedFromEnd = other.begin() + toAssign;
const auto assignedToEnd = std::copy(other.begin(), other.begin() + toAssign, begin());
std::destroy(assignedToEnd, end());
std::uninitialized_copy(assignedFromEnd, other.end(), end());
m_size = other.size();
}
return *this;
}
ArrayVector& operator=(ArrayVector&& other)
noexcept(std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T>)
{
if (this != &other) {
const auto toAssign = std::min(other.size(), size());
const auto assignedFromEnd = other.begin() + toAssign;
const auto assignedToEnd = std::move(other.begin(), other.begin() + toAssign, begin());
std::destroy(assignedToEnd, end());
std::uninitialized_move(assignedFromEnd, other.end(), end());
m_size = other.size();
other.clear();
}
return *this;
}
ArrayVector& operator=(std::initializer_list<T> il)
noexcept(std::is_nothrow_copy_assignable_v<T> && std::is_nothrow_copy_constructible_v<T>)
{
assert(il.size() <= N);
const auto toAssign = std::min(il.size(), size());
const auto assignedFromEnd = il.begin() + toAssign;
const auto assignedToEnd = std::copy(il.begin(), assignedFromEnd, begin());
std::destroy(assignedToEnd, end());
std::uninitialized_copy(assignedFromEnd, il.end(), end());
m_size = il.size();
return *this;
}
void assign(size_type count, const T& value)
noexcept(std::is_nothrow_copy_assignable_v<T> && std::is_nothrow_copy_constructible_v<T>)
{
assert(count <= N);
const auto temp = value;
const auto toAssign = std::min(count, size());
const auto toConstruct = count - toAssign;
const auto assignedToEnd = std::fill_n(begin(), toAssign, temp);
std::destroy(assignedToEnd, end());
std::uninitialized_fill_n(assignedToEnd, toConstruct, temp);
m_size = count;
}
template<typename It, std::enable_if_t<detail::is_input_iterator_v<It>, int> = 0>
void assign(It first, It last)
{
// Can't check the size first as the iterator may not be multipass
auto pos = begin();
for (; first != last && pos != end(); ++pos, ++first) {
*pos = *first;
}
std::destroy(pos, end());
pos = std::uninitialized_copy(first, last, pos);
m_size = pos - begin();
assert(m_size <= N);
}
reference at(size_type index)
{
if (index >= m_size) { throw std::out_of_range{"index out of range"}; }
return data()[index];
}
const_reference at(size_type index) const
{
if (index >= m_size) { throw std::out_of_range{"index out of range"}; }
return data()[index];
}
reference operator[](size_type index) noexcept
{
assert(index < m_size);
return data()[index];
}
const_reference operator[](size_type index) const noexcept
{
assert(index < m_size);
return data()[index];
}
reference front() noexcept { return operator[](0); }
const_reference front() const noexcept { return operator[](0); }
reference back() noexcept { return operator[](m_size - 1); }
const_reference back() const noexcept { return operator[](m_size - 1); }
pointer data() noexcept { return *std::launder(reinterpret_cast<T(*)[N]>(m_data)); }
const_pointer data() const noexcept { return *std::launder(reinterpret_cast<const T(*)[N]>(m_data)); }
iterator begin() noexcept { return data(); }
const_iterator begin() const noexcept { return data(); }
const_iterator cbegin() const noexcept { return data(); }
iterator end() noexcept { return data() + m_size; }
const_iterator end() const noexcept { return data() + m_size; }
const_iterator cend() const noexcept { return data() + m_size; }
reverse_iterator rbegin() noexcept { return std::reverse_iterator{end()}; }
const_reverse_iterator rbegin() const noexcept { return std::reverse_iterator{end()}; }
const_reverse_iterator crbegin() const noexcept { return std::reverse_iterator{cend()}; }
reverse_iterator rend() noexcept { return std::reverse_iterator{begin()}; }
const_reverse_iterator rend() const noexcept { return std::reverse_iterator{begin()}; }
const_reverse_iterator crend() const noexcept { return std::reverse_iterator{cbegin()}; }
bool empty() const noexcept { return m_size == 0; }
bool full() const noexcept { return m_size == N; }
size_type size() const noexcept { return m_size; }
size_type max_size() const noexcept { return N; }
size_type capacity() const noexcept { return N; }
void clear() noexcept
{
std::destroy(begin(), end());
m_size = 0;
}
iterator insert(const_iterator pos, const T& value) { return emplace(pos, value); }
iterator insert(const_iterator pos, T&& value) { return emplace(pos, std::move(value)); }
iterator insert(const_iterator pos, size_type count, const T& value)
{
assert(m_size + count <= N);
assert(cbegin() <= pos && pos <= cend());
const auto mutPos = begin() + (pos - cbegin());
const auto newEnd = std::uninitialized_fill_n(end(), count, value);
std::rotate(mutPos, end(), newEnd);
m_size += count;
return mutPos;
}
template<typename It, std::enable_if_t<detail::is_input_iterator_v<It>, int> = 0>
iterator insert(const_iterator pos, It first, It last)
{
// Can't check the size first as the iterator may not be multipass
assert(cbegin() <= pos && pos <= cend());
const auto mutPos = begin() + (pos - cbegin());
const auto newEnd = std::uninitialized_copy(first, last, end());
std::rotate(mutPos, end(), newEnd);
m_size = newEnd - begin();
assert(m_size <= N);
return mutPos;
}
iterator insert(const_iterator pos, std::initializer_list<T> il) { return insert(pos, il.begin(), il.end()); }
template<typename... Args>
iterator emplace(const_iterator pos, Args&&... args)
{
assert(cbegin() <= pos && pos <= cend());
const auto mutPos = begin() + (pos - cbegin());
emplace_back(std::forward<Args>(args)...);
std::rotate(mutPos, end() - 1, end());
return mutPos;
}
iterator erase(const_iterator pos) { return erase(pos, pos + 1); }
iterator erase(const_iterator first, const_iterator last)
{
assert(cbegin() <= first && first <= last && last <= cend());
const auto mutFirst = begin() + (first - cbegin());
const auto mutLast = begin() + (last - cbegin());
const auto newEnd = std::move(mutLast, end(), mutFirst);
std::destroy(newEnd, end());
m_size = newEnd - begin();
return mutFirst;
}
void push_back(const T& value) { emplace_back(value); }
void push_back(T&& value) { emplace_back(std::move(value)); }
template<typename... Args>
reference emplace_back(Args&&... args)
{
assert(!full());
// TODO C++20: Use std::construct_at
const auto result = new(static_cast<void*>(end())) T(std::forward<Args>(args)...);
++m_size;
return *result;
}
void pop_back()
{
assert(!empty());
--m_size;
std::destroy_at(end());
}
void resize(size_type size)
{
if (size > N) { throw std::length_error{"size exceeds maximum size"}; }
if (size < m_size) {
std::destroy(begin() + size, end());
} else {
std::uninitialized_value_construct(end(), begin() + size);
}
m_size = size;
}
void resize(size_type size, const value_type& value)
{
if (size > N) { throw std::length_error{"size exceeds maximum size"}; }
if (size < m_size) {
std::destroy(begin() + size, end());
} else {
std::uninitialized_fill(end(), begin() + size, value);
}
m_size = size;
}
void swap(ArrayVector& other)
noexcept(std::is_nothrow_swappable_v<T> && std::is_nothrow_move_constructible_v<T>)
{
using std::swap;
swap(*this, other);
}
friend void swap(ArrayVector& a, ArrayVector& b)
noexcept(std::is_nothrow_swappable_v<T> && std::is_nothrow_move_constructible_v<T>)
{
const auto toSwap = std::min(a.size(), b.size());
const auto aSwapEnd = a.begin() + toSwap;
const auto bSwapEnd = b.begin() + toSwap;
std::swap_ranges(a.begin(), aSwapEnd, b.begin());
std::uninitialized_move(aSwapEnd, a.end(), bSwapEnd);
std::uninitialized_move(bSwapEnd, b.end(), aSwapEnd);
std::destroy(aSwapEnd, a.end());
std::destroy(bSwapEnd, b.end());
std::swap(a.m_size, b.m_size);
}
// TODO C++20: Replace with operator<=>
friend bool operator<(const ArrayVector& l, const ArrayVector& r)
{
return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
}
friend bool operator<=(const ArrayVector& l, const ArrayVector& r) { return !(r < l); }
friend bool operator>(const ArrayVector& l, const ArrayVector& r) { return r < l; }
friend bool operator>=(const ArrayVector& l, const ArrayVector& r) { return !(l < r); }
friend bool operator==(const ArrayVector& l, const ArrayVector& r)
{
return std::equal(l.begin(), l.end(), r.begin(), r.end());
}
// TODO C++20: Remove
friend bool operator!=(const ArrayVector& l, const ArrayVector& r) { return !(l == r); }
private:
alignas(T) std::byte m_data[std::max(N * sizeof(T), std::size_t{1})]; // Intentionally a raw array
size_type m_size = 0;
};
} // namespace lmms
#endif // LMMS_ARRAY_VECTOR_H

View File

@@ -22,8 +22,8 @@
*
*/
#ifndef AUDIO_ALSA_H
#define AUDIO_ALSA_H
#ifndef LMMS_AUDIO_ALSA_H
#define LMMS_AUDIO_ALSA_H
#include "lmmsconfig.h"
@@ -37,6 +37,8 @@
#include "AudioDevice.h"
namespace lmms
{
class AudioAlsa : public QThread, public AudioDevice
{
@@ -52,7 +54,7 @@ public:
m_deviceName(deviceName),
m_deviceDescription(deviceDescription)
{}
~DeviceInfo() {}
~DeviceInfo() = default;
QString const & getDeviceName() const { return m_deviceName; }
QString const & getDeviceDescription() const { return m_deviceDescription; }
@@ -63,11 +65,11 @@ public:
};
typedef std::vector<DeviceInfo> DeviceInfoCollection;
using DeviceInfoCollection = std::vector<DeviceInfo>;
public:
AudioAlsa( bool & _success_ful, Mixer* mixer );
virtual ~AudioAlsa();
AudioAlsa( bool & _success_ful, AudioEngine* audioEngine );
~AudioAlsa() override;
inline static QString name()
{
@@ -82,7 +84,6 @@ public:
private:
void startProcessing() override;
void stopProcessing() override;
void applyQualitySettings() override;
void run() override;
int setHWParams( const ch_cnt_t _channels, snd_pcm_access_t _access );
@@ -102,6 +103,8 @@ private:
} ;
#endif
} // namespace lmms
#endif
#endif // LMMS_HAVE_ALSA
#endif // LMMS_AUDIO_ALSA_H

View File

@@ -22,8 +22,8 @@
*
*/
#ifndef AUDIO_ALSA_SETUP_WIDGET_H
#define AUDIO_ALSA_SETUP_WIDGET_H
#ifndef LMMS_GUI_AUDIO_ALSA_SETUP_WIDGET_H
#define LMMS_GUI_AUDIO_ALSA_SETUP_WIDGET_H
#include "lmmsconfig.h"
@@ -35,8 +35,11 @@
class QComboBox;
class LcdSpinBox;
namespace lmms::gui
{
class LcdSpinBox;
class AudioAlsaSetupWidget : public AudioDeviceSetupWidget
{
@@ -44,7 +47,7 @@ class AudioAlsaSetupWidget : public AudioDeviceSetupWidget
public:
AudioAlsaSetupWidget( QWidget * _parent );
virtual ~AudioAlsaSetupWidget();
~AudioAlsaSetupWidget() override;
void saveSettings() override;
@@ -59,6 +62,8 @@ private:
AudioAlsa::DeviceInfoCollection m_deviceInfos;
};
#endif
} // namespace lmms::gui
#endif
#endif // LMMS_HAVE_ALSA
#endif // LMMS_GUI_AUDIO_ALSA_SETUP_WIDGET_H

View File

@@ -1,5 +1,5 @@
/*
* AudioDevice.h - base-class for audio-devices, used by LMMS-mixer
* AudioDevice.h - base-class for audio-devices, used by LMMS audio engine
*
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
@@ -22,24 +22,28 @@
*
*/
#ifndef AUDIO_DEVICE_H
#define AUDIO_DEVICE_H
#ifndef LMMS_AUDIO_DEVICE_H
#define LMMS_AUDIO_DEVICE_H
#include <QtCore/QMutex>
#include <QMutex>
#include <samplerate.h>
#include "lmms_basics.h"
class AudioPort;
class Mixer;
class QThread;
namespace lmms
{
class AudioEngine;
class AudioPort;
class SampleFrame;
class AudioDevice
{
public:
AudioDevice( const ch_cnt_t _channels, Mixer* mixer );
AudioDevice( const ch_cnt_t _channels, AudioEngine* audioEngine );
virtual ~AudioDevice();
inline void lock()
@@ -86,27 +90,18 @@ public:
virtual void stopProcessing();
virtual void applyQualitySettings();
protected:
// subclasses can re-implement this for being used in conjunction with
// processNextBuffer()
virtual void writeBuffer( const surroundSampleFrame * /* _buf*/,
const fpp_t /*_frames*/,
const float /*_master_gain*/ )
{
}
virtual void writeBuffer(const SampleFrame* /* _buf*/, const fpp_t /*_frames*/) {}
// called by according driver for fetching new sound-data
fpp_t getNextBuffer( surroundSampleFrame * _ab );
fpp_t getNextBuffer(SampleFrame* _ab);
// convert a given audio-buffer to a buffer in signed 16-bit samples
// returns num of bytes in outbuf
int convertToS16( const surroundSampleFrame * _ab,
int convertToS16(const SampleFrame* _ab,
const fpp_t _frames,
const float _master_gain,
int_sample_t * _output_buffer,
const bool _convert_endian = false );
@@ -114,25 +109,16 @@ protected:
void clearS16Buffer( int_sample_t * _outbuf,
const fpp_t _frames );
// resample given buffer from samplerate _src_sr to samplerate _dst_sr
fpp_t resample( const surroundSampleFrame * _src,
const fpp_t _frames,
surroundSampleFrame * _dst,
const sample_rate_t _src_sr,
const sample_rate_t _dst_sr );
inline void setSampleRate( const sample_rate_t _new_sr )
{
m_sampleRate = _new_sr;
}
Mixer* mixer()
AudioEngine* audioEngine()
{
return m_mixer;
return m_audioEngine;
}
bool hqAudio() const;
static void stopProcessingThread( QThread * thread );
@@ -143,17 +129,15 @@ protected:
private:
sample_rate_t m_sampleRate;
ch_cnt_t m_channels;
Mixer* m_mixer;
AudioEngine* m_audioEngine;
bool m_inProcess;
QMutex m_devMutex;
SRC_DATA m_srcData;
SRC_STATE * m_srcState;
SampleFrame* m_buffer;
surroundSampleFrame * m_buffer;
};
} ;
} // namespace lmms
#endif
#endif // LMMS_AUDIO_DEVICE_H

View File

@@ -2,6 +2,7 @@
* AudioDeviceSetupWidget.h - Base class for audio device setup widgets
*
* Copyright (c) 2004-2015 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2023- Michael Gregorius
*
* This file is part of LMMS - https://lmms.io
*
@@ -22,24 +23,27 @@
*
*/
#ifndef AUDIO_DEVICE_SETUP_WIDGET_H
#define AUDIO_DEVICE_SETUP_WIDGET_H
#ifndef LMMS_GUI_AUDIO_DEVICE_SETUP_WIDGET_H
#define LMMS_GUI_AUDIO_DEVICE_SETUP_WIDGET_H
#include "TabWidget.h"
#include <QGroupBox>
namespace lmms::gui
{
class AudioDeviceSetupWidget : public TabWidget
class AudioDeviceSetupWidget : public QGroupBox
{
Q_OBJECT
public:
AudioDeviceSetupWidget( const QString & _caption, QWidget * _parent );
virtual ~AudioDeviceSetupWidget();
~AudioDeviceSetupWidget() override = default;
virtual void saveSettings() = 0;
virtual void show();
};
} // namespace lmms::gui
#endif
#endif // LMMS_GUI_AUDIO_DEVICE_SETUP_WIDGET_H

View File

@@ -22,26 +22,28 @@
*
*/
#ifndef AUDIO_DUMMY_H
#define AUDIO_DUMMY_H
#ifndef LMMS_AUDIO_DUMMY_H
#define LMMS_AUDIO_DUMMY_H
#include "AudioDevice.h"
#include "AudioDeviceSetupWidget.h"
#include "AudioEngine.h"
#include "MicroTimer.h"
#include "Mixer.h"
namespace lmms
{
class AudioDummy : public QThread, public AudioDevice
{
Q_OBJECT
public:
AudioDummy( bool & _success_ful, Mixer* mixer ) :
AudioDevice( DEFAULT_CHANNELS, mixer )
AudioDummy( bool & _success_ful, AudioEngine* audioEngine ) :
AudioDevice( DEFAULT_CHANNELS, audioEngine )
{
_success_ful = true;
}
virtual ~AudioDummy()
~AudioDummy() override
{
stopProcessing();
}
@@ -52,17 +54,15 @@ public:
}
class setupWidget : public AudioDeviceSetupWidget
class setupWidget : public gui::AudioDeviceSetupWidget
{
public:
setupWidget( QWidget * _parent ) :
AudioDeviceSetupWidget( AudioDummy::name(), _parent )
gui::AudioDeviceSetupWidget( AudioDummy::name(), _parent )
{
}
virtual ~setupWidget()
{
}
~setupWidget() override = default;
void saveSettings() override
{
@@ -94,17 +94,17 @@ private:
while( true )
{
timer.reset();
const surroundSampleFrame* b = mixer()->nextBuffer();
const SampleFrame* b = audioEngine()->nextBuffer();
if( !b )
{
break;
}
if( mixer()->hasFifoWriter() )
if( audioEngine()->hasFifoWriter() )
{
delete[] b;
}
const int microseconds = static_cast<int>( mixer()->framesPerPeriod() * 1000000.0f / mixer()->processingSampleRate() - timer.elapsed() );
const int microseconds = static_cast<int>( audioEngine()->framesPerPeriod() * 1000000.0f / audioEngine()->outputSampleRate() - timer.elapsed() );
if( microseconds > 0 )
{
usleep( microseconds );
@@ -114,5 +114,6 @@ private:
} ;
} // namespace lmms
#endif
#endif // LMMS_AUDIO_DUMMY_H

412
include/AudioEngine.h Normal file
View File

@@ -0,0 +1,412 @@
/*
* AudioEngine.h - device-independent audio engine for LMMS
*
* Copyright (c) 2004-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_ENGINE_H
#define LMMS_AUDIO_ENGINE_H
#include <mutex>
#include <QThread>
#include <samplerate.h>
#include <memory>
#include <vector>
#include "lmms_basics.h"
#include "SampleFrame.h"
#include "LocklessList.h"
#include "FifoBuffer.h"
#include "AudioEngineProfiler.h"
#include "PlayHandle.h"
namespace lmms
{
class AudioDevice;
class MidiClient;
class AudioPort;
class AudioEngineWorkerThread;
constexpr fpp_t MINIMUM_BUFFER_SIZE = 32;
constexpr fpp_t DEFAULT_BUFFER_SIZE = 256;
constexpr fpp_t MAXIMUM_BUFFER_SIZE = 4096;
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);
constexpr float OUTPUT_SAMPLE_MULTIPLIER = 32767.0f;
class LMMS_EXPORT AudioEngine : public QObject
{
Q_OBJECT
public:
/**
* @brief RAII helper for requestChangesInModel.
* Used by AudioEngine::requestChangesGuard.
*/
class RequestChangesGuard {
friend class AudioEngine;
private:
RequestChangesGuard(AudioEngine* audioEngine)
: m_audioEngine{audioEngine}
{
m_audioEngine->requestChangeInModel();
}
public:
RequestChangesGuard()
: m_audioEngine{nullptr}
{
}
RequestChangesGuard(RequestChangesGuard&& other)
: RequestChangesGuard()
{
std::swap(other.m_audioEngine, m_audioEngine);
}
// Disallow copy.
RequestChangesGuard(const RequestChangesGuard&) = delete;
RequestChangesGuard& operator=(const RequestChangesGuard&) = delete;
~RequestChangesGuard() {
if (m_audioEngine) {
m_audioEngine->doneChangeInModel();
}
}
private:
AudioEngine* m_audioEngine;
};
struct qualitySettings
{
enum class Interpolation
{
Linear,
SincFastest,
SincMedium,
SincBest
} ;
Interpolation interpolation;
qualitySettings(Interpolation i) :
interpolation(i)
{
}
int libsrcInterpolation() const
{
switch( interpolation )
{
case Interpolation::Linear:
return SRC_ZERO_ORDER_HOLD;
case Interpolation::SincFastest:
return SRC_SINC_FASTEST;
case Interpolation::SincMedium:
return SRC_SINC_MEDIUM_QUALITY;
case Interpolation::SincBest:
return SRC_SINC_BEST_QUALITY;
}
return SRC_LINEAR;
}
} ;
void initDevices();
void clear();
void clearNewPlayHandles();
// audio-device-stuff
bool renderOnly() const { return m_renderOnly; }
// Returns the current audio device's name. This is not necessarily
// the user's preferred audio device, in case you were thinking that.
inline const QString & audioDevName() const
{
return m_audioDevName;
}
inline bool audioDevStartFailed() const
{
return m_audioDevStartFailed;
}
//! Set new audio device. Old device will be deleted,
//! unless it's stored using storeAudioDevice
void setAudioDevice( AudioDevice * _dev,
const struct qualitySettings & _qs,
bool _needs_fifo,
bool startNow );
void storeAudioDevice();
void restoreAudioDevice();
inline AudioDevice * audioDev()
{
return m_audioDev;
}
// audio-port-stuff
inline void addAudioPort(AudioPort * port)
{
requestChangeInModel();
m_audioPorts.push_back(port);
doneChangeInModel();
}
void removeAudioPort(AudioPort * port);
// MIDI-client-stuff
inline const QString & midiClientName() const
{
return m_midiClientName;
}
inline MidiClient * midiClient()
{
return m_midiClient;
}
// play-handle stuff
bool addPlayHandle( PlayHandle* handle );
void removePlayHandle( PlayHandle* handle );
inline PlayHandleList& playHandles()
{
return m_playHandles;
}
void removePlayHandlesOfTypes(Track * track, PlayHandle::Types types);
// methods providing information for other classes
inline fpp_t framesPerPeriod() const
{
return m_framesPerPeriod;
}
AudioEngineProfiler& profiler()
{
return m_profiler;
}
int cpuLoad() const
{
return m_profiler.cpuLoad();
}
int detailLoad(const AudioEngineProfiler::DetailType type) const
{
return m_profiler.detailLoad(type);
}
const qualitySettings & currentQualitySettings() const
{
return m_qualitySettings;
}
sample_rate_t baseSampleRate() const;
sample_rate_t outputSampleRate() const;
sample_rate_t inputSampleRate() const;
inline float masterGain() const
{
return m_masterGain;
}
inline void setMasterGain(const float mo)
{
m_masterGain = mo;
}
static inline sample_t clip(const sample_t s)
{
if (s > 1.0f)
{
return 1.0f;
}
else if (s < -1.0f)
{
return -1.0f;
}
return s;
}
bool criticalXRuns() const;
inline bool hasFifoWriter() const
{
return m_fifoWriter != nullptr;
}
void pushInputFrames( SampleFrame* _ab, const f_cnt_t _frames );
inline const SampleFrame* inputBuffer()
{
return m_inputBuffer[ m_inputBufferRead ];
}
inline f_cnt_t inputBufferFrames() const
{
return m_inputBufferFrames[ m_inputBufferRead ];
}
inline const SampleFrame* nextBuffer()
{
return hasFifoWriter() ? m_fifo->read() : renderNextBuffer();
}
void changeQuality(const struct qualitySettings & qs);
//! Block until a change in model can be done (i.e. wait for audio thread)
void requestChangeInModel();
void doneChangeInModel();
RequestChangesGuard requestChangesGuard()
{
return RequestChangesGuard{this};
}
static bool isAudioDevNameValid(QString name);
static bool isMidiDevNameValid(QString name);
signals:
void qualitySettingsChanged();
void sampleRateChanged();
void nextAudioBuffer(const lmms::SampleFrame* buffer);
private:
using Fifo = FifoBuffer<SampleFrame*>;
class fifoWriter : public QThread
{
public:
fifoWriter( AudioEngine * audioEngine, Fifo * fifo );
void finish();
private:
AudioEngine * m_audioEngine;
Fifo * m_fifo;
volatile bool m_writing;
void run() override;
void write(SampleFrame* buffer);
} ;
AudioEngine( bool renderOnly );
~AudioEngine() override;
void startProcessing(bool needsFifo = true);
void stopProcessing();
AudioDevice * tryAudioDevices();
MidiClient * tryMidiClients();
void renderStageNoteSetup();
void renderStageInstruments();
void renderStageEffects();
void renderStageMix();
const SampleFrame* renderNextBuffer();
void swapBuffers();
void clearInternal();
bool m_renderOnly;
std::vector<AudioPort *> m_audioPorts;
fpp_t m_framesPerPeriod;
SampleFrame* m_inputBuffer[2];
f_cnt_t m_inputBufferFrames[2];
f_cnt_t m_inputBufferSize[2];
int m_inputBufferRead;
int m_inputBufferWrite;
std::unique_ptr<SampleFrame[]> m_outputBufferRead;
std::unique_ptr<SampleFrame[]> m_outputBufferWrite;
// worker thread stuff
std::vector<AudioEngineWorkerThread *> m_workers;
int m_numWorkers;
// playhandle stuff
PlayHandleList m_playHandles;
// place where new playhandles are added temporarily
LocklessList<PlayHandle *> m_newPlayHandles;
ConstPlayHandleList m_playHandlesToRemove;
struct qualitySettings m_qualitySettings;
float m_masterGain;
// audio device stuff
void doSetAudioDevice( AudioDevice *_dev );
AudioDevice * m_audioDev;
AudioDevice * m_oldAudioDev;
QString m_audioDevName;
bool m_audioDevStartFailed;
// MIDI device stuff
MidiClient * m_midiClient;
QString m_midiClientName;
// FIFO stuff
Fifo * m_fifo;
fifoWriter * m_fifoWriter;
AudioEngineProfiler m_profiler;
bool m_clearSignal;
std::recursive_mutex m_changeMutex;
friend class Engine;
friend class AudioEngineWorkerThread;
friend class ProjectRenderer;
} ;
} // namespace lmms
#endif // LMMS_AUDIO_ENGINE_H

View File

@@ -0,0 +1,111 @@
/*
* AudioEngineProfiler.h - class for profiling performance of AudioEngine
*
* Copyright (c) 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_ENGINE_PROFILER_H
#define LMMS_AUDIO_ENGINE_PROFILER_H
#include <array>
#include <atomic>
#include <QFile>
#include "lmms_basics.h"
#include "MicroTimer.h"
namespace lmms
{
class AudioEngineProfiler
{
public:
AudioEngineProfiler();
~AudioEngineProfiler() = default;
void startPeriod()
{
m_periodTimer.reset();
}
void finishPeriod( sample_rate_t sampleRate, fpp_t framesPerPeriod );
int cpuLoad() const
{
return m_cpuLoad;
}
void setOutputFile( const QString& outputFile );
enum class DetailType {
NoteSetup,
Instruments,
Effects,
Mixing,
Count
};
constexpr static auto DetailCount = static_cast<std::size_t>(DetailType::Count);
int detailLoad(const DetailType type) const
{
return m_detailLoad[static_cast<std::size_t>(type)].load(std::memory_order_relaxed);
}
class Probe
{
public:
Probe(AudioEngineProfiler& profiler, AudioEngineProfiler::DetailType type)
: m_profiler(profiler)
, m_type(type)
{
profiler.startDetail(type);
}
~Probe() { m_profiler.finishDetail(m_type); }
Probe& operator=(const Probe&) = delete;
Probe(const Probe&) = delete;
Probe(Probe&&) = delete;
private:
AudioEngineProfiler &m_profiler;
const AudioEngineProfiler::DetailType m_type;
};
private:
void startDetail(const DetailType type) { m_detailTimer[static_cast<std::size_t>(type)].reset(); }
void finishDetail(const DetailType type)
{
m_detailTime[static_cast<std::size_t>(type)] = m_detailTimer[static_cast<std::size_t>(type)].elapsed();
}
MicroTimer m_periodTimer;
std::atomic<float> m_cpuLoad;
QFile m_outputFile;
// Use arrays to avoid dynamic allocations in realtime code
std::array<MicroTimer, DetailCount> m_detailTimer;
std::array<int, DetailCount> m_detailTime{0};
std::array<std::atomic<float>, DetailCount> m_detailLoad{0};
};
} // namespace lmms
#endif // LMMS_AUDIO_ENGINE_PROFILER_H

View File

@@ -1,5 +1,5 @@
/*
* MixerWorkerThread.h - declaration of class MixerWorkerThread
* AudioEngineWorkerThread.h - declaration of class AudioEngineWorkerThread
*
* Copyright (c) 2009-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
@@ -22,18 +22,22 @@
*
*/
#ifndef MIXER_WORKER_THREAD_H
#define MIXER_WORKER_THREAD_H
#ifndef LMMS_AUDIO_ENGINE_WORKER_THREAD_H
#define LMMS_AUDIO_ENGINE_WORKER_THREAD_H
#include <QtCore/QThread>
#include <QThread>
#include <atomic>
class QWaitCondition;
class Mixer;
namespace lmms
{
class AudioEngine;
class ThreadableJob;
class MixerWorkerThread : public QThread
class AudioEngineWorkerThread : public QThread
{
Q_OBJECT
public:
@@ -41,18 +45,19 @@ public:
class JobQueue
{
public:
enum OperationMode
enum class OperationMode
{
Static, // no jobs added while processing queue
Dynamic // jobs can be added while processing queue
} ;
#define JOB_QUEUE_SIZE 8192
static constexpr size_t JOB_QUEUE_SIZE = 8192;
JobQueue() :
m_items(),
m_writeIndex( 0 ),
m_itemsDone( 0 ),
m_opMode( Static )
m_opMode( OperationMode::Static )
{
std::fill(m_items, m_items + JOB_QUEUE_SIZE, nullptr);
}
@@ -66,20 +71,19 @@ public:
private:
std::atomic<ThreadableJob*> m_items[JOB_QUEUE_SIZE];
std::atomic_int m_writeIndex;
std::atomic_int m_itemsDone;
std::atomic_size_t m_writeIndex;
std::atomic_size_t m_itemsDone;
OperationMode m_opMode;
} ;
MixerWorkerThread( Mixer* mixer );
virtual ~MixerWorkerThread();
AudioEngineWorkerThread( AudioEngine* audioEngine );
~AudioEngineWorkerThread() override;
virtual void quit();
static void resetJobQueue( JobQueue::OperationMode _opMode =
JobQueue::Static )
JobQueue::OperationMode::Static )
{
globalJobQueue.reset( _opMode );
}
@@ -93,12 +97,12 @@ public:
// to ThreadableJob objects
template<typename T>
static void fillJobQueue( const T & _vec,
JobQueue::OperationMode _opMode = JobQueue::Static )
JobQueue::OperationMode _opMode = JobQueue::OperationMode::Static )
{
resetJobQueue( _opMode );
for( typename T::ConstIterator it = _vec.begin(); it != _vec.end(); ++it )
for (const auto& job : _vec)
{
addJob( *it );
addJob(job);
}
}
@@ -110,11 +114,11 @@ private:
static JobQueue globalJobQueue;
static QWaitCondition * queueReadyWaitCond;
static QList<MixerWorkerThread *> workerThreads;
static QList<AudioEngineWorkerThread *> workerThreads;
volatile bool m_quit;
} ;
} // namespace lmms
#endif
#endif // LMMS_AUDIO_ENGINE_WORKER_THREAD_H

View File

@@ -23,22 +23,24 @@
*
*/
#ifndef AUDIO_FILE_DEVICE_H
#define AUDIO_FILE_DEVICE_H
#ifndef LMMS_AUDIO_FILE_DEVICE_H
#define LMMS_AUDIO_FILE_DEVICE_H
#include <QtCore/QFile>
#include <QFile>
#include "AudioDevice.h"
#include "OutputSettings.h"
namespace lmms
{
class AudioFileDevice : public AudioDevice
{
public:
AudioFileDevice(OutputSettings const & outputSettings,
const ch_cnt_t _channels, const QString & _file,
Mixer* mixer );
virtual ~AudioFileDevice();
AudioEngine* audioEngine );
~AudioFileDevice() override;
QString outputFile() const
{
@@ -66,13 +68,9 @@ private:
OutputSettings m_outputSettings;
} ;
using AudioFileDeviceInstantiaton
= AudioFileDevice* (*)(const QString&, const OutputSettings&, const ch_cnt_t, AudioEngine*, bool&);
typedef AudioFileDevice * ( * AudioFileDeviceInstantiaton )
( const QString & outputFilename,
OutputSettings const & outputSettings,
const ch_cnt_t channels,
Mixer* mixer,
bool & successful );
} // namespace lmms
#endif
#endif // LMMS_AUDIO_FILE_DEVICE_H

View File

@@ -22,30 +22,33 @@
*
*/
#ifndef AUDIO_FILE_FLAC_H
#define AUDIO_FILE_FLAC_H
#ifndef LMMS_AUDIO_FILE_FLAC_H
#define LMMS_AUDIO_FILE_FLAC_H
#include "lmmsconfig.h"
#include "AudioFileDevice.h"
#include <sndfile.h>
class AudioFileFlac: public AudioFileDevice
namespace lmms
{
class AudioFileFlac : public AudioFileDevice
{
public:
AudioFileFlac(OutputSettings const& outputSettings,
ch_cnt_t const channels,
bool& successful,
QString const& file,
Mixer* mixer
AudioEngine* audioEngine
);
virtual ~AudioFileFlac();
~AudioFileFlac() override;
static AudioFileDevice* getInst(QString const& outputFilename,
OutputSettings const& outputSettings,
ch_cnt_t const channels,
Mixer* mixer,
AudioEngine* audioEngine,
bool& successful)
{
return new AudioFileFlac(
@@ -53,7 +56,7 @@ public:
channels,
successful,
outputFilename,
mixer
audioEngine
);
}
@@ -62,13 +65,14 @@ private:
SF_INFO m_sfinfo;
SNDFILE* m_sf;
virtual void writeBuffer(surroundSampleFrame const* _ab,
fpp_t const frames,
float master_gain) override;
void writeBuffer(const SampleFrame* _ab, fpp_t const frames) override;
bool startEncoding();
void finishEncoding();
};
#endif //AUDIO_FILE_FLAC_H
} // namespace lmms
#endif // LMMS_AUDIO_FILE_FLAC_H

View File

@@ -23,8 +23,8 @@
*
*/
#ifndef AUDIO_FILE_MP3_H
#define AUDIO_FILE_MP3_H
#ifndef LMMS_AUDIO_FILE_MP3_H
#define LMMS_AUDIO_FILE_MP3_H
#include "lmmsconfig.h"
@@ -34,6 +34,8 @@
#include "lame/lame.h"
namespace lmms
{
class AudioFileMP3 : public AudioFileDevice
{
@@ -42,23 +44,21 @@ public:
const ch_cnt_t _channels,
bool & successful,
const QString & _file,
Mixer* mixer );
virtual ~AudioFileMP3();
AudioEngine* audioEngine );
~AudioFileMP3() override;
static AudioFileDevice * getInst( const QString & outputFilename,
OutputSettings const & outputSettings,
const ch_cnt_t channels,
Mixer* mixer,
AudioEngine* audioEngine,
bool & successful )
{
return new AudioFileMP3( outputSettings, channels, successful,
outputFilename, mixer );
outputFilename, audioEngine );
}
protected:
virtual void writeBuffer( const surroundSampleFrame * /* _buf*/,
const fpp_t /*_frames*/,
const float /*_master_gain*/ ) override;
void writeBuffer(const SampleFrame* /* _buf*/, const fpp_t /*_frames*/) override;
private:
void flushRemainingBuffers();
@@ -69,6 +69,8 @@ private:
lame_t m_lame;
};
#endif
} // namespace lmms
#endif
#endif // LMMS_HAVE_MP3LAME
#endif // LMMS_AUDIO_FILE_MP3_H

View File

@@ -23,8 +23,8 @@
*
*/
#ifndef AUDIO_FILE_OGG_H
#define AUDIO_FILE_OGG_H
#ifndef LMMS_AUDIO_FILE_OGG_H
#define LMMS_AUDIO_FILE_OGG_H
#include "lmmsconfig.h"
@@ -34,6 +34,8 @@
#include "AudioFileDevice.h"
namespace lmms
{
class AudioFileOgg : public AudioFileDevice
{
@@ -42,24 +44,21 @@ public:
const ch_cnt_t _channels,
bool & _success_ful,
const QString & _file,
Mixer* mixer );
virtual ~AudioFileOgg();
AudioEngine* audioEngine );
~AudioFileOgg() override;
static AudioFileDevice * getInst( const QString & outputFilename,
OutputSettings const & outputSettings,
const ch_cnt_t channels,
Mixer* mixer,
AudioEngine* audioEngine,
bool & successful )
{
return new AudioFileOgg( outputSettings, channels, successful,
outputFilename, mixer );
return new AudioFileOgg( outputSettings, channels, successful, outputFilename, audioEngine );
}
private:
virtual void writeBuffer( const surroundSampleFrame * _ab,
const fpp_t _frames,
const float _master_gain ) override;
void writeBuffer(const SampleFrame* _ab, const fpp_t _frames) override;
bool startEncoding();
void finishEncoding();
@@ -108,6 +107,8 @@ private:
} ;
#endif
} // namespace lmms
#endif
#endif // LMMS_HAVE_OGGVORBIS
#endif // LMMS_AUDIO_FILE_OGG_H

View File

@@ -23,14 +23,16 @@
*
*/
#ifndef AUDIO_FILE_WAVE_H
#define AUDIO_FILE_WAVE_H
#ifndef LMMS_AUDIO_FILE_WAVE_H
#define LMMS_AUDIO_FILE_WAVE_H
#include "lmmsconfig.h"
#include "AudioFileDevice.h"
#include <sndfile.h>
namespace lmms
{
class AudioFileWave : public AudioFileDevice
{
@@ -39,24 +41,22 @@ public:
const ch_cnt_t channels,
bool & successful,
const QString & file,
Mixer* mixer );
virtual ~AudioFileWave();
AudioEngine* audioEngine );
~AudioFileWave() override;
static AudioFileDevice * getInst( const QString & outputFilename,
OutputSettings const & outputSettings,
const ch_cnt_t channels,
Mixer* mixer,
AudioEngine* audioEngine,
bool & successful )
{
return new AudioFileWave( outputSettings, channels, successful,
outputFilename, mixer );
outputFilename, audioEngine );
}
private:
virtual void writeBuffer( const surroundSampleFrame * _ab,
const fpp_t _frames,
float _master_gain ) override;
void writeBuffer(const SampleFrame* _ab, const fpp_t _frames) override;
bool startEncoding();
void finishEncoding();
@@ -66,4 +66,7 @@ private:
SNDFILE * m_sf;
} ;
#endif
} // namespace lmms
#endif // LMMS_AUDIO_FILE_WAVE_H

View File

@@ -22,8 +22,8 @@
*
*/
#ifndef AUDIO_JACK_H
#define AUDIO_JACK_H
#ifndef LMMS_AUDIO_JACK_H
#define LMMS_AUDIO_JACK_H
#include "lmmsconfig.h"
@@ -31,109 +31,107 @@
#ifndef LMMS_HAVE_WEAKJACK
#include <jack/jack.h>
#else
#include "weak_libjack.h"
#include <weak_libjack.h>
#endif
#include <atomic>
#include <QtCore/QVector>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <vector>
#include "AudioDevice.h"
#include "AudioDeviceSetupWidget.h"
class QLineEdit;
class LcdSpinBox;
namespace lmms
{
class MidiJack;
namespace gui
{
class LcdSpinBox;
}
class AudioJack : public QObject, public AudioDevice
{
Q_OBJECT
public:
AudioJack( bool & _success_ful, Mixer* mixer );
virtual ~AudioJack();
AudioJack(bool& successful, AudioEngine* audioEngine);
~AudioJack() override;
// this is to allow the jack midi connection to use the same jack client connection
// the jack callback is handled here, we call the midi client so that it can read
// it's midi data during the callback
AudioJack * addMidiClient(MidiJack *midiClient);
void removeMidiClient(void) { m_midiClient = nullptr; }
jack_client_t * jackClient() {return m_client;};
AudioJack* addMidiClient(MidiJack* midiClient);
void removeMidiClient() { m_midiClient = nullptr; }
jack_client_t* jackClient() { return m_client; };
inline static QString name()
{
return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget",
"JACK (JACK Audio Connection Kit)" );
return QT_TRANSLATE_NOOP("AudioDeviceSetupWidget", "JACK (JACK Audio Connection Kit)");
}
class setupWidget : public AudioDeviceSetupWidget
class setupWidget : public gui::AudioDeviceSetupWidget
{
public:
setupWidget( QWidget * _parent );
virtual ~setupWidget();
setupWidget(QWidget* parent);
~setupWidget() override;
virtual void saveSettings();
void saveSettings() override;
private:
QLineEdit * m_clientName;
LcdSpinBox * m_channels;
} ;
QLineEdit* m_clientName;
gui::LcdSpinBox* m_channels;
};
private slots:
void restartAfterZombified();
private:
bool initJackClient();
virtual void startProcessing();
virtual void stopProcessing();
virtual void applyQualitySettings();
void startProcessing() override;
void stopProcessing() override;
virtual void registerPort( AudioPort * _port );
virtual void unregisterPort( AudioPort * _port );
virtual void renamePort( AudioPort * _port );
void registerPort(AudioPort* port) override;
void unregisterPort(AudioPort* port) override;
void renamePort(AudioPort* port) override;
int processCallback( jack_nframes_t _nframes, void * _udata );
int processCallback(jack_nframes_t nframes);
static int staticProcessCallback( jack_nframes_t _nframes,
void * _udata );
static void shutdownCallback( void * _udata );
static int staticProcessCallback(jack_nframes_t nframes, void* udata);
static void shutdownCallback(void* _udata);
jack_client_t * m_client;
jack_client_t* m_client;
bool m_active;
std::atomic<bool> m_stopped;
std::atomic<MidiJack *> m_midiClient;
QVector<jack_port_t *> m_outputPorts;
jack_default_audio_sample_t * * m_tempOutBufs;
surroundSampleFrame * m_outBuf;
std::atomic<MidiJack*> m_midiClient;
std::vector<jack_port_t*> m_outputPorts;
jack_default_audio_sample_t** m_tempOutBufs;
SampleFrame* m_outBuf;
f_cnt_t m_framesDoneInCurBuf;
f_cnt_t m_framesToDoInCurBuf;
#ifdef AUDIO_PORT_SUPPORT
struct StereoPort
{
jack_port_t * ports[2];
} ;
jack_port_t* ports[2];
};
typedef QMap<AudioPort *, StereoPort> JackPortMap;
using JackPortMap = QMap<AudioPort*, StereoPort>;
JackPortMap m_portMap;
#endif
signals:
void zombified();
};
} ;
} // namespace lmms
#endif
#endif // LMMS_HAVE_JACK
#endif
#endif // LMMS_AUDIO_JACK_H

View File

@@ -22,8 +22,8 @@
*
*/
#ifndef AUDIO_OSS_H
#define AUDIO_OSS_H
#ifndef LMMS_AUDIO_OSS_H
#define LMMS_AUDIO_OSS_H
#include "lmmsconfig.h"
@@ -34,17 +34,24 @@
#include "AudioDevice.h"
#include "AudioDeviceSetupWidget.h"
class LcdSpinBox;
class QLineEdit;
namespace lmms
{
namespace gui
{
class LcdSpinBox;
}
class AudioOss : public QThread, public AudioDevice
{
Q_OBJECT
public:
AudioOss( bool & _success_ful, Mixer* mixer );
virtual ~AudioOss();
AudioOss( bool & _success_ful, AudioEngine* audioEngine );
~AudioOss() override;
inline static QString name()
{
@@ -54,17 +61,17 @@ public:
static QString probeDevice();
class setupWidget : public AudioDeviceSetupWidget
class setupWidget : public gui::AudioDeviceSetupWidget
{
public:
setupWidget( QWidget * _parent );
virtual ~setupWidget();
~setupWidget() override;
void saveSettings() override;
private:
QLineEdit * m_device;
LcdSpinBox * m_channels;
gui::LcdSpinBox * m_channels;
} ;
@@ -72,7 +79,6 @@ public:
private:
void startProcessing() override;
void stopProcessing() override;
void applyQualitySettings() override;
void run() override;
int m_audioFD;
@@ -81,7 +87,8 @@ private:
} ;
} // namespace lmms
#endif
#endif // LMMS_HAVE_OSS
#endif
#endif // LMMS_AUDIO_OSS_H

View File

@@ -22,31 +22,31 @@
*
*/
#ifndef AUDIO_PORT_H
#define AUDIO_PORT_H
#ifndef LMMS_AUDIO_PORT_H
#define LMMS_AUDIO_PORT_H
#include <memory>
#include <QtCore/QString>
#include <QtCore/QMutex>
#include <QtCore/QMutexLocker>
#include <QString>
#include <QMutex>
#include "MemoryManager.h"
#include "PlayHandle.h"
namespace lmms
{
class EffectChain;
class FloatModel;
class BoolModel;
class AudioPort : public ThreadableJob
{
MM_OPERATORS
public:
AudioPort( const QString & _name, bool _has_effect_chain = true,
FloatModel * volumeModel = NULL, FloatModel * panningModel = NULL,
BoolModel * mutedModel = NULL );
FloatModel * volumeModel = nullptr, FloatModel * panningModel = nullptr,
BoolModel * mutedModel = nullptr );
virtual ~AudioPort();
inline sampleFrame * buffer()
inline SampleFrame* buffer()
{
return m_portBuffer;
}
@@ -71,11 +71,11 @@ public:
void setExtOutputEnabled( bool _enabled );
// next effect-channel after this audio-port
// next mixer-channel after this audio-port
// (-1 = none 0 = master)
inline fx_ch_t nextFxChannel() const
inline mix_ch_t nextMixerChannel() const
{
return m_nextFxChannel;
return m_nextMixerChannel;
}
inline EffectChain * effects()
@@ -83,9 +83,9 @@ public:
return m_effects.get();
}
void setNextFxChannel( const fx_ch_t _chnl )
void setNextMixerChannel( const mix_ch_t _chnl )
{
m_nextFxChannel = _chnl;
m_nextMixerChannel = _chnl;
}
@@ -112,11 +112,11 @@ public:
private:
volatile bool m_bufferUsage;
sampleFrame * m_portBuffer;
SampleFrame* m_portBuffer;
QMutex m_portBufferLock;
bool m_extOutputEnabled;
fx_ch_t m_nextFxChannel;
mix_ch_t m_nextMixerChannel;
QString m_name;
@@ -129,10 +129,11 @@ private:
FloatModel * m_panningModel;
BoolModel * m_mutedModel;
friend class Mixer;
friend class MixerWorkerThread;
friend class AudioEngine;
friend class AudioEngineWorkerThread;
} ;
} // namespace lmms
#endif
#endif // LMMS_AUDIO_PORT_H

View File

@@ -22,17 +22,36 @@
*
*/
#ifndef AUDIO_PORTAUDIO_H
#define AUDIO_PORTAUDIO_H
#ifndef LMMS_AUDIO_PORTAUDIO_H
#define LMMS_AUDIO_PORTAUDIO_H
#include <QtCore/QObject>
#include <QObject>
#include "lmmsconfig.h"
#include "ComboBoxModel.h"
#ifdef LMMS_HAVE_PORTAUDIO
# include <portaudio.h>
# include "AudioDevice.h"
# include "AudioDeviceSetupWidget.h"
# if defined paNeverDropInput || defined paNonInterleaved
# define PORTAUDIO_V19
# else
# define PORTAUDIO_V18
# endif
#endif
namespace lmms
{
class AudioPortAudioSetupUtil : public QObject
{
Q_OBJECT
Q_OBJECT
public slots:
void updateBackends();
void updateDevices();
@@ -41,66 +60,51 @@ public slots:
public:
ComboBoxModel m_backendModel;
ComboBoxModel m_deviceModel;
} ;
};
#ifdef LMMS_HAVE_PORTAUDIO
#include <portaudio.h>
#include <QtCore/QSemaphore>
#include "AudioDevice.h"
#include "AudioDeviceSetupWidget.h"
#if defined paNeverDropInput || defined paNonInterleaved
# define PORTAUDIO_V19
#else
# define PORTAUDIO_V18
#endif
namespace gui
{
class ComboBox;
class LcdSpinBox;
}
class AudioPortAudio : public AudioDevice
{
public:
AudioPortAudio( bool & _success_ful, Mixer* mixer );
virtual ~AudioPortAudio();
AudioPortAudio( bool & _success_ful, AudioEngine* audioEngine );
~AudioPortAudio() override;
inline static QString name()
{
return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "PortAudio" );
}
int process_callback(const float* _inputBuffer, float* _outputBuffer, f_cnt_t _framesPerBuffer);
int process_callback( const float *_inputBuffer,
float * _outputBuffer,
unsigned long _framesPerBuffer );
class setupWidget : public AudioDeviceSetupWidget
class setupWidget : public gui::AudioDeviceSetupWidget
{
public:
setupWidget( QWidget * _parent );
virtual ~setupWidget();
~setupWidget() override;
virtual void saveSettings();
virtual void show();
void saveSettings() override;
void show() override;
private:
ComboBox * m_backend;
ComboBox * m_device;
gui::ComboBox * m_backend;
gui::ComboBox * m_device;
AudioPortAudioSetupUtil m_setupUtil;
} ;
private:
virtual void startProcessing();
virtual void stopProcessing();
virtual void applyQualitySettings();
void startProcessing() override;
void stopProcessing() override;
#ifdef PORTAUDIO_V19
static int _process_callback( const void *_inputBuffer, void * _outputBuffer,
@@ -122,10 +126,10 @@ private:
unsigned long _framesPerBuffer, PaTimestamp _outTime, void * _arg );
typedef double PaTime;
typedef PaDeviceID PaDeviceIndex;
using PaTime = double;
using PaDeviceIndex = PaDeviceID;
typedef struct PaStreamParameters
using PaStreamParameters = struct
{
PaDeviceIndex device;
int channelCount;
@@ -134,7 +138,7 @@ private:
void *hostApiSpecificStreamInfo;
} PaStreamParameters;
#endif
#endif // PORTAUDIO_V19
PaStream * m_paStream;
PaStreamParameters m_outputParameters;
@@ -142,14 +146,16 @@ private:
bool m_wasPAInitError;
surroundSampleFrame * m_outBuf;
int m_outBufPos;
int m_outBufSize;
SampleFrame* m_outBuf;
std::size_t m_outBufPos;
fpp_t m_outBufSize;
bool m_stopped;
} ;
#endif
#endif // LMMS_HAVE_PORTAUDIO
#endif
} // namespace lmms
#endif // LMMS_AUDIO_PORTAUDIO_H

View File

@@ -22,8 +22,8 @@
*
*/
#ifndef AUDIO_PULSEAUDIO_H
#define AUDIO_PULSEAUDIO_H
#ifndef LMMS_AUDIO_PULSEAUDIO_H
#define LMMS_AUDIO_PULSEAUDIO_H
#include "lmmsconfig.h"
@@ -36,17 +36,23 @@
#include "AudioDevice.h"
#include "AudioDeviceSetupWidget.h"
class LcdSpinBox;
class QLineEdit;
namespace lmms
{
namespace gui
{
class LcdSpinBox;
}
class AudioPulseAudio : public QThread, public AudioDevice
{
Q_OBJECT
public:
AudioPulseAudio( bool & _success_ful, Mixer* mixer );
virtual ~AudioPulseAudio();
AudioPulseAudio( bool & _success_ful, AudioEngine* audioEngine );
~AudioPulseAudio() override;
inline static QString name()
{
@@ -56,17 +62,17 @@ public:
static QString probeDevice();
class setupWidget : public AudioDeviceSetupWidget
class setupWidget : public gui::AudioDeviceSetupWidget
{
public:
setupWidget( QWidget * _parent );
virtual ~setupWidget();
~setupWidget() override;
void saveSettings() override;
private:
QLineEdit * m_device;
LcdSpinBox * m_channels;
gui::LcdSpinBox * m_channels;
} ;
@@ -82,7 +88,6 @@ public:
private:
void startProcessing() override;
void stopProcessing() override;
void applyQualitySettings() override;
void run() override;
volatile bool m_quit;
@@ -94,6 +99,8 @@ private:
} ;
#endif
} // namespace lmms
#endif
#endif // LMMS_HAVE_PULSEAUDIO
#endif // LMMS_AUDIO_PULSEAUDIO_H

65
include/AudioResampler.h Normal file
View File

@@ -0,0 +1,65 @@
/*
* AudioResampler.h - wrapper around libsamplerate
*
* Copyright (c) 2023 saker <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_AUDIO_RESAMPLER_H
#define LMMS_AUDIO_RESAMPLER_H
#include <samplerate.h>
#include "lmms_export.h"
namespace lmms {
class LMMS_EXPORT AudioResampler
{
public:
struct ProcessResult
{
int error;
long inputFramesUsed;
long outputFramesGenerated;
};
AudioResampler(int interpolationMode, int channels);
AudioResampler(const AudioResampler&) = delete;
AudioResampler(AudioResampler&&) = delete;
~AudioResampler();
AudioResampler& operator=(const AudioResampler&) = delete;
AudioResampler& operator=(AudioResampler&&) = delete;
auto resample(const float* in, long inputFrames, float* out, long outputFrames, double ratio) -> ProcessResult;
auto interpolationMode() const -> int { return m_interpolationMode; }
auto channels() const -> int { return m_channels; }
void setRatio(double ratio);
private:
int m_interpolationMode = -1;
int m_channels = 0;
int m_error = 0;
SRC_STATE* m_state = nullptr;
};
} // namespace lmms
#endif // LMMS_AUDIO_RESAMPLER_H

View File

@@ -23,37 +23,38 @@
*
*/
#ifndef AUDIO_SAMPLE_RECORDER_H
#define AUDIO_SAMPLE_RECORDER_H
#ifndef LMMS_AUDIO_SAMPLE_RECORDER_H
#define LMMS_AUDIO_SAMPLE_RECORDER_H
#include <QtCore/QList>
#include <QtCore/QPair>
#include <QList>
#include <QPair>
#include <memory>
#include "AudioDevice.h"
namespace lmms
{
class SampleBuffer;
class AudioSampleRecorder : public AudioDevice
{
public:
AudioSampleRecorder( const ch_cnt_t _channels, bool & _success_ful,
Mixer* mixer );
virtual ~AudioSampleRecorder();
AudioSampleRecorder( const ch_cnt_t _channels, bool & _success_ful, AudioEngine* audioEngine );
~AudioSampleRecorder() override;
f_cnt_t framesRecorded() const;
void createSampleBuffer( SampleBuffer** sampleBuffer );
std::shared_ptr<const SampleBuffer> createSampleBuffer();
private:
virtual void writeBuffer( const surroundSampleFrame * _ab,
const fpp_t _frames,
const float _master_gain ) override;
void writeBuffer(const SampleFrame* _ab, const fpp_t _frames) override;
typedef QList<QPair<sampleFrame *, fpp_t> > BufferList;
using BufferList = QList<QPair<SampleFrame*, fpp_t>>;
BufferList m_buffers;
} ;
} // namespace lmms
#endif
#endif // LMMS_AUDIO_SAMPLE_RECORDER_H

View File

@@ -22,32 +22,28 @@
*
*/
#ifndef AUDIO_SDL_H
#define AUDIO_SDL_H
#ifndef LMMS_AUDIO_SDL_H
#define LMMS_AUDIO_SDL_H
#include "lmmsconfig.h"
#ifdef LMMS_HAVE_SDL
#ifdef LMMS_HAVE_SDL2
#include <SDL2/SDL.h>
#include <SDL2/SDL_audio.h>
#else
#include <SDL/SDL.h>
#include <SDL/SDL_audio.h>
#endif
#include "AudioDevice.h"
#include "AudioDeviceSetupWidget.h"
class QLineEdit;
class QComboBox;
namespace lmms
{
class AudioSdl : public AudioDevice
{
public:
AudioSdl( bool & _success_ful, Mixer* mixer );
virtual ~AudioSdl();
AudioSdl( bool & _success_ful, AudioEngine* audioEngine );
~AudioSdl() override;
inline static QString name()
{
@@ -56,59 +52,54 @@ public:
}
class setupWidget : public AudioDeviceSetupWidget
class setupWidget : public gui::AudioDeviceSetupWidget
{
public:
setupWidget( QWidget * _parent );
~setupWidget() override;
~setupWidget() override = default;
void saveSettings() override;
private:
void populatePlaybackDeviceComboBox();
void populateInputDeviceComboBox();
private:
QLineEdit * m_device;
QComboBox* m_playbackDeviceComboBox = nullptr;
QComboBox* m_inputDeviceComboBox = nullptr;
static QString s_systemDefaultDevice;
} ;
private:
void startProcessing() override;
void stopProcessing() override;
void applyQualitySettings() override;
static void sdlAudioCallback( void * _udata, Uint8 * _buf, int _len );
void sdlAudioCallback( Uint8 * _buf, int _len );
#ifdef LMMS_HAVE_SDL2
static void sdlInputAudioCallback( void * _udata, Uint8 * _buf, int _len );
void sdlInputAudioCallback( Uint8 * _buf, int _len );
#endif
SDL_AudioSpec m_audioHandle;
surroundSampleFrame * m_outBuf;
SampleFrame* m_outBuf;
#ifdef LMMS_HAVE_SDL2
size_t m_currentBufferFramePos;
size_t m_currentBufferFramesCount;
#else
Uint8 * m_convertedBuf;
int m_convertedBufPos;
int m_convertedBufSize;
bool m_outConvertEndian;
#endif
bool m_stopped;
#ifdef LMMS_HAVE_SDL2
SDL_AudioDeviceID m_outputDevice;
SDL_AudioSpec m_inputAudioHandle;
SDL_AudioDeviceID m_inputDevice;
#endif
} ;
#endif
#endif
} // namespace lmms
#endif // LMMS_HAVE_SDL
#endif // LMMS_AUDIO_SDL_H

View File

@@ -23,8 +23,8 @@
*
*/
#ifndef _AUDIO_SNDIO_H
#define _AUDIO_SNDIO_H
#ifndef LMMS_AUDIO_SNDIO_H
#define LMMS_AUDIO_SNDIO_H
#include "lmmsconfig.h"
@@ -36,40 +36,46 @@
#include "AudioDevice.h"
#include "AudioDeviceSetupWidget.h"
class LcdSpinBox;
class QLineEdit;
namespace lmms
{
namespace gui
{
class LcdSpinBox;
}
class AudioSndio : public QThread, public AudioDevice
{
Q_OBJECT
public:
AudioSndio( bool & _success_ful, Mixer * _mixer );
virtual ~AudioSndio();
AudioSndio( bool & _success_ful, AudioEngine * _audioEngine );
~AudioSndio() override;
inline static QString name( void )
inline static QString name()
{
return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "sndio" );
}
class setupWidget : public AudioDeviceSetupWidget
class setupWidget : public gui::AudioDeviceSetupWidget
{
public:
setupWidget( QWidget * _parent );
virtual ~setupWidget();
~setupWidget() override = default;
void saveSettings( void ) override;
void saveSettings() override;
private:
QLineEdit * m_device;
LcdSpinBox * m_channels;
gui::LcdSpinBox * m_channels;
} ;
private:
void startProcessing( void ) override;
void stopProcessing( void ) override;
void applyQualitySettings( void ) override;
void run( void ) override;
void startProcessing() override;
void stopProcessing() override;
void run() override;
struct sio_hdl *m_hdl;
struct sio_par m_par;
@@ -78,6 +84,8 @@ private:
} ;
#endif /* LMMS_HAVE_SNDIO */
} // namespace lmms
#endif /* _AUDIO_SNDIO_H */
#endif // LMMS_HAVE_SNDIO
#endif // LMMS_AUDIO_SNDIO_H

View File

@@ -22,10 +22,10 @@
*
*/
#ifndef AUDIO_SOUNDIO_H
#define AUDIO_SOUNDIO_H
#ifndef LMMS_AUDIO_SOUNDIO_H
#define LMMS_AUDIO_SOUNDIO_H
#include <QtCore/QObject>
#include <QObject>
#include "lmmsconfig.h"
#include "ComboBoxModel.h"
@@ -37,15 +37,21 @@
#include "AudioDevice.h"
#include "AudioDeviceSetupWidget.h"
namespace lmms
{
namespace gui
{
class ComboBox;
class LcdSpinBox;
}
// Exists only to work around "Error: Meta object features not supported for nested classes"
class AudioSoundIoSetupUtil : public QObject
{
Q_OBJECT
public:
virtual ~AudioSoundIoSetupUtil();
virtual ~AudioSoundIoSetupUtil() = default;
void *m_setupWidget;
public slots:
@@ -56,7 +62,7 @@ public slots:
class AudioSoundIo : public AudioDevice
{
public:
AudioSoundIo( bool & _success_ful, Mixer* mixer );
AudioSoundIo( bool & _success_ful, AudioEngine* audioEngine );
virtual ~AudioSoundIo();
inline static QString name()
@@ -64,7 +70,7 @@ public:
return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "soundio" );
}
class setupWidget : public AudioDeviceSetupWidget
class setupWidget : public gui::AudioDeviceSetupWidget
{
public:
setupWidget( QWidget * _parent );
@@ -78,8 +84,8 @@ public:
private:
AudioSoundIoSetupUtil m_setupUtil;
ComboBox * m_backend;
ComboBox * m_device;
gui::ComboBox * m_backend;
gui::ComboBox * m_device;
ComboBoxModel m_backendModel;
ComboBoxModel m_deviceModel;
@@ -104,7 +110,7 @@ private:
SoundIo *m_soundio;
SoundIoOutStream *m_outstream;
surroundSampleFrame * m_outBuf;
SampleFrame* m_outBuf;
int m_outBufSize;
fpp_t m_outBufFramesTotal;
fpp_t m_outBufFrameIndex;
@@ -134,6 +140,9 @@ private:
};
#endif
#endif
} // namespace lmms
#endif // LMMS_HAVE_SOUNDIO
#endif // LMMS_AUDIO_SOUNDIO_H

View File

@@ -22,14 +22,15 @@
*
*/
#ifndef AUTOMATABLE_BUTTON_H
#define AUTOMATABLE_BUTTON_H
#ifndef LMMS_GUI_AUTOMATABLE_BUTTON_H
#define LMMS_GUI_AUTOMATABLE_BUTTON_H
#include <QPushButton>
#include "AutomatableModelView.h"
namespace lmms::gui
{
class automatableButtonGroup;
@@ -40,7 +41,7 @@ class LMMS_EXPORT AutomatableButton : public QPushButton, public BoolModelView
public:
AutomatableButton( QWidget * _parent, const QString & _name
= QString() );
virtual ~AutomatableButton();
~AutomatableButton() override;
inline void setCheckable( bool _on )
{
@@ -85,7 +86,7 @@ class LMMS_EXPORT automatableButtonGroup : public QWidget, public IntModelView
public:
automatableButtonGroup( QWidget * _parent, const QString & _name
= QString() );
virtual ~automatableButtonGroup();
~automatableButtonGroup() override;
void addButton( AutomatableButton * _btn );
void removeButton( AutomatableButton * _btn );
@@ -105,5 +106,6 @@ private:
} ;
} // namespace lmms::gui
#endif
#endif // LMMS_GUI_AUTOMATABLE_BUTTON_H

View File

@@ -22,19 +22,24 @@
*
*/
#ifndef AUTOMATABLE_MODEL_H
#define AUTOMATABLE_MODEL_H
#ifndef LMMS_AUTOMATABLE_MODEL_H
#define LMMS_AUTOMATABLE_MODEL_H
#include <QtCore/QMap>
#include <QtCore/QMutex>
#include <cmath>
#include <QMap>
#include <QMutex>
#include <QRegularExpression>
#include "JournallingObject.h"
#include "Model.h"
#include "TimePos.h"
#include "ValueBuffer.h"
#include "MemoryManager.h"
#include "ModelVisitor.h"
namespace lmms
{
// simple way to map a property of a view to a model
#define mapPropertyFromModelPtr(type,getfunc,setfunc,modelname) \
public: \
@@ -72,12 +77,10 @@ class ControllerConnection;
class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject
{
Q_OBJECT
MM_OPERATORS
public:
using AutoModelVector = std::vector<AutomatableModel*>;
typedef QVector<AutomatableModel *> AutoModelVector;
enum ScaleType
enum class ScaleType
{
Linear,
Logarithmic,
@@ -85,7 +88,7 @@ public:
};
virtual ~AutomatableModel();
~AutomatableModel() override;
// Implement those by using the MODEL_IS_VISITABLE macro
virtual void accept(ModelVisitor& v) = 0;
@@ -120,7 +123,7 @@ public:
bool isAutomated() const;
bool isAutomatedOrControlled() const
{
return isAutomated() || m_controllerConnection != NULL;
return isAutomated() || m_controllerConnection != nullptr;
}
ControllerConnection* controllerConnection() const
@@ -141,14 +144,25 @@ public:
template<bool>
static bool castValue( const float v )
{
return ( qRound( v ) != 0 );
return (std::round(v) != 0);
}
template<class T>
inline T value( int frameOffset = 0 ) const
{
if( hasLinkedModels() || m_controllerConnection != NULL )
if (m_controllerConnection)
{
if (!m_useControllerValue)
{
return castValue<T>(m_value);
}
else
{
return castValue<T>(controllerValue(frameOffset));
}
}
else if (hasLinkedModels())
{
return castValue<T>( controllerValue( frameOffset ) );
}
@@ -217,11 +231,11 @@ public:
}
void setScaleLogarithmic( bool setToTrue = true )
{
setScaleType( setToTrue ? Logarithmic : Linear );
setScaleType( setToTrue ? ScaleType::Logarithmic : ScaleType::Linear );
}
bool isScaleLogarithmic() const
{
return m_scaleType == Logarithmic;
return m_scaleType == ScaleType::Logarithmic;
}
void setStep( const float step );
@@ -298,9 +312,15 @@ public:
s_periodCounter = 0;
}
bool useControllerValue()
{
return m_useControllerValue;
}
public slots:
virtual void reset();
void unlinkControllerConnection();
void setUseControllerValue(bool b = true);
protected:
@@ -309,7 +329,7 @@ protected:
const float min = 0,
const float max = 0,
const float step = 0,
Model* parent = NULL,
Model* parent = nullptr,
const QString& displayName = QString(),
bool defaultConstructed = false );
//! returns a value which is in range between min() and
@@ -395,9 +415,11 @@ private:
// prevent several threads from attempting to write the same vb at the same time
QMutex m_valueBufferMutex;
bool m_useControllerValue;
signals:
void initValueChanged( float val );
void destroyed( jo_id_t id );
void destroyed( lmms::jo_id_t id );
} ;
@@ -438,7 +460,7 @@ class LMMS_EXPORT FloatModel : public TypedAutomatableModel<float>
MODEL_IS_VISITABLE
public:
FloatModel( float val = 0, float min = 0, float max = 0, float step = 0,
Model * parent = NULL,
Model * parent = nullptr,
const QString& displayName = QString(),
bool defaultConstructed = false ) :
TypedAutomatableModel( val, min, max, step, parent, displayName, defaultConstructed )
@@ -456,7 +478,7 @@ class LMMS_EXPORT IntModel : public TypedAutomatableModel<int>
MODEL_IS_VISITABLE
public:
IntModel( int val = 0, int min = 0, int max = 0,
Model* parent = NULL,
Model* parent = nullptr,
const QString& displayName = QString(),
bool defaultConstructed = false ) :
TypedAutomatableModel( val, min, max, 1, parent, displayName, defaultConstructed )
@@ -472,7 +494,7 @@ class LMMS_EXPORT BoolModel : public TypedAutomatableModel<bool>
MODEL_IS_VISITABLE
public:
BoolModel( const bool val = false,
Model* parent = NULL,
Model* parent = nullptr,
const QString& displayName = QString(),
bool defaultConstructed = false ) :
TypedAutomatableModel( val, false, true, 1, parent, displayName, defaultConstructed )
@@ -481,7 +503,8 @@ public:
QString displayValue( const float val ) const override;
} ;
typedef QMap<AutomatableModel*, float> AutomatedValueMap;
using AutomatedValueMap = QMap<AutomatableModel*, float>;
#endif
} // namespace lmms
#endif // LMMS_AUTOMATABLE_MODEL_H

View File

@@ -23,8 +23,8 @@
*
*/
#ifndef AUTOMATABLE_MODEL_VIEW_H
#define AUTOMATABLE_MODEL_VIEW_H
#ifndef LMMS_GUI_AUTOMATABLE_MODEL_VIEW_H
#define LMMS_GUI_AUTOMATABLE_MODEL_VIEW_H
#include "ModelView.h"
#include "AutomatableModel.h"
@@ -32,11 +32,14 @@
class QMenu;
class QMouseEvent;
namespace lmms::gui
{
class LMMS_EXPORT AutomatableModelView : public ModelView
{
public:
AutomatableModelView( Model* model, QWidget* _this );
virtual ~AutomatableModelView() = default;
~AutomatableModelView() override = default;
// some basic functions for convenience
AutomatableModel* modelUntyped()
@@ -132,5 +135,6 @@ using FloatModelView = TypedModelView<FloatModel>;
using IntModelView = TypedModelView<IntModel>;
using BoolModelView = TypedModelView<BoolModel>;
#endif
} // namespace lmms::gui
#endif // LMMS_GUI_AUTOMATABLE_MODEL_VIEW_H

View File

@@ -22,22 +22,23 @@
*
*/
#ifndef AUTOMATABLE_SLIDER_H
#define AUTOMATABLE_SLIDER_H
#ifndef LMMS_GUI_AUTOMATABLE_SLIDER_H
#define LMMS_GUI_AUTOMATABLE_SLIDER_H
#include <QSlider>
#include "AutomatableModelView.h"
namespace lmms::gui
{
class AutomatableSlider : public QSlider, public IntModelView
{
Q_OBJECT
public:
AutomatableSlider( QWidget * _parent, const QString & _name = QString() );
virtual ~AutomatableSlider();
~AutomatableSlider() override = default;
bool showStatus()
{
@@ -71,7 +72,8 @@ private slots:
} ;
typedef IntModel sliderModel;
using sliderModel = IntModel;
} // namespace lmms::gui
#endif
#endif // LMMS_GUI_AUTOMATABLE_SLIDER_H

300
include/AutomationClip.h Normal file
View File

@@ -0,0 +1,300 @@
/*
* AutomationClip.h - declaration of class AutomationClip, which contains
* all information about an automation clip
*
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2006-2008 Javier Serrano Polo <jasp00/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_AUTOMATION_CLIP_H
#define LMMS_AUTOMATION_CLIP_H
#include <QMap>
#include <QPointer>
#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0))
#include <QRecursiveMutex>
#endif
#include "AutomationNode.h"
#include "Clip.h"
namespace lmms
{
class AutomationTrack;
class TimePos;
namespace gui
{
class AutomationClipView;
class AutomationEditor;
} // namespace gui
class LMMS_EXPORT AutomationClip : public Clip
{
Q_OBJECT
public:
enum class ProgressionType
{
Discrete,
Linear,
CubicHermite
} ;
using timeMap = QMap<int, AutomationNode>;
using objectVector = std::vector<QPointer<AutomatableModel>>;
using TimemapIterator = timeMap::const_iterator;
AutomationClip( AutomationTrack * _auto_track );
AutomationClip( const AutomationClip & _clip_to_copy );
~AutomationClip() override = default;
bool addObject( AutomatableModel * _obj, bool _search_dup = true );
const AutomatableModel * firstObject() const;
const objectVector& objects() const;
// progression-type stuff
inline ProgressionType progressionType() const
{
return m_progressionType;
}
void setProgressionType( ProgressionType _new_progression_type );
inline float getTension() const
{
return m_tension;
}
void setTension( QString _new_tension );
TimePos timeMapLength() const;
void updateLength();
TimePos putValue(
const TimePos & time,
const float value,
const bool quantPos = true,
const bool ignoreSurroundingPoints = true
);
TimePos putValues(
const TimePos & time,
const float inValue,
const float outValue,
const bool quantPos = true,
const bool ignoreSurroundingPoints = true
);
void removeNode(const TimePos & time);
void removeNodes(const int tick0, const int tick1);
void resetNodes(const int tick0, const int tick1);
/**
* @brief Resets the tangents from the nodes between the given ticks
* @param Int first tick of the range
* @param Int second tick of the range
*/
void resetTangents(const int tick0, const int tick1);
void recordValue(TimePos time, float value);
TimePos setDragValue( const TimePos & time,
const float value,
const bool quantPos = true,
const bool controlKey = false );
void applyDragValue();
bool isDragging() const
{
return m_dragging;
}
inline const timeMap & getTimeMap() const
{
return m_timeMap;
}
inline timeMap & getTimeMap()
{
return m_timeMap;
}
inline float getMin() const
{
return firstObject()->minValue<float>();
}
inline float getMax() const
{
return firstObject()->maxValue<float>();
}
inline bool hasAutomation() const
{
return m_timeMap.isEmpty() == false;
}
static bool supportsTangentEditing(ProgressionType pType)
{
// Update function if we have new progression types that support tangent editing
return pType == ProgressionType::CubicHermite;
}
inline bool canEditTangents() const
{
return supportsTangentEditing(m_progressionType);
}
float valueAt( const TimePos & _time ) const;
float *valuesAfter( const TimePos & _time ) const;
QString name() const;
// settings-management
void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override;
void loadSettings( const QDomElement & _this ) override;
static const QString classNodeName() { return "automationclip"; }
QString nodeName() const override { return classNodeName(); }
gui::ClipView * createView( gui::TrackView * _tv ) override;
static bool isAutomated( const AutomatableModel * _m );
static std::vector<AutomationClip*> clipsForModel(const AutomatableModel* _m);
static AutomationClip * globalAutomationClip( AutomatableModel * _m );
static void resolveAllIDs();
bool isRecording() const { return m_isRecording; }
void setRecording( const bool b ) { m_isRecording = b; }
static int quantization() { return s_quantization; }
static void setQuantization(int q) { s_quantization = q; }
public slots:
void clear();
void objectDestroyed( lmms::jo_id_t );
void flipY( int min, int max );
void flipY();
void flipX( int length = -1 );
private:
void cleanObjects();
void generateTangents();
void generateTangents(timeMap::iterator it, int numToGenerate);
float valueAt( timeMap::const_iterator v, int offset ) const;
/**
* @brief
* This function combines the song tracks, pattern store tracks,
* and the global automation track all in one vector.
*
* @return std::vector<Track*>
*/
static std::vector<Track*> combineAllTracks();
// Mutex to make methods involving automation clips thread safe
// Mutable so we can lock it from const objects
#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0))
mutable QRecursiveMutex m_clipMutex;
#else
mutable QMutex m_clipMutex;
#endif
AutomationTrack * m_autoTrack;
std::vector<jo_id_t> m_idsToResolve;
objectVector m_objects;
timeMap m_timeMap; // actual values
timeMap m_oldTimeMap; // old values for storing the values before setDragValue() is called.
float m_tension;
bool m_hasAutomation;
ProgressionType m_progressionType;
bool m_dragging;
bool m_dragKeepOutValue; // Should we keep the current dragged node's outValue?
float m_dragOutValue; // The outValue of the dragged node's
bool m_dragLockedTan; // If the dragged node has it's tangents locked
float m_dragInTan; // The dragged node's inTangent
float m_dragOutTan; // The dragged node's outTangent
bool m_isRecording;
float m_lastRecordedValue;
static int s_quantization;
static const float DEFAULT_MIN_VALUE;
static const float DEFAULT_MAX_VALUE;
friend class gui::AutomationClipView;
friend class AutomationNode;
friend class gui::AutomationEditor;
} ;
//Short-hand functions to access node values in an automation clip;
// replacement for CPP macros with the same purpose; could be refactored
// further in the future.
inline float INVAL(AutomationClip::TimemapIterator it)
{
return it->getInValue();
}
inline float OUTVAL(AutomationClip::TimemapIterator it)
{
return it->getOutValue();
}
inline float OFFSET(AutomationClip::TimemapIterator it)
{
return it->getValueOffset();
}
inline float INTAN(AutomationClip::TimemapIterator it)
{
return it->getInTangent();
}
inline float OUTTAN(AutomationClip::TimemapIterator it)
{
return it->getOutTangent();
}
inline float LOCKEDTAN(AutomationClip::TimemapIterator it)
{
return it->lockedTangents();
}
inline int POS(AutomationClip::TimemapIterator it)
{
return it.key();
}
} // namespace lmms
#endif // LMMS_AUTOMATION_CLIP_H

View File

@@ -1,5 +1,5 @@
/*
* AutomationPatternView.h - declaration of class AutomationPatternView
* AutomationClipView.h - declaration of class AutomationClipView
*
* Copyright (c) 2008-2010 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
@@ -22,28 +22,33 @@
*
*/
#ifndef AUTOMATION_PATTERN_VIEW_H
#define AUTOMATION_PATTERN_VIEW_H
#ifndef LMMS_GUI_AUTOMATION_CLIP_VIEW_H
#define LMMS_GUI_AUTOMATION_CLIP_VIEW_H
#include <QStaticText>
#include "AutomationPattern.h"
#include "Song.h"
#include "SongEditor.h"
#include "TrackContentObjectView.h"
#include "ClipView.h"
namespace lmms
{
class AutomationClip;
namespace gui
{
class AutomationPatternView : public TrackContentObjectView
class AutomationClipView : public ClipView
{
Q_OBJECT
public:
AutomationPatternView( AutomationPattern * _pat, TrackView * _parent );
virtual ~AutomationPatternView();
AutomationClipView( AutomationClip * _clip, TrackView * _parent );
~AutomationClipView() override = default;
public slots:
/// Opens this view's pattern in the global automation editor
/// Opens this view's clip in the global automation editor
void openInAutomationEditor();
void update() override;
@@ -65,15 +70,16 @@ protected:
private:
AutomationPattern * m_pat;
AutomationClip * m_clip;
QPixmap m_paintPixmap;
QStaticText m_staticTextName;
static QPixmap * s_pat_rec;
void scaleTimemapToFit( float oldMin, float oldMax );
} ;
#endif
} // namespace gui
} // namespace lmms
#endif // LMMS_GUI_AUTOMATION_CLIP_VIEW_H

View File

@@ -23,28 +23,36 @@
*
*/
#ifndef AUTOMATION_EDITOR_H
#define AUTOMATION_EDITOR_H
#ifndef LMMS_GUI_AUTOMATION_EDITOR_H
#define LMMS_GUI_AUTOMATION_EDITOR_H
#include <QtCore/QMutex>
#include <QVector>
#include <QPushButton>
#include <QWidget>
#include <array>
#include "Editor.h"
#include "lmms_basics.h"
#include "JournallingObject.h"
#include "TimePos.h"
#include "AutomationPattern.h"
#include "AutomationClip.h"
#include "ComboBoxModel.h"
#include "Knob.h"
#include "Editor.h"
#include "JournallingObject.h"
#include "MidiClip.h"
#include "SampleClip.h"
#include "TimePos.h"
#include "lmms_basics.h"
class QPainter;
class QPixmap;
class QScrollBar;
class ComboBox;
namespace lmms
{
class NotePlayHandle;
namespace gui
{
class Knob;
class ComboBox;
class TimeLineWidget;
@@ -52,25 +60,32 @@ class TimeLineWidget;
class AutomationEditor : public QWidget, public JournallingObject
{
Q_OBJECT
Q_PROPERTY(QColor barLineColor READ barLineColor WRITE setBarLineColor)
Q_PROPERTY(QColor beatLineColor READ beatLineColor WRITE setBeatLineColor)
Q_PROPERTY(QColor lineColor READ lineColor WRITE setLineColor)
Q_PROPERTY(QColor vertexColor READ vertexColor WRITE setVertexColor)
Q_PROPERTY(QBrush scaleColor READ scaleColor WRITE setScaleColor)
Q_PROPERTY(QBrush graphColor READ graphColor WRITE setGraphColor)
Q_PROPERTY(QColor crossColor READ crossColor WRITE setCrossColor)
Q_PROPERTY(QColor backgroundShade READ backgroundShade WRITE setBackgroundShade)
Q_PROPERTY(QColor barLineColor MEMBER m_barLineColor)
Q_PROPERTY(QColor beatLineColor MEMBER m_beatLineColor)
Q_PROPERTY(QColor lineColor MEMBER m_lineColor)
Q_PROPERTY(QColor nodeInValueColor MEMBER m_nodeInValueColor)
Q_PROPERTY(QColor nodeOutValueColor MEMBER m_nodeOutValueColor)
Q_PROPERTY(QColor nodeTangentLineColor MEMBER m_nodeTangentLineColor)
Q_PROPERTY(QBrush scaleColor MEMBER m_scaleColor)
Q_PROPERTY(QBrush graphColor MEMBER m_graphColor)
Q_PROPERTY(QColor crossColor MEMBER m_crossColor)
Q_PROPERTY(QColor backgroundShade MEMBER m_backgroundShade)
Q_PROPERTY(QColor ghostNoteColor MEMBER m_ghostNoteColor)
Q_PROPERTY(QColor detuningNoteColor MEMBER m_detuningNoteColor)
Q_PROPERTY(QColor ghostSampleColor MEMBER m_ghostSampleColor)
public:
void setCurrentPattern(AutomationPattern * new_pattern);
void setCurrentClip(AutomationClip * new_clip);
void setGhostMidiClip(MidiClip* newMidiClip);
void setGhostSample(SampleClip* newSample);
inline const AutomationPattern * currentPattern() const
inline const AutomationClip * currentClip() const
{
return m_pattern;
return m_clip;
}
inline bool validPattern() const
inline bool validClip() const
{
return m_pattern != nullptr && m_pattern->hasAutomation();
return m_clip != nullptr;
}
void saveSettings(QDomDocument & doc, QDomElement & parent) override;
@@ -80,43 +95,26 @@ public:
return "automationeditor";
}
// qproperty access methods
QColor barLineColor() const;
void setBarLineColor(const QColor & c);
QColor beatLineColor() const;
void setBeatLineColor(const QColor & c);
QColor lineColor() const;
void setLineColor(const QColor & c);
QBrush graphColor() const;
void setGraphColor(const QBrush & c);
QColor vertexColor() const;
void setVertexColor(const QColor & c);
QBrush scaleColor() const;
void setScaleColor(const QBrush & c);
QColor crossColor() const;
void setCrossColor(const QColor & c);
QColor backgroundShade() const;
void setBackgroundShade(const QColor & c);
enum EditModes
enum class EditMode
{
DRAW,
ERASE,
SELECT,
MOVE
Draw,
Erase,
DrawOutValues,
EditTangents
};
public slots:
void update();
void updateAfterPatternChange();
void updateAfterClipChange();
protected:
typedef AutomationPattern::timeMap timeMap;
using timeMap = AutomationClip::timeMap;
void keyPressEvent(QKeyEvent * ke) override;
void leaveEvent(QEvent * e) override;
void mousePressEvent(QMouseEvent * mouseEvent) override;
void mouseDoubleClickEvent(QMouseEvent * mouseEvent) override;
void mouseReleaseEvent(QMouseEvent * mouseEvent) override;
void mouseMoveEvent(QMouseEvent * mouseEvent) override;
void paintEvent(QPaintEvent * pe) override;
@@ -126,13 +124,19 @@ protected:
float getLevel( int y );
int xCoordOfTick( int tick );
float yCoordOfLevel( float level );
inline void drawLevelTick( QPainter & p, int tick, float value);// bool is_selected ); //NEEDS Change in CSS
void removeSelection();
void selectAll();
void getSelectedValues(timeMap & selected_values );
inline void drawLevelTick(QPainter & p, int tick, float value);
timeMap::iterator getNodeAt(int x, int y, bool outValue = false, int r = 5);
/**
* @brief Given a mouse X coordinate, returns a timeMap::iterator that points to
* the closest node.
* @param Int X coordinate
* @return timeMap::iterator with the closest node or timeMap.end() if there are no nodes.
*/
timeMap::iterator getClosestNode(int x);
void drawLine( int x0, float y0, int x1, float y1 );
void removePoints( int x0, int x1 );
bool fineTuneValue(timeMap::iterator node, bool editingOutValue);
protected slots:
void play();
@@ -141,34 +145,46 @@ protected slots:
void horScrolled( int new_pos );
void verScrolled( int new_pos );
void setEditMode(AutomationEditor::EditModes mode);
void setEditMode(AutomationEditor::EditMode mode);
void setEditMode(int mode);
void setProgressionType(AutomationPattern::ProgressionTypes type);
void setProgressionType(AutomationClip::ProgressionType type);
/**
* @brief This method handles the AutomationEditorWindow event of changing
* progression types. After that, it calls updateEditTanButton so the edit
* tangents button is updated accordingly
* @param Int New progression type
*/
void setProgressionType(int type);
void setTension();
void copySelectedValues();
void cutSelectedValues();
void pasteValues();
void deleteSelectedValues();
void updatePosition( const TimePos & t );
void updatePosition( const lmms::TimePos & t );
void zoomingXChanged();
void zoomingYChanged();
/// Updates the pattern's quantization using the current user selected value.
/// Updates the clip's quantization using the current user selected value.
void setQuantization();
void resetGhostNotes()
{
m_ghostNotes = nullptr;
m_ghostSample = nullptr;
update();
}
private:
enum Actions
enum class Action
{
NONE,
MOVE_VALUE,
SELECT_VALUES,
MOVE_SELECTION
None,
MoveValue,
EraseValues,
MoveOutValue,
ResetOutValues,
DrawLine,
MoveTangent,
ResetTangents
} ;
// some constants...
@@ -181,27 +197,33 @@ private:
static const int VALUES_WIDTH = 64;
static const int NOTE_HEIGHT = 10; // height of individual notes
static const int NOTE_MARGIN = 40; // total border margin for notes
static const int MIN_NOTE_RANGE = 20; // min number of keys for fixed size
static const int SAMPLE_MARGIN = 40;
static constexpr int MAX_SAMPLE_HEIGHT = 400; // constexpr for use in min
AutomationEditor();
AutomationEditor( const AutomationEditor & );
virtual ~AutomationEditor();
~AutomationEditor() override;
static QPixmap * s_toolDraw;
static QPixmap * s_toolErase;
static QPixmap * s_toolSelect;
static QPixmap * s_toolMove;
static QPixmap * s_toolYFlip;
static QPixmap * s_toolXFlip;
QPixmap m_toolDraw = embed::getIconPixmap("edit_draw");
QPixmap m_toolErase = embed::getIconPixmap("edit_erase");
QPixmap m_toolDrawOut = embed::getIconPixmap("edit_draw_outvalue");
QPixmap m_toolEditTangents = embed::getIconPixmap("edit_tangent");
QPixmap m_toolMove = embed::getIconPixmap("edit_move");
QPixmap m_toolYFlip = embed::getIconPixmap("flip_y");
QPixmap m_toolXFlip = embed::getIconPixmap("flip_x");
ComboBoxModel m_zoomingXModel;
ComboBoxModel m_zoomingYModel;
ComboBoxModel m_quantizeModel;
static const QVector<double> m_zoomXLevels;
static const std::array<float, 7> m_zoomXLevels;
FloatModel * m_tensionModel;
QMutex m_patternMutex;
AutomationPattern * m_pattern;
AutomationClip * m_clip;
float m_minLevel;
float m_maxLevel;
float m_step;
@@ -209,6 +231,10 @@ private:
float m_bottomLevel;
float m_topLevel;
MidiClip* m_ghostNotes = nullptr;
QPointer<SampleClip> m_ghostSample = nullptr; // QPointer to set to nullptr on deletion
bool m_renderSample = false;
void centerTopBottomScroll();
void updateTopBottomLevels();
@@ -217,15 +243,8 @@ private:
TimePos m_currentPosition;
Actions m_action;
Action m_action;
tick_t m_selectStartTick;
tick_t m_selectedTick;
float m_selectStartLevel;
float m_selectedLevels;
float m_moveStartLevel;
tick_t m_moveStartTick;
int m_moveXOffset;
float m_drawLastLevel;
@@ -235,11 +254,15 @@ private:
int m_y_delta;
bool m_y_auto;
timeMap m_valuesToCopy;
timeMap m_selValuesForMove;
// Time position (key) of automation node whose outValue is being dragged
int m_draggedOutValueKey;
// The tick from the node whose tangent is being dragged
int m_draggedTangentTick;
// Whether the tangent being dragged is the InTangent or OutTangent
bool m_draggedOutTangent;
EditModes m_editMode;
EditMode m_editMode;
bool m_mouseDownLeft;
bool m_mouseDownRight; //true if right click is being held down
@@ -249,23 +272,29 @@ private:
void drawCross(QPainter & p );
void drawAutomationPoint( QPainter & p, timeMap::iterator it );
bool inBBEditor();
void drawAutomationTangents(QPainter& p, timeMap::iterator it);
bool inPatternEditor();
QColor m_barLineColor;
QColor m_beatLineColor;
QColor m_lineColor;
QBrush m_graphColor;
QColor m_vertexColor;
QColor m_nodeInValueColor;
QColor m_nodeOutValueColor;
QColor m_nodeTangentLineColor;
QBrush m_scaleColor;
QColor m_crossColor;
QColor m_backgroundShade;
QColor m_ghostNoteColor;
QColor m_detuningNoteColor;
QColor m_ghostSampleColor;
friend class AutomationEditorWindow;
signals:
void currentPatternChanged();
void positionChanged( const TimePos & );
void currentClipChanged();
void positionChanged( const lmms::TimePos & );
} ;
@@ -279,25 +308,28 @@ class AutomationEditorWindow : public Editor
static const int INITIAL_HEIGHT = 480;
public:
AutomationEditorWindow();
~AutomationEditorWindow();
~AutomationEditorWindow() override = default;
void setCurrentPattern(AutomationPattern* pattern);
const AutomationPattern* currentPattern();
void setCurrentClip(AutomationClip* clip);
void setGhostMidiClip(MidiClip* clip) { m_editor->setGhostMidiClip(clip); };
void setGhostSample(SampleClip* newSample) { m_editor->setGhostSample(newSample); };
const AutomationClip* currentClip();
void dropEvent( QDropEvent * _de ) override;
void dragEnterEvent( QDragEnterEvent * _dee ) override;
void open(AutomationPattern* pattern);
void open(AutomationClip* clip);
AutomationEditor* m_editor;
QSize sizeHint() const override;
public slots:
void clearCurrentPattern();
void clearCurrentClip();
signals:
void currentPatternChanged();
void currentClipChanged();
protected:
void focusInEvent(QFocusEvent * event) override;
@@ -308,8 +340,21 @@ protected slots:
private slots:
void updateWindowTitle();
void setProgressionType(int progType);
/**
* @brief The Edit Tangent edit mode should only be available for
* Cubic Hermite progressions, so this method is responsable for disabling it
* for other edit modes and reenabling it when it changes back to the Edit Tangent
* mode.
*/
void updateEditTanButton();
private:
QAction* m_drawAction;
QAction* m_eraseAction;
QAction* m_drawOutAction;
QAction* m_editTanAction;
QAction* m_discreteAction;
QAction* m_linearAction;
QAction* m_cubicHermiteAction;
@@ -322,7 +367,12 @@ private:
ComboBox * m_zoomingXComboBox;
ComboBox * m_zoomingYComboBox;
ComboBox * m_quantizeComboBox;
QPushButton* m_resetGhostNotes;
};
} // namespace gui
#endif
} // namespace lmms
#endif // LMMS_GUI_AUTOMATION_EDITOR_H

180
include/AutomationNode.h Normal file
View File

@@ -0,0 +1,180 @@
/*
* AutomationNode.h - Declaration of class AutomationNode, which contains
* all information about an automation node
*
* Copyright (c) 2020 Ian Caio <iancaio_dev/at/hotmail.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_AUTOMATION_NODE_H
#define LMMS_AUTOMATION_NODE_H
namespace lmms
{
class AutomationClip;
// Note: We use the default copy-assignment on the AutomationClip constructor. It's
// fine for now as we don't have dynamic allocated members, but if any are added we should
// have an user-defined one to perform a deep-copy.
class AutomationNode
{
public:
AutomationNode(); // Dummy constructor for the QMap
AutomationNode(AutomationClip* clip, float value, int pos);
AutomationNode(AutomationClip* clip, float inValue, float outValue, int pos);
AutomationNode& operator+=(float f)
{
m_inValue += f;
m_outValue += f;
return *this;
}
AutomationNode& operator-=(float f)
{
m_inValue -= f;
m_outValue -= f;
return *this;
}
AutomationNode& operator*=(float f)
{
m_inValue *= f;
m_outValue *= f;
return *this;
}
AutomationNode& operator/=(float f)
{
m_inValue /= f;
m_outValue /= f;
return *this;
}
inline const float getInValue() const
{
return m_inValue;
}
void setInValue(float value);
inline const float getOutValue() const
{
return m_outValue;
}
void setOutValue(float value);
void resetOutValue();
/**
* @brief Gets the offset between inValue and outValue
* @return Float representing the offset between inValue and outValue
*/
inline const float getValueOffset() const
{
return m_outValue - m_inValue;
}
/**
* @brief Gets the tangent of the left side of the node
* @return Float with the tangent from the inValue side
*/
inline const float getInTangent() const
{
return m_inTangent;
}
/**
* @brief Sets the tangent of the left side of the node
* @param Float with the tangent for the inValue side
*/
inline void setInTangent(float tangent)
{
m_inTangent = tangent;
}
/**
* @brief Gets the tangent of the right side of the node
* @return Float with the tangent from the outValue side
*/
inline const float getOutTangent() const
{
return m_outTangent;
}
/**
* @brief Sets the tangent of the right side of the node
* @param Float with the tangent for the outValue side
*/
inline void setOutTangent(float tangent)
{
m_outTangent = tangent;
}
/**
* @brief Checks if the tangents from the node are locked
*/
inline const bool lockedTangents() const
{
return m_lockedTangents;
}
/**
* @brief Locks or Unlocks the tangents from this node
*/
inline void setLockedTangents(bool b)
{
m_lockedTangents = b;
}
/**
* @brief Sets the clip this node belongs to
* @param AutomationClip* clip that m_clip will be
* set to
*/
inline void setClip(AutomationClip* clip)
{
m_clip = clip;
}
private:
// Clip that this node belongs to
AutomationClip* m_clip;
// Time position of this node (matches the timeMap key)
int m_pos;
// Values of this node
float m_inValue;
float m_outValue;
// Slope at each point for calculating spline
// We might have discrete jumps between curves, so we possibly have
// two different tangents for each side of the curve. If inValue and
// outValue are equal, inTangent and outTangent are equal too.
float m_inTangent;
float m_outTangent;
// If the tangents were edited manually, this will be true. That way
// the tangents from this node will not be recalculated. It's set back
// to false if the tangents are reset.
bool m_lockedTangents;
};
} // namespace lmms
#endif // LMMS_AUTOMATION_NODE_H

View File

@@ -1,201 +0,0 @@
/*
* AutomationPattern.h - declaration of class AutomationPattern, which contains
* all information about an automation pattern
*
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2006-2008 Javier Serrano Polo <jasp00/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 AUTOMATION_PATTERN_H
#define AUTOMATION_PATTERN_H
#include <QtCore/QMap>
#include <QtCore/QPointer>
#include "TrackContentObject.h"
class AutomationTrack;
class TimePos;
class LMMS_EXPORT AutomationPattern : public TrackContentObject
{
Q_OBJECT
public:
enum ProgressionTypes
{
DiscreteProgression,
LinearProgression,
CubicHermiteProgression
} ;
typedef QMap<int, float> timeMap;
typedef QVector<QPointer<AutomatableModel> > objectVector;
AutomationPattern( AutomationTrack * _auto_track );
AutomationPattern( const AutomationPattern & _pat_to_copy );
virtual ~AutomationPattern() = default;
bool addObject( AutomatableModel * _obj, bool _search_dup = true );
const AutomatableModel * firstObject() const;
const objectVector& objects() const;
// progression-type stuff
inline ProgressionTypes progressionType() const
{
return m_progressionType;
}
void setProgressionType( ProgressionTypes _new_progression_type );
inline float getTension() const
{
return m_tension;
}
void setTension( QString _new_tension );
TimePos timeMapLength() const;
void updateLength();
TimePos putValue( const TimePos & time,
const float value,
const bool quantPos = true,
const bool ignoreSurroundingPoints = true );
void removeValue( const TimePos & time );
void recordValue(TimePos time, float value);
TimePos setDragValue( const TimePos & time,
const float value,
const bool quantPos = true,
const bool controlKey = false );
void applyDragValue();
bool isDragging() const
{
return m_dragging;
}
inline const timeMap & getTimeMap() const
{
return m_timeMap;
}
inline timeMap & getTimeMap()
{
return m_timeMap;
}
inline const timeMap & getTangents() const
{
return m_tangents;
}
inline timeMap & getTangents()
{
return m_tangents;
}
inline float getMin() const
{
return firstObject()->minValue<float>();
}
inline float getMax() const
{
return firstObject()->maxValue<float>();
}
inline bool hasAutomation() const
{
return m_timeMap.isEmpty() == false;
}
float valueAt( const TimePos & _time ) const;
float *valuesAfter( const TimePos & _time ) const;
const QString name() const;
// settings-management
void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override;
void loadSettings( const QDomElement & _this ) override;
static const QString classNodeName() { return "automationpattern"; }
QString nodeName() const override { return classNodeName(); }
TrackContentObjectView * createView( TrackView * _tv ) override;
static bool isAutomated( const AutomatableModel * _m );
static QVector<AutomationPattern *> patternsForModel( const AutomatableModel * _m );
static AutomationPattern * globalAutomationPattern( AutomatableModel * _m );
static void resolveAllIDs();
bool isRecording() const { return m_isRecording; }
void setRecording( const bool b ) { m_isRecording = b; }
static int quantization() { return s_quantization; }
static void setQuantization(int q) { s_quantization = q; }
public slots:
void clear();
void objectDestroyed( jo_id_t );
void flipY( int min, int max );
void flipY();
void flipX( int length = -1 );
private:
void cleanObjects();
void generateTangents();
void generateTangents( timeMap::const_iterator it, int numToGenerate );
float valueAt( timeMap::const_iterator v, int offset ) const;
AutomationTrack * m_autoTrack;
QVector<jo_id_t> m_idsToResolve;
objectVector m_objects;
timeMap m_timeMap; // actual values
timeMap m_oldTimeMap; // old values for storing the values before setDragValue() is called.
timeMap m_tangents; // slope at each point for calculating spline
float m_tension;
bool m_hasAutomation;
ProgressionTypes m_progressionType;
bool m_dragging;
bool m_isRecording;
float m_lastRecordedValue;
static int s_quantization;
static const float DEFAULT_MIN_VALUE;
static const float DEFAULT_MAX_VALUE;
friend class AutomationPatternView;
} ;
#endif

View File

@@ -24,33 +24,33 @@
*
*/
#ifndef AUTOMATION_TRACK_H
#define AUTOMATION_TRACK_H
#ifndef LMMS_AUTOMATION_TRACK_H
#define LMMS_AUTOMATION_TRACK_H
#include "Track.h"
#include "TrackView.h"
namespace lmms
{
class AutomationTrack : public Track
{
Q_OBJECT
public:
AutomationTrack( TrackContainer* tc, bool _hidden = false );
virtual ~AutomationTrack() = default;
~AutomationTrack() override = default;
virtual bool play( const TimePos & _start, const fpp_t _frames,
const f_cnt_t _frame_base, int _tco_num = -1 ) override;
bool play( const TimePos & _start, const fpp_t _frames,
const f_cnt_t _frame_base, int _clip_num = -1 ) override;
QString nodeName() const override
{
return "automationtrack";
}
TrackView * createView( TrackContainerView* ) override;
TrackContentObject* createTCO(const TimePos & pos) override;
gui::TrackView * createView( gui::TrackContainerView* ) override;
Clip* createClip(const TimePos & pos) override;
virtual void saveTrackSpecificSettings( QDomDocument & _doc,
QDomElement & _parent ) override;
void saveTrackSpecificSettings(QDomDocument& doc, QDomElement& parent, bool presetMode) override;
void loadTrackSpecificSettings( const QDomElement & _this ) override;
private:
@@ -59,17 +59,6 @@ private:
} ;
} // namespace lmms
class AutomationTrackView : public TrackView
{
public:
AutomationTrackView( AutomationTrack* at, TrackContainerView* tcv );
virtual ~AutomationTrackView() = default;
void dragEnterEvent( QDragEnterEvent * _dee ) override;
void dropEvent( QDropEvent * _de ) override;
} ;
#endif
#endif // LMMS_AUTOMATION_TRACK_H

View File

@@ -0,0 +1,55 @@
/*
* AutomationTrackView.h - declaration of class AutomationTrackView
*
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2006-2008 Javier Serrano Polo <jasp00/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_GUI_AUTOMATION_TRACK_VIEW_H
#define LMMS_GUI_AUTOMATION_TRACK_VIEW_H
#include "TrackView.h"
namespace lmms
{
class AutomationTrack;
namespace gui
{
class AutomationTrackView : public TrackView
{
public:
AutomationTrackView( AutomationTrack* at, TrackContainerView* tcv );
~AutomationTrackView() override = default;
void dragEnterEvent( QDragEnterEvent * _dee ) override;
void dropEvent( QDropEvent * _de ) override;
} ;
} // namespace gui
} // namespace lmms
#endif // LMMS_GUI_AUTOMATION_TRACK_VIEW_H

View File

@@ -1,184 +0,0 @@
/*
* BBTrack.h - class BBTrack, a wrapper for using bbEditor
* (which is a singleton-class) as track
*
* Copyright (c) 2004-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 BB_TRACK_H
#define BB_TRACK_H
#include <QtCore/QObject>
#include <QtCore/QMap>
#include <QStaticText>
#include "TrackContentObjectView.h"
#include "Track.h"
#include "TrackView.h"
class TrackLabelButton;
class TrackContainer;
class BBTCO : public TrackContentObject
{
public:
BBTCO( Track * _track );
virtual ~BBTCO() = default;
void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override;
void loadSettings( const QDomElement & _this ) override;
inline QString nodeName() const override
{
return( "bbtco" );
}
int bbTrackIndex();
TrackContentObjectView * createView( TrackView * _tv ) override;
private:
friend class BBTCOView;
} ;
class BBTCOView : public TrackContentObjectView
{
Q_OBJECT
public:
BBTCOView( TrackContentObject * _tco, TrackView * _tv );
virtual ~BBTCOView() = default;
public slots:
void update() override;
protected slots:
void openInBBEditor();
void resetName();
void changeName();
protected:
void paintEvent( QPaintEvent * pe ) override;
void mouseDoubleClickEvent( QMouseEvent * _me ) override;
void constructContextMenu( QMenu * ) override;
private:
BBTCO * m_bbTCO;
QPixmap m_paintPixmap;
QStaticText m_staticTextName;
} ;
class LMMS_EXPORT BBTrack : public Track
{
Q_OBJECT
public:
BBTrack( TrackContainer* tc );
virtual ~BBTrack();
virtual bool play( const TimePos & _start, const fpp_t _frames,
const f_cnt_t _frame_base, int _tco_num = -1 ) override;
TrackView * createView( TrackContainerView* tcv ) override;
TrackContentObject* createTCO(const TimePos & pos) override;
virtual void saveTrackSpecificSettings( QDomDocument & _doc,
QDomElement & _parent ) override;
void loadTrackSpecificSettings( const QDomElement & _this ) override;
static BBTrack * findBBTrack( int _bb_num );
static void swapBBTracks( Track * _track1, Track * _track2 );
int index()
{
return s_infoMap[this];
}
bool automationDisabled( Track * _track )
{
return( m_disabledTracks.contains( _track ) );
}
void disableAutomation( Track * _track )
{
m_disabledTracks.append( _track );
}
void enableAutomation( Track * _track )
{
m_disabledTracks.removeAll( _track );
}
protected:
inline QString nodeName() const override
{
return( "bbtrack" );
}
private:
QList<Track *> m_disabledTracks;
typedef QMap<BBTrack *, int> infoMap;
static infoMap s_infoMap;
friend class BBTrackView;
} ;
class BBTrackView : public TrackView
{
Q_OBJECT
public:
BBTrackView( BBTrack* bbt, TrackContainerView* tcv );
virtual ~BBTrackView();
bool close() override;
const BBTrack * getBBTrack() const
{
return( m_bbTrack );
}
public slots:
void clickedTrackLabel();
private:
BBTrack * m_bbTrack;
TrackLabelButton * m_trackLabel;
} ;
#endif

View File

@@ -1,82 +0,0 @@
/*
* BBTrackContainer.h - model-component of BB-Editor
*
* Copyright (c) 2004-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 BB_TRACK_CONTAINER_H
#define BB_TRACK_CONTAINER_H
#include "TrackContainer.h"
#include "ComboBoxModel.h"
class LMMS_EXPORT BBTrackContainer : public TrackContainer
{
Q_OBJECT
mapPropertyFromModel(int,currentBB,setCurrentBB,m_bbComboBoxModel);
public:
BBTrackContainer();
virtual ~BBTrackContainer();
virtual bool play(TimePos start, const fpp_t frames, const f_cnt_t frameBase, int tcoNum = -1);
void updateAfterTrackAdd() override;
inline QString nodeName() const override
{
return "bbtrackcontainer";
}
bar_t lengthOfBB(int bb) const;
inline bar_t lengthOfCurrentBB()
{
return lengthOfBB(currentBB());
}
int numOfBBs() const;
void removeBB(int bb);
void swapBB(int bb1, int bb2);
void updateBBTrack(TrackContentObject * tco);
void fixIncorrectPositions();
void createTCOsForBB(int bb);
AutomatedValueMap automatedValuesAt(TimePos time, int tcoNum) const override;
public slots:
void play();
void stop();
void updateComboBox();
void currentBBChanged();
private:
ComboBoxModel m_bbComboBoxModel;
friend class BBEditor;
} ;
#endif

View File

@@ -23,8 +23,8 @@
*
*/
#ifndef BANDLIMITEDWAVE_H
#define BANDLIMITEDWAVE_H
#ifndef LMMS_BANDLIMITEDWAVE_H
#define LMMS_BANDLIMITEDWAVE_H
class QDataStream;
class QString;
@@ -34,14 +34,17 @@ class QString;
#include "lmms_basics.h"
#include "lmms_math.h"
#include "Engine.h"
#include "Mixer.h"
#include "AudioEngine.h"
#define MAXLEN 11
#define MIPMAPSIZE 2 << ( MAXLEN + 1 )
#define MIPMAPSIZE3 3 << ( MAXLEN + 1 )
#define MAXTBL 23
#define MINTLEN 2 << 0
#define MAXTLEN 3 << MAXLEN
namespace lmms
{
constexpr int MAXLEN = 11;
constexpr int MIPMAPSIZE = 2 << ( MAXLEN + 1 );
constexpr int MIPMAPSIZE3 = 3 << ( MAXLEN + 1 );
constexpr int MAXTBL = 23;
constexpr int MINTLEN = 2 << 0;
constexpr int MAXTLEN = 3 << MAXLEN;
// table for table sizes
const int TLENS[MAXTBL+1] = { 2 << 0, 3 << 0, 2 << 1, 3 << 1,
@@ -51,29 +54,30 @@ const int TLENS[MAXTBL+1] = { 2 << 0, 3 << 0, 2 << 1, 3 << 1,
2 << 8, 3 << 8, 2 << 9, 3 << 9,
2 << 10, 3 << 10, 2 << 11, 3 << 11 };
typedef struct
struct WaveMipMap
{
public:
inline sample_t sampleAt( int table, int ph )
inline sample_t sampleAt(int table, int ph)
{
if( table % 2 == 0 )
{ return m_data[ TLENS[ table ] + ph ]; }
if (table % 2 == 0) { return m_data[TLENS[table] + ph]; }
else
{ return m_data3[ TLENS[ table ] + ph ]; }
{
return m_data3[TLENS[table] + ph];
}
}
inline void setSampleAt( int table, int ph, sample_t sample )
inline void setSampleAt(int table, int ph, sample_t sample)
{
if( table % 2 == 0 )
{ m_data[ TLENS[ table ] + ph ] = sample; }
if (table % 2 == 0) { m_data[TLENS[table] + ph] = sample; }
else
{ m_data3[ TLENS[ table ] + ph ] = sample; }
{
m_data3[TLENS[table] + ph] = sample;
}
}
private:
sample_t m_data [ MIPMAPSIZE ];
sample_t m_data3 [ MIPMAPSIZE3 ];
} WaveMipMap;
sample_t m_data[MIPMAPSIZE];
sample_t m_data3[MIPMAPSIZE3];
};
QDataStream& operator<< ( QDataStream &out, WaveMipMap &waveMipMap );
@@ -85,24 +89,25 @@ QDataStream& operator>> ( QDataStream &in, WaveMipMap &waveMipMap );
class LMMS_EXPORT BandLimitedWave
{
public:
enum Waveforms
enum class Waveform
{
BLSaw,
BLSquare,
BLTriangle,
BLMoog,
NumBLWaveforms
Count
};
constexpr static auto NumWaveforms = static_cast<std::size_t>(Waveform::Count);
BandLimitedWave() {};
virtual ~BandLimitedWave() {};
BandLimitedWave() = default;
virtual ~BandLimitedWave() = default;
/*! \brief This method converts frequency to wavelength. The oscillate function takes wavelength as argument so
* use this to convert your note frequency to wavelength before using it.
*/
static inline float freqToLen( float f )
{
return freqToLen( f, Engine::mixer()->processingSampleRate() );
return freqToLen( f, Engine::audioEngine()->outputSampleRate() );
}
/*! \brief This method converts frequency to wavelength, but you can use any custom sample rate with it.
@@ -123,7 +128,7 @@ public:
* \param _wavelen The wavelength (length of one cycle, ie. the inverse of frequency) of the wanted oscillation, measured in sample frames
* \param _wave The wanted waveform. Options currently are saw, triangle, square and moog saw.
*/
static inline sample_t oscillate( float _ph, float _wavelen, Waveforms _wave )
static inline sample_t oscillate( float _ph, float _wavelen, Waveform _wave )
{
// get the next higher tlen
int t = 0;
@@ -135,27 +140,28 @@ public:
int lookup = static_cast<int>( lookupf );
const float ip = fraction( lookupf );
const sample_t s1 = s_waveforms[ _wave ].sampleAt( t, lookup );
const sample_t s2 = s_waveforms[ _wave ].sampleAt( t, ( lookup + 1 ) % tlen );
const sample_t s1 = s_waveforms[ static_cast<std::size_t>(_wave) ].sampleAt( t, lookup );
const sample_t s2 = s_waveforms[ static_cast<std::size_t>(_wave) ].sampleAt( t, ( lookup + 1 ) % tlen );
const int lm = lookup == 0 ? tlen - 1 : lookup - 1;
const sample_t s0 = s_waveforms[ _wave ].sampleAt( t, lm );
const sample_t s3 = s_waveforms[ _wave ].sampleAt( t, ( lookup + 2 ) % tlen );
const sample_t s0 = s_waveforms[ static_cast<std::size_t>(_wave) ].sampleAt( t, lm );
const sample_t s3 = s_waveforms[ static_cast<std::size_t>(_wave) ].sampleAt( t, ( lookup + 2 ) % tlen );
const sample_t sr = optimal4pInterpolate( s0, s1, s2, s3, ip );
return sr;
/* lookup = lookup << 1;
/*
lookup = lookup << 1;
tlen = tlen << 1;
t += 1;
const sample_t s3 = s_waveforms[ _wave ].sampleAt( t, lookup );
const sample_t s4 = s_waveforms[ _wave ].sampleAt( t, ( lookup + 1 ) % tlen );
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 float ip2 = ( ( tlen - _wavelen ) / tlen - 0.5 ) * 2.0;
return linearInterpolate( s12, s34, ip2 );
*/
*/
};
@@ -163,10 +169,11 @@ public:
static bool s_wavesGenerated;
static WaveMipMap s_waveforms [NumBLWaveforms];
static std::array<WaveMipMap, NumWaveforms> s_waveforms;
static QString s_wavetableDir;
};
} // namespace lmms
#endif
#endif // LMMS_BANDLIMITEDWAVE_H

76
include/BarModelEditor.h Normal file
View File

@@ -0,0 +1,76 @@
/*
* BarModelEditor.h - edit model values using a bar display
*
* Copyright (c) 2023-now Michael Gregorius
*
* 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.
*
*/
#pragma once
#ifndef LMMS_GUI_BAR_MODEL_EDITOR_H
#define LMMS_GUI_BAR_MODEL_EDITOR_H
#include "FloatModelEditorBase.h"
namespace lmms::gui
{
class LMMS_EXPORT BarModelEditor : public FloatModelEditorBase
{
Q_OBJECT
public:
Q_PROPERTY(QBrush backgroundBrush READ getBackgroundBrush WRITE setBackgroundBrush)
Q_PROPERTY(QBrush barBrush READ getBarBrush WRITE setBarBrush)
Q_PROPERTY(QColor textColor READ getTextColor WRITE setTextColor)
BarModelEditor(QString text, FloatModel * floatModel, QWidget * parent = nullptr);
// Define how the widget will behave in a layout
QSizePolicy sizePolicy() const;
QSize minimumSizeHint() const override;
QSize sizeHint() const override;
QBrush const & getBackgroundBrush() const;
void setBackgroundBrush(QBrush const & backgroundBrush);
QBrush const & getBarBrush() const;
void setBarBrush(QBrush const & barBrush);
QColor const & getTextColor() const;
void setTextColor(QColor const & textColor);
protected:
void paintEvent(QPaintEvent *event) override;
private:
QString const m_text;
QBrush m_backgroundBrush;
QBrush m_barBrush;
QColor m_textColor;
};
} // namespace lmms::gui
#endif // LMMS_GUI_BAR_MODEL_EDITOR_H

View File

@@ -28,34 +28,35 @@
*
*/
#ifndef BASIC_FILTERS_H
#define BASIC_FILTERS_H
#ifndef LMMS_BASIC_FILTERS_H
#define LMMS_BASIC_FILTERS_H
#ifndef __USE_XOPEN
#define __USE_XOPEN
#endif
#include <math.h>
#include <cmath>
#include <array>
#include "lmms_basics.h"
#include "lmms_constants.h"
#include "interpolation.h"
#include "MemoryManager.h"
namespace lmms
{
template<ch_cnt_t CHANNELS=DEFAULT_CHANNELS> class BasicFilters;
template<ch_cnt_t CHANNELS>
class LinkwitzRiley
{
MM_OPERATORS
public:
LinkwitzRiley( float sampleRate )
{
m_sampleRate = sampleRate;
clearHistory();
}
virtual ~LinkwitzRiley() {}
virtual ~LinkwitzRiley() = default;
inline void clearHistory()
{
@@ -135,21 +136,25 @@ private:
double m_a, m_a0, m_a1, m_a2;
double m_b1, m_b2, m_b3, m_b4;
typedef double frame[CHANNELS];
using frame = std::array<double, CHANNELS>;
frame m_z1, m_z2, m_z3, m_z4;
};
typedef LinkwitzRiley<2> StereoLinkwitzRiley;
using StereoLinkwitzRiley = LinkwitzRiley<2>;
template<ch_cnt_t CHANNELS>
class BiQuad
{
MM_OPERATORS
public:
BiQuad()
BiQuad() :
m_a1(0.),
m_a2(0.),
m_b0(0.),
m_b1(0.),
m_b2(0.)
{
clearHistory();
}
virtual ~BiQuad() {}
virtual ~BiQuad() = default;
inline void setCoeffs( float a1, float a2, float b0, float b1, float b2 )
{
@@ -181,12 +186,11 @@ private:
friend class BasicFilters<CHANNELS>; // needed for subfilter stuff in BasicFilters
};
typedef BiQuad<2> StereoBiQuad;
using StereoBiQuad = BiQuad<2>;
template<ch_cnt_t CHANNELS>
class OnePole
{
MM_OPERATORS
public:
OnePole()
{
@@ -197,7 +201,7 @@ public:
m_z1[i] = 0.0;
}
}
virtual ~OnePole() {}
virtual ~OnePole() = default;
inline void setCoeffs( float a0, float b1 )
{
@@ -207,7 +211,7 @@ public:
inline float update( float s, ch_cnt_t ch )
{
if( qAbs( s ) < 1.0e-10f && qAbs( 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;
}
@@ -215,14 +219,13 @@ private:
float m_a0, m_b1;
float m_z1 [CHANNELS];
};
typedef OnePole<2> StereoOnePole;
using StereoOnePole = OnePole<2>;
template<ch_cnt_t CHANNELS>
class BasicFilters
{
MM_OPERATORS
public:
enum FilterTypes
enum class FilterType
{
LowPass,
HiPass,
@@ -245,8 +248,7 @@ public:
Highpass_SV,
Notch_SV,
FastFormant,
Tripole,
NumFilters
Tripole
};
static inline float minFreq()
@@ -259,21 +261,21 @@ public:
return( 0.01f );
}
inline void setFilterType( const int _idx )
inline void setFilterType( const FilterType _idx )
{
m_doubleFilter = _idx == DoubleLowPass || _idx == DoubleMoog;
m_doubleFilter = _idx == FilterType::DoubleLowPass || _idx == FilterType::DoubleMoog;
if( !m_doubleFilter )
{
m_type = static_cast<FilterTypes>( _idx );
m_type = _idx;
return;
}
// Double lowpass mode, backwards-compat for the goofy
// Add-NumFilters to signify doubleFilter stuff
m_type = _idx == DoubleLowPass
? LowPass
: Moog;
if( m_subFilter == NULL )
m_type = _idx == FilterType::DoubleLowPass
? FilterType::LowPass
: FilterType::Moog;
if( m_subFilter == nullptr )
{
m_subFilter = new BasicFilters<CHANNELS>(
static_cast<sample_rate_t>(
@@ -286,7 +288,7 @@ public:
m_doubleFilter( false ),
m_sampleRate( (float) _sample_rate ),
m_sampleRatio( 1.0f / m_sampleRate ),
m_subFilter( NULL )
m_subFilter( nullptr )
{
clearHistory();
}
@@ -327,33 +329,39 @@ public:
}
}
inline void setSampleRate(const sample_rate_t sampleRate)
{
m_sampleRate = sampleRate;
m_sampleRatio = 1.f / m_sampleRate;
if (m_subFilter != nullptr)
{
m_subFilter->setSampleRate(m_sampleRate);
}
}
inline sample_t update( sample_t _in0, ch_cnt_t _chnl )
{
sample_t out;
sample_t out = 0.0f;
switch( m_type )
{
case Moog:
case FilterType::Moog:
{
sample_t x = _in0 - m_r*m_y4[_chnl];
// four cascaded onepole filters
// (bilinear transform)
m_y1[_chnl] = qBound( -10.0f,
( x + m_oldx[_chnl] ) * m_p
- m_k * m_y1[_chnl],
10.0f );
m_y2[_chnl] = qBound( -10.0f,
( m_y1[_chnl] + m_oldy1[_chnl] ) * m_p
- m_k * m_y2[_chnl],
10.0f );
m_y3[_chnl] = qBound( -10.0f,
( m_y2[_chnl] + m_oldy2[_chnl] ) * m_p
- m_k * m_y3[_chnl],
10.0f );
m_y4[_chnl] = qBound( -10.0f,
( m_y3[_chnl] + m_oldy3[_chnl] ) * m_p
- m_k * m_y4[_chnl],
m_y1[_chnl] = std::clamp((x + m_oldx[_chnl]) * m_p
- m_k * m_y1[_chnl], -10.0f,
10.0f);
m_y2[_chnl] = std::clamp((m_y1[_chnl] + m_oldy1[_chnl]) * m_p
- m_k * m_y2[_chnl], -10.0f,
10.0f);
m_y3[_chnl] = std::clamp((m_y2[_chnl] + m_oldy2[_chnl]) * m_p
- m_k * m_y3[_chnl], -10.0f,
10.0f );
m_y4[_chnl] = std::clamp((m_y3[_chnl] + m_oldy3[_chnl]) * m_p
- m_k * m_y4[_chnl], -10.0f,
10.0f);
m_oldx[_chnl] = x;
m_oldy1[_chnl] = m_y1[_chnl];
@@ -366,27 +374,23 @@ public:
// 3x onepole filters with 4x oversampling and interpolation of oversampled signal:
// input signal is linear-interpolated after oversampling, output signal is averaged from oversampled outputs
case Tripole:
case FilterType::Tripole:
{
out = 0.0f;
float ip = 0.0f;
for( int i = 0; i < 4; ++i )
{
ip += 0.25f;
sample_t x = linearInterpolate( m_last[_chnl], _in0, ip ) - m_r * m_y3[_chnl];
m_y1[_chnl] = qBound( -10.0f,
( x + m_oldx[_chnl] ) * m_p
- m_k * m_y1[_chnl],
10.0f );
m_y2[_chnl] = qBound( -10.0f,
( m_y1[_chnl] + m_oldy1[_chnl] ) * m_p
- m_k * m_y2[_chnl],
10.0f );
m_y3[_chnl] = qBound( -10.0f,
( m_y2[_chnl] + m_oldy2[_chnl] ) * m_p
- m_k * m_y3[_chnl],
10.0f );
m_y1[_chnl] = std::clamp((x + m_oldx[_chnl]) * m_p
- m_k * m_y1[_chnl], -10.0f,
10.0f);
m_y2[_chnl] = std::clamp((m_y1[_chnl] + m_oldy1[_chnl]) * m_p
- m_k * m_y2[_chnl], -10.0f,
10.0f);
m_y3[_chnl] = std::clamp((m_y2[_chnl] + m_oldy2[_chnl]) * m_p
- m_k * m_y3[_chnl], -10.0f,
10.0f);
m_oldx[_chnl] = x;
m_oldy1[_chnl] = m_y1[_chnl];
m_oldy2[_chnl] = m_y2[_chnl];
@@ -402,8 +406,8 @@ public:
// and extended to other SV filter types
// /* Hal Chamberlin's state variable filter */
case Lowpass_SV:
case Bandpass_SV:
case FilterType::Lowpass_SV:
case FilterType::Bandpass_SV:
{
float highpass;
@@ -419,15 +423,14 @@ public:
}
/* mix filter output into output buffer */
return m_type == Lowpass_SV
return m_type == FilterType::Lowpass_SV
? m_delay4[_chnl]
: m_delay3[_chnl];
}
case Highpass_SV:
case FilterType::Highpass_SV:
{
float hp;
for( int i = 0; i < 2; ++i ) // 2x oversample
{
m_delay2[_chnl] = m_delay2[_chnl] + m_svf1 * m_delay1[_chnl];
@@ -438,10 +441,9 @@ public:
return hp;
}
case Notch_SV:
case FilterType::Notch_SV:
{
float hp1, hp2;
float hp1;
for( int i = 0; i < 2; ++i ) // 2x oversample
{
m_delay2[_chnl] = m_delay2[_chnl] + m_svf1 * m_delay1[_chnl]; /* delay2/4 = lowpass output */
@@ -449,7 +451,7 @@ public:
m_delay1[_chnl] = m_svf1 * hp1 + m_delay1[_chnl]; /* delay1/3 = bandpass output */
m_delay4[_chnl] = m_delay4[_chnl] + m_svf2 * m_delay3[_chnl];
hp2 = m_delay2[_chnl] - m_delay4[_chnl] - m_svq * m_delay3[_chnl];
float hp2 = m_delay2[_chnl] - m_delay4[_chnl] - m_svq * m_delay3[_chnl];
m_delay3[_chnl] = m_svf2 * hp2 + m_delay3[_chnl];
}
@@ -463,22 +465,22 @@ public:
// can be driven up to self-oscillation (BTW: do not remove the limits!!!).
// (C) 1998 ... 2009 S.Fendt. Released under the GPL v2.0 or any later version.
case Lowpass_RC12:
case FilterType::Lowpass_RC12:
{
sample_t lp, bp, hp, in;
sample_t lp = 0.0f;
for( int n = 4; n != 0; --n )
{
in = _in0 + m_rcbp0[_chnl] * m_rcq;
in = qBound( -1.0f, in, 1.0f );
sample_t in = _in0 + m_rcbp0[_chnl] * m_rcq;
in = std::clamp(in, -1.0f, 1.0f);
lp = in * m_rcb + m_rclp0[_chnl] * m_rca;
lp = qBound( -1.0f, lp, 1.0f );
lp = std::clamp(lp, -1.0f, 1.0f);
hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] );
hp = qBound( -1.0f, hp, 1.0f );
sample_t hp = m_rcc * (m_rchp0[_chnl] + in - m_rclast0[_chnl]);
hp = std::clamp(hp, -1.0f, 1.0f);
bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca;
bp = qBound( -1.0f, bp, 1.0f );
sample_t bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca;
bp = std::clamp(bp, -1.0f, 1.0f);
m_rclast0[_chnl] = in;
m_rclp0[_chnl] = lp;
@@ -487,45 +489,45 @@ public:
}
return lp;
}
case Highpass_RC12:
case Bandpass_RC12:
case FilterType::Highpass_RC12:
case FilterType::Bandpass_RC12:
{
sample_t hp, bp, in;
sample_t hp, bp;
for( int n = 4; n != 0; --n )
{
in = _in0 + m_rcbp0[_chnl] * m_rcq;
in = qBound( -1.0f, in, 1.0f );
sample_t in = _in0 + m_rcbp0[_chnl] * m_rcq;
in = std::clamp(in, -1.0f, 1.0f);
hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] );
hp = qBound( -1.0f, hp, 1.0f );
hp = std::clamp(hp, -1.0f, 1.0f);
bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca;
bp = qBound( -1.0f, bp, 1.0f );
bp = std::clamp(bp, -1.0f, 1.0f);
m_rclast0[_chnl] = in;
m_rchp0[_chnl] = hp;
m_rcbp0[_chnl] = bp;
}
return m_type == Highpass_RC12 ? hp : bp;
return m_type == FilterType::Highpass_RC12 ? hp : bp;
}
case Lowpass_RC24:
case FilterType::Lowpass_RC24:
{
sample_t lp, bp, hp, in;
sample_t lp;
for( int n = 4; n != 0; --n )
{
// first stage is as for the 12dB case...
in = _in0 + m_rcbp0[_chnl] * m_rcq;
in = qBound( -1.0f, in, 1.0f );
sample_t in = _in0 + m_rcbp0[_chnl] * m_rcq;
in = std::clamp(in, -1.0f, 1.0f);
lp = in * m_rcb + m_rclp0[_chnl] * m_rca;
lp = qBound( -1.0f, lp, 1.0f );
lp = std::clamp(lp, -1.0f, 1.0f);
hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] );
hp = qBound( -1.0f, hp, 1.0f );
sample_t hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] );
hp = std::clamp(hp, -1.0f, 1.0f);
bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca;
bp = qBound( -1.0f, bp, 1.0f );
sample_t bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca;
bp = std::clamp(bp, -1.0f, 1.0f);
m_rclast0[_chnl] = in;
m_rclp0[_chnl] = lp;
@@ -534,16 +536,16 @@ public:
// second stage gets the output of the first stage as input...
in = lp + m_rcbp1[_chnl] * m_rcq;
in = qBound( -1.0f, in, 1.0f );
in = std::clamp(in, -1.0f, 1.0f );
lp = in * m_rcb + m_rclp1[_chnl] * m_rca;
lp = qBound( -1.0f, lp, 1.0f );
lp = std::clamp(lp, -1.0f, 1.0f);
hp = m_rcc * ( m_rchp1[_chnl] + in - m_rclast1[_chnl] );
hp = qBound( -1.0f, hp, 1.0f );
hp = std::clamp(hp, -1.0f, 1.0f);
bp = hp * m_rcb + m_rcbp1[_chnl] * m_rca;
bp = qBound( -1.0f, bp, 1.0f );
bp = std::clamp(bp, -1.0f, 1.0f);
m_rclast1[_chnl] = in;
m_rclp1[_chnl] = lp;
@@ -552,91 +554,89 @@ public:
}
return lp;
}
case Highpass_RC24:
case Bandpass_RC24:
case FilterType::Highpass_RC24:
case FilterType::Bandpass_RC24:
{
sample_t hp, bp, in;
sample_t hp, bp;
for( int n = 4; n != 0; --n )
{
// first stage is as for the 12dB case...
in = _in0 + m_rcbp0[_chnl] * m_rcq;
in = qBound( -1.0f, in, 1.0f );
sample_t in = _in0 + m_rcbp0[_chnl] * m_rcq;
in = std::clamp(in, -1.0f, 1.0f);
hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] );
hp = qBound( -1.0f, hp, 1.0f );
hp = std::clamp(hp, -1.0f, 1.0f);
bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca;
bp = qBound( -1.0f, bp, 1.0f );
bp = std::clamp(bp, -1.0f, 1.0f);
m_rclast0[_chnl] = in;
m_rchp0[_chnl] = hp;
m_rcbp0[_chnl] = bp;
// second stage gets the output of the first stage as input...
in = m_type == Highpass_RC24
in = m_type == FilterType::Highpass_RC24
? hp + m_rcbp1[_chnl] * m_rcq
: bp + m_rcbp1[_chnl] * m_rcq;
in = qBound( -1.0f, in, 1.0f );
in = std::clamp(in, -1.0f, 1.0f);
hp = m_rcc * ( m_rchp1[_chnl] + in - m_rclast1[_chnl] );
hp = qBound( -1.0f, hp, 1.0f );
hp = std::clamp(hp, -1.0f, 1.0f);
bp = hp * m_rcb + m_rcbp1[_chnl] * m_rca;
bp = qBound( -1.0f, bp, 1.0f );
bp = std::clamp(bp, -1.0f, 1.0f);
m_rclast1[_chnl] = in;
m_rchp1[_chnl] = hp;
m_rcbp1[_chnl] = bp;
}
return m_type == Highpass_RC24 ? hp : bp;
return m_type == FilterType::Highpass_RC24 ? hp : bp;
}
case Formantfilter:
case FastFormant:
case FilterType::Formantfilter:
case FilterType::FastFormant:
{
if( qAbs( _in0 ) < 1.0e-10f && qAbs( m_vflast[0][_chnl] ) < 1.0e-10f ) { return 0.0f; } // performance hack - skip processing when the numbers get too small
sample_t hp, bp, in;
if (std::abs(_in0) < 1.0e-10f && std::abs(m_vflast[0][_chnl]) < 1.0e-10f) { return 0.0f; } // performance hack - skip processing when the numbers get too small
out = 0;
const int os = m_type == FastFormant ? 1 : 4; // no oversampling for fast formant
const int os = m_type == FilterType::FastFormant ? 1 : 4; // no oversampling for fast formant
for( int o = 0; o < os; ++o )
{
// first formant
in = _in0 + m_vfbp[0][_chnl] * m_vfq;
in = qBound( -1.0f, in, 1.0f );
sample_t in = _in0 + m_vfbp[0][_chnl] * m_vfq;
in = std::clamp(in, -1.0f, 1.0f);
hp = m_vfc[0] * ( m_vfhp[0][_chnl] + in - m_vflast[0][_chnl] );
hp = qBound( -1.0f, hp, 1.0f );
sample_t hp = m_vfc[0] * ( m_vfhp[0][_chnl] + in - m_vflast[0][_chnl] );
hp = std::clamp(hp, -1.0f, 1.0f);
bp = hp * m_vfb[0] + m_vfbp[0][_chnl] * m_vfa[0];
bp = qBound( -1.0f, bp, 1.0f );
sample_t bp = hp * m_vfb[0] + m_vfbp[0][_chnl] * m_vfa[0];
bp = std::clamp(bp, -1.0f, 1.0f);
m_vflast[0][_chnl] = in;
m_vfhp[0][_chnl] = hp;
m_vfbp[0][_chnl] = bp;
in = bp + m_vfbp[2][_chnl] * m_vfq;
in = qBound( -1.0f, in, 1.0f );
in = std::clamp(in, -1.0f, 1.0f);
hp = m_vfc[0] * ( m_vfhp[2][_chnl] + in - m_vflast[2][_chnl] );
hp = qBound( -1.0f, hp, 1.0f );
hp = std::clamp(hp, -1.0f, 1.0f);
bp = hp * m_vfb[0] + m_vfbp[2][_chnl] * m_vfa[0];
bp = qBound( -1.0f, bp, 1.0f );
bp = std::clamp(bp, -1.0f, 1.0f);
m_vflast[2][_chnl] = in;
m_vfhp[2][_chnl] = hp;
m_vfbp[2][_chnl] = bp;
in = bp + m_vfbp[4][_chnl] * m_vfq;
in = qBound( -1.0f, in, 1.0f );
in = std::clamp(in, -1.0f, 1.0f);
hp = m_vfc[0] * ( m_vfhp[4][_chnl] + in - m_vflast[4][_chnl] );
hp = qBound( -1.0f, hp, 1.0f );
hp = std::clamp(hp, -1.0f, 1.0f);
bp = hp * m_vfb[0] + m_vfbp[4][_chnl] * m_vfa[0];
bp = qBound( -1.0f, bp, 1.0f );
bp = std::clamp(bp, -1.0f, 1.0f);
m_vflast[4][_chnl] = in;
m_vfhp[4][_chnl] = hp;
@@ -646,39 +646,39 @@ public:
// second formant
in = _in0 + m_vfbp[0][_chnl] * m_vfq;
in = qBound( -1.0f, in, 1.0f );
in = std::clamp(in, -1.0f, 1.0f);
hp = m_vfc[1] * ( m_vfhp[1][_chnl] + in - m_vflast[1][_chnl] );
hp = qBound( -1.0f, hp, 1.0f );
hp = std::clamp(hp, -1.0f, 1.0f);
bp = hp * m_vfb[1] + m_vfbp[1][_chnl] * m_vfa[1];
bp = qBound( -1.0f, bp, 1.0f );
bp = std::clamp(bp, -1.0f, 1.0f);
m_vflast[1][_chnl] = in;
m_vfhp[1][_chnl] = hp;
m_vfbp[1][_chnl] = bp;
in = bp + m_vfbp[3][_chnl] * m_vfq;
in = qBound( -1.0f, in, 1.0f );
in = std::clamp(in, -1.0f, 1.0f);
hp = m_vfc[1] * ( m_vfhp[3][_chnl] + in - m_vflast[3][_chnl] );
hp = qBound( -1.0f, hp, 1.0f );
hp = std::clamp(hp, -1.0f, 1.0f);
bp = hp * m_vfb[1] + m_vfbp[3][_chnl] * m_vfa[1];
bp = qBound( -1.0f, bp, 1.0f );
bp = std::clamp(bp, -1.0f, 1.0f);
m_vflast[3][_chnl] = in;
m_vfhp[3][_chnl] = hp;
m_vfbp[3][_chnl] = bp;
in = bp + m_vfbp[5][_chnl] * m_vfq;
in = qBound( -1.0f, in, 1.0f );
in = std::clamp(in, -1.0f, 1.0f);
hp = m_vfc[1] * ( m_vfhp[5][_chnl] + in - m_vflast[5][_chnl] );
hp = qBound( -1.0f, hp, 1.0f );
hp = std::clamp(hp, -1.0f, 1.0f);
bp = hp * m_vfb[1] + m_vfbp[5][_chnl] * m_vfa[1];
bp = qBound( -1.0f, bp, 1.0f );
bp = std::clamp(bp, -1.0f, 1.0f);
m_vflast[5][_chnl] = in;
m_vfhp[5][_chnl] = hp;
@@ -686,7 +686,7 @@ public:
out += bp;
}
return m_type == FastFormant ? out * 2.0f : out * 0.5f;
return m_type == FilterType::FastFormant ? out * 2.0f : out * 0.5f;
}
default:
@@ -707,16 +707,16 @@ public:
inline void calcFilterCoeffs( float _freq, float _q )
{
// temp coef vars
_q = qMax( _q, minQ() );
_q = std::max(_q, minQ());
if( m_type == Lowpass_RC12 ||
m_type == Bandpass_RC12 ||
m_type == Highpass_RC12 ||
m_type == Lowpass_RC24 ||
m_type == Bandpass_RC24 ||
m_type == Highpass_RC24 )
if( m_type == FilterType::Lowpass_RC12 ||
m_type == FilterType::Bandpass_RC12 ||
m_type == FilterType::Highpass_RC12 ||
m_type == FilterType::Lowpass_RC24 ||
m_type == FilterType::Bandpass_RC24 ||
m_type == FilterType::Highpass_RC24 )
{
_freq = qBound( 50.0f, _freq, 20000.0f );
_freq = std::clamp(_freq, 50.0f, 20000.0f);
const float sr = m_sampleRatio * 0.25f;
const float f = 1.0f / ( _freq * F_2PI );
@@ -729,10 +729,10 @@ public:
return;
}
if( m_type == Formantfilter ||
m_type == FastFormant )
if( m_type == FilterType::Formantfilter ||
m_type == FilterType::FastFormant )
{
_freq = qBound( minFreq(), _freq, 20000.0f ); // limit freq and q for not getting bad noise out of the filter...
_freq = std::clamp(_freq, minFreq(), 20000.0f); // limit freq and q for not getting bad noise out of the filter...
// formats for a, e, i, o, u, a
static const float _f[6][2] = { { 1000, 1400 }, { 500, 2300 },
@@ -755,7 +755,7 @@ public:
const float f1 = 1.0f / ( linearInterpolate( _f[vowel+0][1], _f[vowel+1][1], fract ) * F_2PI );
// samplerate coeff: depends on oversampling
const float sr = m_type == FastFormant ? m_sampleRatio : m_sampleRatio * 0.25f;
const float sr = m_type == FilterType::FastFormant ? m_sampleRatio : m_sampleRatio * 0.25f;
m_vfa[0] = 1.0f - sr / ( f0 + sr );
m_vfb[0] = 1.0f - m_vfa[0];
@@ -766,11 +766,11 @@ public:
return;
}
if( m_type == Moog ||
m_type == DoubleMoog )
if( m_type == FilterType::Moog ||
m_type == FilterType::DoubleMoog )
{
// [ 0 - 0.5 ]
const float f = qBound( minFreq(), _freq, 20000.0f ) * m_sampleRatio;
const float f = std::clamp(_freq, minFreq(), 20000.0f) * m_sampleRatio;
// (Empirical tunning)
m_p = ( 3.6f - 3.2f * f ) * f;
m_k = 2.0f * m_p - 1;
@@ -785,9 +785,9 @@ public:
return;
}
if( m_type == Tripole )
if( m_type == FilterType::Tripole )
{
const float f = qBound( 20.0f, _freq, 20000.0f ) * m_sampleRatio * 0.25f;
const float f = std::clamp(_freq, 20.0f, 20000.0f) * m_sampleRatio * 0.25f;
m_p = ( 3.6f - 3.2f * f ) * f;
m_k = 2.0f * m_p - 1.0f;
@@ -796,20 +796,20 @@ public:
return;
}
if( m_type == Lowpass_SV ||
m_type == Bandpass_SV ||
m_type == Highpass_SV ||
m_type == Notch_SV )
if( m_type == FilterType::Lowpass_SV ||
m_type == FilterType::Bandpass_SV ||
m_type == FilterType::Highpass_SV ||
m_type == FilterType::Notch_SV )
{
const float f = sinf( qMax( minFreq(), _freq ) * m_sampleRatio * F_PI );
m_svf1 = qMin( f, 0.825f );
m_svf2 = qMin( f * 2.0f, 0.825f );
m_svq = qMax( 0.0001f, 2.0f - ( _q * 0.1995f ) );
const float f = sinf(std::max(minFreq(), _freq) * m_sampleRatio * F_PI);
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));
return;
}
// other filters
_freq = qBound( minFreq(), _freq, 20000.0f );
_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 );
@@ -823,38 +823,38 @@ public:
switch( m_type )
{
case LowPass:
case FilterType::LowPass:
{
const float b1 = ( 1.0f - tcos ) * a0;
const float b0 = b1 * 0.5f;
m_biQuad.setCoeffs( a1, a2, b0, b1, b0 );
break;
}
case HiPass:
case FilterType::HiPass:
{
const float b1 = ( -1.0f - tcos ) * a0;
const float b0 = b1 * -0.5f;
m_biQuad.setCoeffs( a1, a2, b0, b1, b0 );
break;
}
case BandPass_CSG:
case FilterType::BandPass_CSG:
{
const float b0 = tsin * a0;
m_biQuad.setCoeffs( a1, a2, b0, 0.0f, -b0 );
break;
}
case BandPass_CZPG:
case FilterType::BandPass_CZPG:
{
const float b0 = alpha * a0;
m_biQuad.setCoeffs( a1, a2, b0, 0.0f, -b0 );
break;
}
case Notch:
case FilterType::Notch:
{
m_biQuad.setCoeffs( a1, a2, a0, a1, a0 );
break;
}
case AllPass:
case FilterType::AllPass:
{
m_biQuad.setCoeffs( a1, a2, a2, a1, 1.0f );
break;
@@ -886,7 +886,7 @@ private:
// coeffs for Lowpass_SV (state-variant lowpass)
float m_svf1, m_svf2, m_svq;
typedef sample_t frame[CHANNELS];
using frame = std::array<sample_t, CHANNELS>;
// in/out history for moog-filter
frame m_y1, m_y2, m_y3, m_y4, m_oldx, m_oldy1, m_oldy2, m_oldy3;
@@ -903,7 +903,7 @@ private:
// in/out history for Lowpass_SV (state-variant lowpass)
frame m_delay1, m_delay2, m_delay3, m_delay4;
FilterTypes m_type;
FilterType m_type;
bool m_doubleFilter;
float m_sampleRate;
@@ -913,4 +913,6 @@ private:
} ;
#endif
} // namespace lmms
#endif // LMMS_BASIC_FILTERS_H

View File

@@ -28,11 +28,22 @@
#include "lmms_export.h"
#include "lmms_basics.h"
namespace lmms
{
class SampleFrame;
/// Legacy interface for buffer re-use. Uses MemoryPool internally now.
class LMMS_EXPORT BufferPool
{
public:
static void init( fpp_t framesPerPeriod );
static sampleFrame * acquire();
static void release( sampleFrame * buf );
static void init( fpp_t fpp );
static SampleFrame* acquire();
static void release( SampleFrame* buf );
private:
static fpp_t s_framesPerPeriod;
};
} // namespace lmms

View File

@@ -23,23 +23,28 @@
*
*/
#ifndef LMMS_GUI_CPU_LOAD_WIDGET_H
#define LMMS_GUI_CPU_LOAD_WIDGET_H
#ifndef CPULOAD_WIDGET_H
#define CPULOAD_WIDGET_H
#include <QtCore/QTimer>
#include <algorithm>
#include <QTimer>
#include <QPixmap>
#include <QWidget>
#include "lmms_basics.h"
namespace lmms::gui
{
class CPULoadWidget : public QWidget
{
Q_OBJECT
Q_PROPERTY(int stepSize MEMBER m_stepSize)
public:
CPULoadWidget( QWidget * _parent );
virtual ~CPULoadWidget();
~CPULoadWidget() override = default;
protected:
@@ -51,6 +56,8 @@ protected slots:
private:
int stepSize() const { return std::max(1, m_stepSize); }
int m_currentLoad;
QPixmap m_temp;
@@ -61,7 +68,11 @@ private:
QTimer m_updateTimer;
int m_stepSize = 1;
} ;
#endif
} // namespace lmms::gui
#endif // LMMS_GUI_CPU_LOAD_WIDGET_H

View File

@@ -22,14 +22,16 @@
*
*/
#ifndef CAPTION_MENU_H
#define CAPTION_MENU_H
#ifndef LMMS_GUI_CAPTION_MENU_H
#define LMMS_GUI_CAPTION_MENU_H
#include <QMenu>
#include "lmms_export.h"
namespace lmms::gui
{
///
/// \brief A context menu with a caption
///
@@ -38,10 +40,10 @@ class LMMS_EXPORT CaptionMenu : public QMenu
Q_OBJECT
public:
CaptionMenu( const QString & _title, QWidget * _parent = 0 );
virtual ~CaptionMenu();
~CaptionMenu() override = default;
} ;
} // namespace lmms::gui
#endif
#endif // LMMS_GUI_CAPTION_MENU_H

View File

@@ -1,5 +1,5 @@
/*
* TrackConteintObject.h - declaration of TrackContentObject class
* TrackConteintObject.h - declaration of Clip class
*
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
@@ -22,30 +22,39 @@
*
*/
#ifndef TRACK_CONTENT_OBJECT_H
#define TRACK_CONTENT_OBJECT_H
#ifndef LMMS_CLIP_H
#define LMMS_CLIP_H
#include <optional>
#include <QColor>
#include "AutomatableModel.h"
#include "lmms_basics.h"
namespace lmms
{
class Track;
class TrackContentObjectView;
class TrackContainer;
namespace gui
{
class ClipView;
class TrackView;
} // namespace gui
class LMMS_EXPORT TrackContentObject : public Model, public JournallingObject
class LMMS_EXPORT Clip : public Model, public JournallingObject
{
Q_OBJECT
MM_OPERATORS
mapPropertyFromModel(bool,isMuted,setMuted,m_mutedModel);
mapPropertyFromModel(bool,isSolo,setSolo,m_soloModel);
public:
TrackContentObject( Track * track );
virtual ~TrackContentObject();
Clip( Track * track );
~Clip() override;
inline Track * getTrack() const
{
@@ -85,6 +94,12 @@ public:
return m_length;
}
/*! \brief Specify whether or not a TCO automatically resizes.
*
* If a TCO does automatically resize, it cannot be manually
* resized by clicking and dragging its edge.
*
*/
inline void setAutoResize( const bool r )
{
m_autoResize = r;
@@ -95,29 +110,13 @@ public:
return m_autoResize;
}
QColor color() const
{
return m_color;
}
void setColor( const QColor & c )
{
m_color = c;
}
bool hasColor();
void useCustomClipColor( bool b );
bool usesCustomClipColor()
{
return m_useCustomClipColor;
}
auto color() const -> const std::optional<QColor>& { return m_color; }
void setColor(const std::optional<QColor>& color);
virtual void movePosition( const TimePos & pos );
virtual void changeLength( const TimePos & length );
virtual TrackContentObjectView * createView( TrackView * tv ) = 0;
virtual gui::ClipView * createView( gui::TrackView * tv ) = 0;
inline void selectViewOnCreate( bool select )
{
@@ -130,15 +129,13 @@ public:
}
/// Returns true if and only if a->startPosition() < b->startPosition()
static bool comparePosition(const TrackContentObject* a, const TrackContentObject* b);
static bool comparePosition(const Clip* a, const Clip* b);
TimePos startTimeOffset() const;
void setStartTimeOffset( const TimePos &startTimeOffset );
void updateColor();
// Will copy the state of a TCO to another TCO
static void copyStateTo( TrackContentObject *src, TrackContentObject *dst );
// Will copy the state of a clip to another clip
static void copyStateTo( Clip *src, Clip *dst );
public slots:
void toggleMute();
@@ -147,18 +144,11 @@ public slots:
signals:
void lengthChanged();
void positionChanged();
void destroyedTCO();
void trackColorChanged();
void destroyedClip();
void colorChanged();
private:
enum Actions
{
NoAction,
Move,
Resize
} ;
Track * m_track;
QString m_name;
@@ -172,12 +162,13 @@ private:
bool m_selectViewOnCreate;
QColor m_color;
bool m_useCustomClipColor;
std::optional<QColor> m_color;
friend class TrackContentObjectView;
friend class ClipView;
} ;
#endif
} // namespace lmms
#endif // LMMS_CLIP_H

View File

@@ -1,5 +1,5 @@
/*
* TrackContentObjectView.h - declaration of TrackContentObjectView class
* ClipView.h - declaration of ClipView class
*
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
@@ -22,27 +22,35 @@
*
*/
#ifndef TRACK_CONTENT_OBJECT_VIEW_H
#define TRACK_CONTENT_OBJECT_VIEW_H
#ifndef LMMS_GUI_CLIP_VIEW_H
#define LMMS_GUI_CLIP_VIEW_H
#include <optional>
#include <QtCore/QVector>
#include <QVector>
#include "ModelView.h"
#include "Rubberband.h"
#include "TrackContentObject.h"
#include "Clip.h"
class QMenu;
class QContextMenuEvent;
namespace lmms
{
class DataFile;
class Clip;
namespace gui
{
class TextFloat;
class TrackContentObject;
class TrackView;
class TrackContentObjectView : public selectableObject, public ModelView
class ClipView : public selectableObject, public ModelView
{
Q_OBJECT
@@ -53,21 +61,24 @@ class TrackContentObjectView : public selectableObject, public ModelView
Q_PROPERTY( QColor textColor READ textColor WRITE setTextColor )
Q_PROPERTY( QColor textBackgroundColor READ textBackgroundColor WRITE setTextBackgroundColor )
Q_PROPERTY( QColor textShadowColor READ textShadowColor WRITE setTextShadowColor )
Q_PROPERTY( QColor BBPatternBackground READ BBPatternBackground WRITE setBBPatternBackground )
Q_PROPERTY( QColor patternClipBackground READ patternClipBackground WRITE setPatternClipBackground )
Q_PROPERTY( bool gradient READ gradient WRITE setGradient )
// We have to use a QSize here because using QPoint isn't supported.
// width -> x, height -> y
Q_PROPERTY( QSize mouseHotspotHand WRITE setMouseHotspotHand )
Q_PROPERTY( QSize mouseHotspotHand MEMBER m_mouseHotspotHand )
Q_PROPERTY( QSize mouseHotspotKnife MEMBER m_mouseHotspotKnife )
public:
TrackContentObjectView( TrackContentObject * tco, TrackView * tv );
virtual ~TrackContentObjectView();
const static int BORDER_WIDTH = 2;
bool fixedTCOs();
ClipView( Clip * clip, TrackView * tv );
~ClipView() override;
inline TrackContentObject * getTrackContentObject()
bool fixedClips();
inline Clip * getClip()
{
return m_tco;
return m_clip;
}
inline TrackView * getTrackView()
@@ -82,7 +93,7 @@ public:
QColor textColor() const;
QColor textBackgroundColor() const;
QColor textShadowColor() const;
QColor BBPatternBackground() const;
QColor patternClipBackground() const;
bool gradient() const;
void setMutedColor( const QColor & c );
void setMutedBackgroundColor( const QColor & c );
@@ -90,48 +101,63 @@ public:
void setTextColor( const QColor & c );
void setTextBackgroundColor( const QColor & c );
void setTextShadowColor( const QColor & c );
void setBBPatternBackground( const QColor & c );
void setPatternClipBackground(const QColor& c);
void setGradient( const bool & b );
void setMouseHotspotHand(const QSize & s);
// access needsUpdate member variable
bool needsUpdate();
void setNeedsUpdate( bool b );
// Method to get a QVector of TCOs to be affected by a context menu action
QVector<TrackContentObjectView *> getClickedTCOs();
// Method to get a QVector of Clips to be affected by a context menu action
QVector<ClipView *> getClickedClips();
// Methods to remove, copy, cut, paste and mute a QVector of TCO views
void copy( QVector<TrackContentObjectView *> tcovs );
void cut( QVector<TrackContentObjectView *> tcovs );
// Methods to remove, copy, cut, paste and mute a QVector of Clip views
void copy( QVector<ClipView *> clipvs );
void cut( QVector<ClipView *> clipvs );
void paste();
// remove and toggleMute are static because they don't depend
// being called from a particular TCO view, but can be called anywhere as long
// as a valid TCO view list is given, while copy/cut require an instance for
// being called from a particular Clip view, but can be called anywhere as long
// as a valid Clip view list is given, while copy/cut require an instance for
// some metadata to be written to the clipboard.
static void remove( QVector<TrackContentObjectView *> tcovs );
static void toggleMute( QVector<TrackContentObjectView *> tcovs );
static void remove( QVector<ClipView *> clipvs );
static void toggleMute( QVector<ClipView *> clipvs );
static void mergeClips(QVector<ClipView*> clipvs);
// Returns true if selection can be merged and false if not
static bool canMergeSelection(QVector<ClipView*> clipvs);
QColor getColorForDisplay( QColor );
void inline setMarkerPos(int x) { m_markerPos = x; }
void inline setMarkerEnabled(bool e) { m_marker = e; }
public slots:
virtual bool close();
void remove();
void update() override;
void changeClipColor();
void useTrackColor();
void selectColor();
void randomizeColor();
void resetColor();
protected:
enum ContextMenuAction
enum class ContextMenuAction
{
Remove,
Cut,
Copy,
Paste,
Mute
Mute,
Merge
};
TrackView * m_trackView;
TimePos m_initialClipPos;
TimePos m_initialClipEnd;
bool m_marker = false;
int m_markerPos = 0;
virtual void constructContextMenu( QMenu * )
{
}
@@ -140,7 +166,6 @@ protected:
void contextMenuAction( ContextMenuAction action );
void dragEnterEvent( QDragEnterEvent * dee ) override;
void dropEvent( QDropEvent * de ) override;
void leaveEvent( QEvent * e ) override;
void mousePressEvent( QMouseEvent * me ) override;
void mouseMoveEvent( QMouseEvent * me ) override;
void mouseReleaseEvent( QMouseEvent * me ) override;
@@ -150,13 +175,17 @@ protected:
selectableObject::resizeEvent( re );
}
bool unquantizedModHeld( QMouseEvent * me );
TimePos quantizeSplitPos( TimePos, bool shiftMode );
float pixelsPerBar();
DataFile createTCODataFiles(const QVector<TrackContentObjectView *> & tcos) const;
DataFile createClipDataFiles(const QVector<ClipView *> & clips) const;
virtual void paintTextLabel(QString const & text, QPainter & painter);
auto hasCustomColor() const -> bool;
protected slots:
void updateLength();
@@ -164,26 +193,24 @@ protected slots:
private:
enum Actions
enum class Action
{
NoAction,
None,
Move,
MoveSelection,
Resize,
ResizeLeft,
Split,
CopySelection,
ToggleSelected
} ;
static TextFloat * s_textFloat;
TrackContentObject * m_tco;
TrackView * m_trackView;
Actions m_action;
Clip * m_clip;
Action m_action;
QPoint m_initialMousePos;
QPoint m_initialMouseGlobalPos;
TimePos m_initialTCOPos;
TimePos m_initialTCOEnd;
QVector<TimePos> m_initialOffsets;
TextFloat * m_hint;
@@ -195,9 +222,12 @@ private:
QColor m_textColor;
QColor m_textBackgroundColor;
QColor m_textShadowColor;
QColor m_BBPatternBackground;
QColor m_patternClipBackground;
bool m_gradient;
QSize m_mouseHotspotHand; // QSize must be used because QPoint isn't supported by property system
QSize m_mouseHotspotHand; // QSize must be used because QPoint
QSize m_mouseHotspotKnife; // isn't supported by property system
QCursor m_cursorHand;
QCursor m_cursorKnife;
bool m_cursorSetYet;
bool m_needsUpdate;
@@ -205,14 +235,23 @@ private:
{
m_initialMousePos = pos;
m_initialMouseGlobalPos = mapToGlobal( pos );
m_initialTCOPos = m_tco->startPosition();
m_initialTCOEnd = m_initialTCOPos + m_tco->length();
m_initialClipPos = m_clip->startPosition();
m_initialClipEnd = m_initialClipPos + m_clip->length();
}
void setInitialOffsets();
bool mouseMovedDistance( QMouseEvent * me, int distance );
TimePos draggedTCOPos( QMouseEvent * me );
TimePos draggedClipPos( QMouseEvent * me );
int knifeMarkerPos( QMouseEvent * me );
void setColor(const std::optional<QColor>& color);
//! Return true iff the clip could be split. Currently only implemented for samples
virtual bool splitClip( const TimePos pos ){ return false; };
void updateCursor(QMouseEvent * me);
} ;
#endif
} // namespace gui
} // namespace lmms
#endif // LMMS_GUI_CLIP_VIEW_H

View File

@@ -1,5 +1,5 @@
/*
* Clipboard.h - the clipboard for patterns, notes etc.
* Clipboard.h - the clipboard for clips, notes etc.
*
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
@@ -22,15 +22,19 @@
*
*/
#ifndef CLIPBOARD_H
#define CLIPBOARD_H
#ifndef LMMS_CLIPBOARD_H
#define LMMS_CLIPBOARD_H
#include <QtCore/QMap>
#include <QDomElement>
#include <QMap>
#include "lmms_export.h"
namespace Clipboard
class QMimeData;
namespace lmms::Clipboard
{
enum class MimeType
{
StringPair,
@@ -42,7 +46,7 @@ namespace Clipboard
bool hasFormat( MimeType mT );
// Helper methods for String data
void copyString( const QString & str, MimeType mT );
void LMMS_EXPORT copyString(const QString& str, MimeType mT);
QString getString( MimeType mT );
// Helper methods for String Pair data
@@ -63,6 +67,7 @@ namespace Clipboard
break;
}
}
} ;
#endif
} // namespace lmms::Clipboard
#endif // LMMS_CLIPBOARD_H

View File

@@ -21,19 +21,26 @@
*
*/
#ifndef LMMS_GUI_COLOR_CHOOSER_H
#define LMMS_GUI_COLOR_CHOOSER_H
#include <QApplication>
#include <QColor>
#include <QColorDialog>
#include <QKeyEvent>
#include <QVector>
class ColorChooser: public QColorDialog
namespace lmms::gui
{
class ColorChooser : public QColorDialog
{
public:
ColorChooser(const QColor &initial, QWidget *parent): QColorDialog(initial, parent) {};
ColorChooser(QWidget *parent): QColorDialog(parent) {};
//! For getting a color without having to initialise a color dialog
ColorChooser() {};
ColorChooser() = default;
enum class Palette {Default, Track, Mixer};
//! Set global palette via array, checking bounds
void setPalette (QVector<QColor>);
@@ -57,3 +64,8 @@ private:
//! Generate a nice palette, with adjustable value
static QVector<QColor> nicePalette (int);
};
} // namespace lmms::gui
#endif // LMMS_GUI_COLOR_CHOOSER_H

54
include/ColorHelper.h Normal file
View File

@@ -0,0 +1,54 @@
/* ColorHelper.h - Helper methods for color related algorithms, etc.
*
* Copyright (c) 2024- Michael Gregorius
*
* 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_GUI_COLOR_HELPER_H
#define LMMS_GUI_COLOR_HELPER_H
#include <QColor>
namespace lmms::gui
{
class ColorHelper
{
public:
static QColor interpolateInRgb(const QColor& a, const QColor& b, float t)
{
qreal ar, ag, ab, aa;
a.getRgbF(&ar, &ag, &ab, &aa);
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);
return QColor::fromRgbF(interH, interS, interV, interA);
}
};
} // namespace lmms::gui
#endif // LMMS_GUI_COLOR_HELPER_H

View File

@@ -22,9 +22,8 @@
*
*/
#ifndef COMBOBOX_H
#define COMBOBOX_H
#ifndef LMMS_GUI_COMBOBOX_H
#define LMMS_GUI_COMBOBOX_H
#include <QMenu>
#include <QWidget>
@@ -32,12 +31,15 @@
#include "ComboBoxModel.h"
#include "AutomatableModelView.h"
namespace lmms::gui
{
class LMMS_EXPORT ComboBox : public QWidget, public IntModelView
{
Q_OBJECT
public:
ComboBox( QWidget* parent = NULL, const QString& name = QString() );
virtual ~ComboBox();
ComboBox( QWidget* parent = nullptr, const QString& name = QString() );
~ComboBox() override = default;
ComboBoxModel* model()
{
@@ -64,9 +66,9 @@ protected:
private:
static QPixmap* s_background;
static QPixmap* s_arrow;
static QPixmap* s_arrowSelected;
QPixmap m_background = embed::getIconPixmap("combobox_bg");
QPixmap m_arrow = embed::getIconPixmap("combobox_arrow");
QPixmap m_arrowSelected = embed::getIconPixmap("combobox_arrow_selected");
QMenu m_menu;
@@ -78,4 +80,6 @@ private slots:
} ;
#endif
} // namespace lmms::gui
#endif // LMMS_GUI_COMBOBOX_H

View File

@@ -22,8 +22,8 @@
*
*/
#ifndef COMBOBOX_MODEL_H
#define COMBOBOX_MODEL_H
#ifndef LMMS_COMBOBOX_MODEL_H
#define LMMS_COMBOBOX_MODEL_H
#include <memory>
#include <utility>
@@ -32,26 +32,25 @@
#include "AutomatableModel.h"
#include "embed.h"
namespace lmms
{
class LMMS_EXPORT ComboBoxModel : public IntModel
{
Q_OBJECT
MODEL_IS_VISITABLE
public:
ComboBoxModel( Model* parent = NULL,
ComboBoxModel( Model* parent = nullptr,
const QString& displayName = QString(),
bool isDefaultConstructed = false ) :
IntModel( 0, 0, 0, parent, displayName, isDefaultConstructed )
{
}
virtual ~ComboBoxModel()
{
clear();
}
void addItem( QString item, std::unique_ptr<PixmapLoader> loader = nullptr );
void replaceItem(std::size_t index, QString item, std::unique_ptr<PixmapLoader> loader = nullptr);
void clear();
int findText( const QString& txt ) const;
@@ -68,12 +67,12 @@ public:
const QString & itemText( int i ) const
{
return m_items[qBound<int>( minValue(), i, maxValue() )].first;
return m_items[std::clamp(i, minValue(), maxValue())].first;
}
const PixmapLoader* itemPixmap( int i ) const
{
return m_items[qBound<int>( minValue(), i, maxValue() )].second.get();
return m_items[std::clamp(i, minValue(), maxValue())].second.get();
}
int size() const
@@ -83,11 +82,12 @@ public:
private:
typedef std::pair<QString, std::unique_ptr<PixmapLoader> > Item;
using Item = std::pair<QString, std::unique_ptr<PixmapLoader>>;
std::vector<Item> m_items;
} ;
} // namespace lmms
#endif
#endif // LMMS_COMBOBOX_MODEL_H

View File

@@ -22,22 +22,25 @@
*
*/
#ifndef CONFIG_MGR_H
#define CONFIG_MGR_H
#ifndef LMMS_CONFIG_MANAGER_H
#define LMMS_CONFIG_MANAGER_H
#include "lmmsconfig.h"
#include <QtCore/QMap>
#include <QtCore/QPair>
#include <QtCore/QStringList>
#include <QtCore/QVector>
#include <QtCore/QObject>
#include <QMap>
#include <QPair>
#include <QStringList>
#include <QObject>
#include <vector>
#include "lmms_export.h"
class LmmsCore;
namespace lmms
{
class Engine;
const QString PROJECTS_PATH = "projects/";
const QString TEMPLATE_PATH = "templates/";
@@ -60,7 +63,7 @@ class LMMS_EXPORT ConfigManager : public QObject
public:
static inline ConfigManager * inst()
{
if(s_instanceOfMe == NULL )
if(s_instanceOfMe == nullptr )
{
s_instanceOfMe = new ConfigManager();
}
@@ -227,6 +230,7 @@ public:
QString defaultVersion() const;
static bool enableBlockedPlugins();
static QStringList availableVstEmbedMethods();
QString vstEmbedMethod() const;
@@ -236,11 +240,8 @@ public:
void addRecentlyOpenedProject(const QString & _file);
const QString & value(const QString & cls,
const QString & attribute) const;
const QString & value(const QString & cls,
const QString & attribute,
const QString & defaultVal) const;
QString value(const QString& cls, const QString& attribute, const QString& defaultVal = "") const;
void setValue(const QString & cls, const QString & attribute,
const QString & value);
void deleteValue(const QString & cls, const QString & attribute);
@@ -270,10 +271,11 @@ private:
ConfigManager();
ConfigManager(const ConfigManager & _c);
~ConfigManager();
~ConfigManager() override;
void upgrade_1_1_90();
void upgrade_1_1_91();
void upgrade_1_2_2();
void upgrade();
// List of all upgrade methods
@@ -298,11 +300,15 @@ private:
unsigned int m_configVersion;
QStringList m_recentlyOpenedProjects;
typedef QVector<QPair<QString, QString> > stringPairVector;
typedef QMap<QString, stringPairVector> settingsMap;
using stringPairVector = std::vector<QPair<QString, QString>>;
using settingsMap = QMap<QString, stringPairVector>;
settingsMap m_settings;
friend class LmmsCore;
friend class Engine;
};
#endif
} // namespace lmms
#endif // LMMS_CONFIG_MANAGER_H

View File

@@ -70,15 +70,21 @@
**
****************************************************************************/
#ifndef CONTROLLAYOUT_H
#define CONTROLLAYOUT_H
#ifndef LMMS_GUI_CONTROL_LAYOUT_H
#define LMMS_GUI_CONTROL_LAYOUT_H
#include <QLayout>
#include <QMultiMap>
#include <QStyle>
class QLayoutItem;
class QRect;
class QString;
class QLineEdit;
namespace lmms::gui
{
/**
Layout for controls (models)
@@ -128,9 +134,11 @@ private:
// relevant dimension is width, as later, heightForWidth() will be called
// 400 looks good and is ~4 knobs in a row
constexpr const static int m_minWidth = 400;
class QLineEdit* m_searchBar;
QLineEdit* m_searchBar;
//! name of search bar, must be ASCII sorted before any alpha numerics
static constexpr const char* s_searchBarName = "!!searchBar!!";
};
#endif // CONTROLLAYOUT_H
} // namespace lmms::gui
#endif // LMMS_GUI_CONTROL_LAYOUT_H

View File

@@ -23,9 +23,8 @@
*
*/
#ifndef CONTROLLER_H
#define CONTROLLER_H
#ifndef LMMS_CONTROLLER_H
#define LMMS_CONTROLLER_H
#include "lmms_export.h"
#include "Engine.h"
@@ -33,34 +32,41 @@
#include "JournallingObject.h"
#include "ValueBuffer.h"
class ControllerDialog;
namespace lmms
{
class Controller;
class ControllerConnection;
typedef QVector<Controller *> ControllerVector;
namespace gui
{
class ControllerDialog;
} // namespace gui
using ControllerVector = std::vector<Controller*>;
class LMMS_EXPORT Controller : public Model, public JournallingObject
{
Q_OBJECT
public:
enum ControllerTypes
enum class ControllerType
{
DummyController,
LfoController,
MidiController,
PeakController,
Dummy,
Lfo,
Midi,
Peak,
/*
XYController,
EquationController
XY,
Equation
*/
NumControllerTypes
} ;
Controller( ControllerTypes _type, Model * _parent,
Controller( ControllerType _type, Model * _parent,
const QString & _display_name );
virtual ~Controller();
~Controller() override;
virtual float currentValue( int _offset );
// The per-controller get-value-in-buffers function
@@ -76,7 +82,7 @@ public:
m_sampleExact = _exact;
}
inline ControllerTypes type() const
inline ControllerType type() const
{
return( m_type );
}
@@ -87,8 +93,8 @@ public:
{
switch( m_type )
{
case LfoController: return( true );
case PeakController: return( true );
case ControllerType::Lfo: return( true );
case ControllerType::Peak: return( true );
default:
break;
}
@@ -105,13 +111,13 @@ public:
void loadSettings( const QDomElement & _this ) override;
QString nodeName() const override;
static Controller * create( ControllerTypes _tt, Model * _parent );
static Controller * create( ControllerType _tt, Model * _parent );
static Controller * create( const QDomElement & _this,
Model * _parent );
inline static float fittedValue( float _val )
{
return qBound<float>( 0.0f, _val, 1.0f );
return std::clamp(_val, 0.0f, 1.0f);
}
static long runningPeriods()
@@ -132,7 +138,7 @@ public:
bool hasModel( const Model * m ) const;
public slots:
virtual ControllerDialog * createDialog( QWidget * _parent );
virtual gui::ControllerDialog * createDialog( QWidget * _parent );
virtual void setName( const QString & _new_name )
{
@@ -158,7 +164,7 @@ protected:
int m_connectionCount;
QString m_name;
ControllerTypes m_type;
ControllerType m_type;
static ControllerVector s_controllers;
@@ -166,12 +172,14 @@ protected:
signals:
// The value changed while the mixer isn't running (i.e: MIDI CC)
// The value changed while the audio engine isn't running (i.e: MIDI CC)
void valueChanged();
friend class ControllerDialog;
friend class gui::ControllerDialog;
} ;
#endif
} // namespace lmms
#endif // LMMS_CONTROLLER_H

View File

@@ -26,31 +26,38 @@
*
*/
#ifndef LMMS_CONTROLLER_CONNECTION_H
#define LMMS_CONTROLLER_CONNECTION_H
#ifndef CONTROLLER_CONNECTION_H
#define CONTROLLER_CONNECTION_H
#include <QtCore/QObject>
#include <QtCore/QVector>
#include <QObject>
#include "Controller.h"
#include "JournallingObject.h"
#include "ValueBuffer.h"
#include <vector>
namespace lmms
{
class ControllerConnection;
typedef QVector<ControllerConnection *> ControllerConnectionVector;
namespace gui
{
class ControllerConnectionDialog;
}
using ControllerConnectionVector = std::vector<ControllerConnection*>;
class LMMS_EXPORT ControllerConnection : public QObject, public JournallingObject
{
Q_OBJECT
public:
ControllerConnection( Controller * _controller );
ControllerConnection(Controller * _controller);
ControllerConnection( int _controllerId );
virtual ~ControllerConnection();
~ControllerConnection() override;
inline Controller * getController()
{
@@ -98,7 +105,6 @@ public:
return classNodeName();
}
public slots:
void deleteConnection();
@@ -113,11 +119,13 @@ protected:
static ControllerConnectionVector s_connections;
signals:
// The value changed while the mixer isn't running (i.e: MIDI CC)
// The value changed while the audio engine isn't running (i.e: MIDI CC)
void valueChanged();
friend class ControllerConnectionDialog;
friend class gui::ControllerConnectionDialog;
};
#endif
} // namespace lmms
#endif // LMMS_CONTROLLER_CONNECTION_H

View File

@@ -23,9 +23,8 @@
*
*/
#ifndef CONTROLLER_CONNECTION_DIALOG_H
#define CONTROLLER_CONNECTION_DIALOG_H
#ifndef LMMS_GUI_CONTROLLER_CONNECTION_DIALOG_H
#define LMMS_GUI_CONTROLLER_CONNECTION_DIALOG_H
#include <QDialog>
#include <QSortFilterProxyModel>
@@ -38,7 +37,15 @@
class QLineEdit;
class QListView;
class QScrollArea;
namespace lmms
{
class AutoDetectMidiController;
namespace gui
{
class ComboBox;
class GroupBox;
class TabWidget;
@@ -47,14 +54,13 @@ class LedCheckBox;
class MidiPortMenu;
class ControllerConnectionDialog : public QDialog
{
Q_OBJECT
public:
ControllerConnectionDialog( QWidget * _parent,
const AutomatableModel * _target_model );
virtual ~ControllerConnectionDialog();
~ControllerConnectionDialog() override;
Controller * chosenController()
{
@@ -99,4 +105,9 @@ private:
AutoDetectMidiController * m_midiController;
} ;
#endif
} // namespace gui
} // namespace lmms
#endif // LMMS_GUI_CONTROLLER_CONNECTION_DIALOG_H

View File

@@ -23,15 +23,20 @@
*
*/
#ifndef CONTROLLER_DIALOG_H
#define CONTROLLER_DIALOG_H
#ifndef LMMS_GUI_CONTROLLER_DIALOG_H
#define LMMS_GUI_CONTROLLER_DIALOG_H
#include <QWidget>
#include "ModelView.h"
namespace lmms
{
class Controller;
namespace gui
{
class ControllerDialog : public QWidget, public ModelView
{
@@ -39,7 +44,7 @@ class ControllerDialog : public QWidget, public ModelView
public:
ControllerDialog( Controller * _controller, QWidget * _parent );
virtual ~ControllerDialog();
~ControllerDialog() override = default;
signals:
@@ -51,4 +56,9 @@ protected:
} ;
#endif
} // namespace gui
} // namespace lmms
#endif // LMMS_GUI_CONTROLLER_DIALOG_H

View File

@@ -22,8 +22,8 @@
*
*/
#ifndef CONTROLLER_RACK_VIEW_H
#define CONTROLLER_RACK_VIEW_H
#ifndef LMMS_GUI_CONTROLLER_RACK_VIEW_H
#define LMMS_GUI_CONTROLLER_RACK_VIEW_H
#include <QWidget>
#include <QCloseEvent>
@@ -36,16 +36,24 @@ class QPushButton;
class QScrollArea;
class QVBoxLayout;
class ControllerView;
namespace lmms
{
class Controller;
namespace gui
{
class ControllerView;
class ControllerRackView : public QWidget, public SerializingObject
{
Q_OBJECT
public:
ControllerRackView();
virtual ~ControllerRackView();
~ControllerRackView() override = default;
void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override;
void loadSettings( const QDomElement & _this ) override;
@@ -57,9 +65,11 @@ public:
public slots:
void deleteController( ControllerView * _view );
void onControllerAdded( Controller * );
void onControllerRemoved( Controller * );
void deleteController(ControllerView* view);
void moveUp(ControllerView* view);
void moveDown(ControllerView* view);
void addController(Controller* controller);
void removeController(Controller* controller);
protected:
void closeEvent( QCloseEvent * _ce ) override;
@@ -80,4 +90,8 @@ private:
int m_nextIndex;
} ;
#endif
} // namespace gui
} // namespace lmms
#endif // LMMS_GUI_CONTROLLER_RACK_VIEW_H

View File

@@ -22,8 +22,8 @@
*
*/
#ifndef CONTROLLER_VIEW_H
#define CONTROLLER_VIEW_H
#ifndef LMMS_GUI_CONTROLLER_VIEW_H
#define LMMS_GUI_CONTROLLER_VIEW_H
#include <QFrame>
@@ -36,6 +36,10 @@ class QLabel;
class QPushButton;
class QMdiSubWindow;
namespace lmms::gui
{
class LedCheckBox;
@@ -44,7 +48,7 @@ class ControllerView : public QFrame, public ModelView
Q_OBJECT
public:
ControllerView( Controller * _controller, QWidget * _parent );
virtual ~ControllerView();
~ControllerView() override;
inline Controller * getController()
{
@@ -59,12 +63,16 @@ public:
public slots:
void editControls();
void deleteController();
void removeController();
void closeControls();
void renameController();
void moveUp();
void moveDown();
signals:
void deleteController( ControllerView * _view );
void movedUp(ControllerView* view);
void movedDown(ControllerView* view);
void removedController(ControllerView* view);
protected:
@@ -81,4 +89,7 @@ private:
} ;
#endif
} // namespace lmms::gui
#endif // LMMS_GUI_CONTROLLER_VIEW_H

View File

@@ -22,11 +22,8 @@
*
*/
#ifndef CONTROLS_H
#define CONTROLS_H
#include "Model.h"
#ifndef LMMS_GUI_CONTROLS_H
#define LMMS_GUI_CONTROLS_H
// headers only required for covariance
#include "AutomatableModel.h"
@@ -35,8 +32,20 @@
class QString;
class QWidget;
class QLabel;
namespace lmms
{
class AutomatableModel;
namespace gui
{
class AutomatableModelView;
class Knob;
class ComboBox;
class LedCheckBox;
/**
These classes provide
@@ -53,15 +62,15 @@ public:
virtual void setModel(AutomatableModel* model) = 0;
virtual AutomatableModel* model() = 0;
virtual class AutomatableModelView* modelView() = 0;
virtual AutomatableModelView* modelView() = 0;
virtual ~Control();
virtual ~Control() = default;
};
class KnobControl : public Control
{
class Knob* m_knob;
Knob* m_knob;
public:
void setText(const QString& text) override;
@@ -69,18 +78,18 @@ public:
void setModel(AutomatableModel* model) override;
FloatModel* model() override;
class AutomatableModelView* modelView() override;
AutomatableModelView* modelView() override;
KnobControl(QWidget* parent = nullptr);
~KnobControl() override;
~KnobControl() override = default;
};
class ComboControl : public Control
{
QWidget* m_widget;
class ComboBox* m_combo;
class QLabel* m_label;
ComboBox* m_combo;
QLabel* m_label;
public:
void setText(const QString& text) override;
@@ -88,10 +97,10 @@ public:
void setModel(AutomatableModel* model) override;
ComboBoxModel* model() override;
class AutomatableModelView* modelView() override;
AutomatableModelView* modelView() override;
ComboControl(QWidget* parent = nullptr);
~ComboControl() override;
~ComboControl() override = default;
};
@@ -105,17 +114,17 @@ public:
void setModel(AutomatableModel* model) override;
IntModel* model() override;
class AutomatableModelView* modelView() override;
AutomatableModelView* modelView() override;
LcdControl(int numDigits, QWidget* parent = nullptr);
~LcdControl() override;
~LcdControl() override = default;
};
class CheckControl : public Control
{
QWidget* m_widget;
class LedCheckBox* m_checkBox;
LedCheckBox* m_checkBox;
QLabel* m_label;
public:
@@ -123,12 +132,16 @@ public:
QWidget* topWidget() override;
void setModel(AutomatableModel* model) override;
BoolModel *model() override;
class AutomatableModelView* modelView() override;
BoolModel* model() override;
AutomatableModelView* modelView() override;
CheckControl(QWidget* parent = nullptr);
~CheckControl() override;
~CheckControl() override = default;
};
#endif // CONTROLS_H
} // namespace gui
} // namespace lmms
#endif // LMMS_GUI_CONTROLS_H

View File

@@ -1,17 +1,44 @@
/* Text customizable knob */
#ifndef CUSTOM_TEXT_KNOB_H
#define CUSTOM_TEXT_KNOB_H
/*
* CustomTextKnob.h
*
* Copyright (c) 2020 Ibuki Sugiyama <main/at/fuwa.dev>
*
* 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_GUI_CUSTOM_TEXT_KNOB_H
#define LMMS_GUI_CUSTOM_TEXT_KNOB_H
#include "Knob.h"
namespace lmms::gui
{
class LMMS_EXPORT CustomTextKnob : public Knob
{
protected:
inline void setHintText( const QString & _txt_before, const QString & _txt_after ) {} // inaccessible
public:
CustomTextKnob( knobTypes _knob_num, QWidget * _parent = NULL, const QString & _name = QString(), const QString & _value_text = QString() );
CustomTextKnob( KnobType _knob_num, QWidget * _parent = nullptr, const QString & _name = QString(), const QString & _value_text = QString() );
CustomTextKnob( QWidget * _parent = NULL, const QString & _name = QString(), const QString & _value_text = QString() ); //!< default ctor
CustomTextKnob( QWidget * _parent = nullptr, const QString & _name = QString(), const QString & _value_text = QString() ); //!< default ctor
CustomTextKnob( const Knob& other ) = delete;
@@ -21,10 +48,13 @@ public:
}
private:
virtual QString displayValue() const;
QString displayValue() const override;
protected:
QString m_value_text;
} ;
#endif
} // namespace lmms::gui
#endif // LMMS_GUI_CUSTOM_TEXT_KNOB_H

View File

@@ -23,28 +23,32 @@
*
*/
#ifndef LMMS_DATA_FILE_H
#define LMMS_DATA_FILE_H
#ifndef DATA_FILE_H
#define DATA_FILE_H
#include <map>
#include <QDomDocument>
#include <vector>
#include "lmms_export.h"
#include "MemoryManager.h"
#include "ProjectVersion.h"
class QTextStream;
namespace lmms
{
class ProjectVersion;
class LMMS_EXPORT DataFile : public QDomDocument
{
MM_OPERATORS
using UpgradeMethod = void(DataFile::*)();
public:
enum Types
enum class Type
{
UnknownType,
Unknown,
SongProject,
SongProjectTemplate,
InstrumentTrackSettings,
@@ -52,15 +56,14 @@ public:
ClipboardData,
JournalData,
EffectSettings,
TypeCount
MidiClip
} ;
typedef Types Type;
DataFile( const QString& fileName );
DataFile( const QByteArray& data );
DataFile( Type type );
virtual ~DataFile();
virtual ~DataFile() = default;
///
/// \brief validate
@@ -71,7 +74,9 @@ public:
QString nameWithExtension( const QString& fn ) const;
void write( QTextStream& strm );
bool writeFile( const QString& fn );
bool writeFile(const QString& fn, bool withResources = false);
bool copyResources(const QString& resourcesDir); //!< Copies resources to the resourcesDir and changes the DataFile to use local paths to them
bool hasLocalPlugins(QDomElement parent = QDomElement(), bool firstCall = true) const;
QDomElement& content()
{
@@ -96,6 +101,8 @@ private:
void cleanMetaNodes( QDomElement de );
void mapSrcAttributeInElementsWithResources(const QMap<QString, QString>& map);
// helper upgrade routines
void upgrade_0_2_1_20070501();
void upgrade_0_2_1_20070508();
@@ -115,30 +122,40 @@ private:
void upgrade_1_2_0_rc3();
void upgrade_1_3_0();
void upgrade_noHiddenClipNames();
void upgrade_automationNodes();
void upgrade_extendedNoteRange();
void upgrade_defaultTripleOscillatorHQ();
void upgrade_mixerRename();
void upgrade_bbTcoRename();
void upgrade_sampleAndHold();
void upgrade_midiCCIndexing();
void upgrade_loopsRename();
void upgrade_noteTypes();
void upgrade_fixCMTDelays();
void upgrade_fixBassLoopsTypo();
void findProblematicLadspaPlugins();
// List of all upgrade methods
static const std::vector<UpgradeMethod> UPGRADE_METHODS;
// List of ProjectVersions for the legacyFileVersion method
static const std::vector<ProjectVersion> UPGRADE_VERSIONS;
// Map with DOM elements that access resources (for making bundles)
using ResourcesMap = std::map<QString, std::vector<QString>>;
static const ResourcesMap ELEMENTS_WITH_RESOURCES;
void upgrade();
void loadData( const QByteArray & _data, const QString & _sourceFile );
struct LMMS_EXPORT typeDescStruct
{
Type m_type;
QString m_name;
} ;
static typeDescStruct s_types[TypeCount];
QString m_fileName; //!< The origin file name or "" if this DataFile didn't originate from a file
QDomElement m_content;
QDomElement m_head;
Type m_type;
unsigned int m_fileVersion;
} ;
#endif
} // namespace lmms
#endif // LMMS_DATA_FILE_H

View File

@@ -22,15 +22,16 @@
* Boston, MA 02110-1301 USA.
*
*/
#ifndef DELAY_H
#define DELAY_H
#ifndef LMMS_DELAY_H
#define LMMS_DELAY_H
#include "lmms_basics.h"
#include "lmms_math.h"
#include "interpolation.h"
#include "MemoryManager.h"
namespace lmms
{
// brief usage
@@ -63,7 +64,7 @@ template<ch_cnt_t CHANNELS>
class CombFeedback
{
public:
typedef double frame[CHANNELS];
using frame = std::array<double, CHANNELS>;
CombFeedback( int maxDelay ) :
m_size( maxDelay ),
@@ -72,20 +73,20 @@ public:
m_delay( 0 ),
m_fraction( 0.0 )
{
m_buffer = MM_ALLOC( frame, maxDelay );
m_buffer = new frame[maxDelay];
memset( m_buffer, 0, sizeof( frame ) * maxDelay );
}
virtual ~CombFeedback()
{
MM_FREE( m_buffer );
delete[] m_buffer;
}
inline void setMaxDelay( int maxDelay )
{
if( maxDelay > m_size )
{
MM_FREE( m_buffer );
m_buffer = MM_ALLOC( frame, maxDelay );
delete[] m_buffer;
m_buffer = new frame[maxDelay];
memset( m_buffer, 0, sizeof( frame ) * maxDelay );
}
m_size = maxDelay;
@@ -134,7 +135,7 @@ private:
template<ch_cnt_t CHANNELS>
class CombFeedfwd
{
typedef double frame[CHANNELS];
using frame = std::array<double, CHANNELS>;
CombFeedfwd( int maxDelay ) :
m_size( maxDelay ),
@@ -143,20 +144,20 @@ class CombFeedfwd
m_delay( 0 ),
m_fraction( 0.0 )
{
m_buffer = MM_ALLOC( frame, maxDelay );
m_buffer = new frame[maxDelay];
memset( m_buffer, 0, sizeof( frame ) * maxDelay );
}
virtual ~CombFeedfwd()
{
MM_FREE( m_buffer );
delete[] m_buffer;
}
inline void setMaxDelay( int maxDelay )
{
if( maxDelay > m_size )
{
MM_FREE( m_buffer );
m_buffer = MM_ALLOC( frame, maxDelay );
delete[] m_buffer;
m_buffer = new frame[maxDelay];
memset( m_buffer, 0, sizeof( frame ) * maxDelay );
}
m_size = maxDelay;
@@ -205,7 +206,7 @@ private:
template<ch_cnt_t CHANNELS>
class CombFeedbackDualtap
{
typedef double frame[CHANNELS];
using frame = std::array<double, CHANNELS>;
CombFeedbackDualtap( int maxDelay ) :
m_size( maxDelay ),
@@ -214,20 +215,20 @@ class CombFeedbackDualtap
m_delay( 0 ),
m_fraction( 0.0 )
{
m_buffer = MM_ALLOC( frame, maxDelay );
m_buffer = new frame[maxDelay];
memset( m_buffer, 0, sizeof( frame ) * maxDelay );
}
virtual ~CombFeedbackDualtap()
{
MM_FREE( m_buffer );
delete[] m_buffer;
}
inline void setMaxDelay( int maxDelay )
{
if( maxDelay > m_size )
{
MM_FREE( m_buffer );
m_buffer = MM_ALLOC( frame, maxDelay );
delete[] m_buffer;
m_buffer = new frame[maxDelay];
memset( m_buffer, 0, sizeof( frame ) * maxDelay );
}
m_size = maxDelay;
@@ -286,7 +287,7 @@ template<ch_cnt_t CHANNELS>
class AllpassDelay
{
public:
typedef double frame[CHANNELS];
using frame = std::array<double, CHANNELS>;
AllpassDelay( int maxDelay ) :
m_size( maxDelay ),
@@ -295,20 +296,20 @@ public:
m_delay( 0 ),
m_fraction( 0.0 )
{
m_buffer = MM_ALLOC( frame, maxDelay );
m_buffer = new frame[maxDelay];
memset( m_buffer, 0, sizeof( frame ) * maxDelay );
}
virtual ~AllpassDelay()
{
MM_FREE( m_buffer );
delete[] m_buffer;
}
inline void setMaxDelay( int maxDelay )
{
if( maxDelay > m_size )
{
MM_FREE( m_buffer );
m_buffer = MM_ALLOC( frame, maxDelay );
delete[] m_buffer;
m_buffer = new frame[maxDelay];
memset( m_buffer, 0, sizeof( frame ) * maxDelay );
}
m_size = maxDelay;
@@ -355,9 +356,11 @@ private:
};
// convenience typedefs for stereo effects
typedef CombFeedback<2> StereoCombFeedback;
typedef CombFeedfwd<2> StereoCombFeedfwd;
typedef CombFeedbackDualtap<2> StereoCombFeedbackDualtap;
typedef AllpassDelay<2> StereoAllpassDelay;
using StereoCombFeedback = CombFeedback<2>;
using StereoCombFeedfwd = CombFeedfwd<2>;
using StereoCombFeedbackDualtap = CombFeedbackDualtap<2>;
using StereoAllpassDelay = AllpassDelay<2>;
#endif
} // namespace lmms
#endif // LMMS_DELAY_H

View File

@@ -24,12 +24,15 @@
*
*/
#ifndef DEPRECATIONHELPER_H
#define DEPRECATIONHELPER_H
#ifndef LMMS_DEPRECATIONHELPER_H
#define LMMS_DEPRECATIONHELPER_H
#include <QFontMetrics>
#include <QWheelEvent>
namespace lmms
{
/**
* @brief horizontalAdvance is a backwards-compatible adapter for
* QFontMetrics::horizontalAdvance and width functions.
@@ -60,4 +63,7 @@ inline QPoint position(QWheelEvent *wheelEvent)
return wheelEvent->pos();
#endif
}
#endif // DEPRECATIONHELPER_H
} // namespace lmms
#endif // LMMS_DEPRECATIONHELPER_H

View File

@@ -23,25 +23,24 @@
*
*/
#ifndef DETUNING_HELPER_H
#define DETUNING_HELPER_H
#ifndef LMMS_DETUNING_HELPER_H
#define LMMS_DETUNING_HELPER_H
#include "InlineAutomation.h"
#include "MemoryManager.h"
namespace lmms
{
class DetuningHelper : public InlineAutomation
{
Q_OBJECT
MM_OPERATORS
public:
DetuningHelper() :
InlineAutomation()
{
}
virtual ~DetuningHelper()
{
}
~DetuningHelper() override = default;
float defaultValue() const override
{
@@ -61,4 +60,6 @@ public:
} ;
#endif
} // namespace lmms
#endif // LMMS_DETUNING_HELPER_H

View File

@@ -23,32 +23,37 @@
*
*/
#ifndef _DRUMSYNTH_H__
#define _DRUMSYNTH_H__
#ifndef LMMS_DRUM_SYNTH_H
#define LMMS_DRUM_SYNTH_H
#include <stdint.h>
#include "lmms_basics.h"
class QString;
class DrumSynth {
public:
DrumSynth() {};
int GetDSFileSamples(QString dsfile, int16_t *&wave, int channels, sample_rate_t Fs);
namespace lmms {
private:
float LoudestEnv(void);
int LongestEnv(void);
void UpdateEnv(int e, long t);
void GetEnv(int env, const char *sec, const char *key, QString ini);
class DrumSynth
{
public:
DrumSynth() = default;
int GetDSFileSamples(QString dsfile, int16_t*& wave, int channels, sample_rate_t Fs);
float waveform(float ph, int form);
private:
float LoudestEnv();
int LongestEnv();
void UpdateEnv(int e, long t);
void GetEnv(int env, const char* sec, const char* key, QString ini);
int GetPrivateProfileString(const char *sec, const char *key, const char *def, char *buffer, int size, QString file);
int GetPrivateProfileInt(const char *sec, const char *key, int def, QString file);
float GetPrivateProfileFloat(const char *sec, const char *key, float def, QString file);
float waveform(float ph, int form);
int GetPrivateProfileString(
const char* sec, const char* key, const char* def, char* buffer, int size, QString file);
int GetPrivateProfileInt(const char* sec, const char* key, int def, QString file);
float GetPrivateProfileFloat(const char* sec, const char* key, float def, QString file);
};
#endif
} // namespace lmms
#endif // LMMS_DRUM_SYNTH_H

View File

@@ -22,23 +22,22 @@
*
*/
#ifndef DSP_EFFECT_LIBRARY_H
#define DSP_EFFECT_LIBRARY_H
#ifndef LMMS_DSPEFFECTLIBRARY_H
#define LMMS_DSPEFFECTLIBRARY_H
#include "lmms_math.h"
#include "lmms_constants.h"
#include "lmms_basics.h"
#include "SampleFrame.h"
namespace DspEffectLibrary
namespace lmms::DspEffectLibrary
{
template<typename T>
class MonoBase
{
public:
typedef class MonoBypass bypassType;
using bypassType = class MonoBypass;
static void process( sample_t * * _buf, const f_cnt_t _frames )
{
@@ -53,7 +52,7 @@ namespace DspEffectLibrary
class StereoBase
{
public:
typedef class StereoBypass bypassType;
using bypassType = class StereoBypass;
static void process( sample_t * * _buf, const f_cnt_t _frames )
{
@@ -81,6 +80,17 @@ namespace DspEffectLibrary
{
}
void setGain(float gain)
{
leftFX().setGain(gain);
rightFX().setGain(gain);
}
void nextSample(SampleFrame & in)
{
nextSample(in.left(), in.right());
}
void nextSample( sample_t& inLeft, sample_t& inRight )
{
inLeft = m_leftFX.nextSample( inLeft );
@@ -164,7 +174,7 @@ namespace DspEffectLibrary
class Chain : public FX0::bypassType
{
public:
typedef typename FX0::sample_t sample_t;
using sample_t = typename FX0::sample_t;
Chain( const FX0& fx0, const FX1& fx1 = FX1() ) :
m_FX0( fx0 ),
m_FX1( fx1 )
@@ -188,7 +198,7 @@ namespace DspEffectLibrary
template<typename sample_t>
inline sample_t saturate( sample_t x )
{
return qMin<sample_t>( qMax<sample_t>( -1.0f, x ), 1.0f );
return std::min<sample_t>(std::max<sample_t>(-1.0f, x), 1.0f);
}
@@ -199,7 +209,7 @@ namespace DspEffectLibrary
const sample_t _gain,
const sample_t _ratio,
const FastBassBoost & _orig = FastBassBoost() ) :
m_frequency( qMax<sample_t>( _frequency, 10.0 ) ),
m_frequency(std::max<sample_t>(_frequency, 10.0)),
m_gain1( 1.0 / ( m_frequency + 1.0 ) ),
m_gain2( _gain ),
m_ratio( _ratio ),
@@ -329,7 +339,6 @@ namespace DspEffectLibrary
} ;
} ;
} // namespace lmms::DspEffectLibrary
#endif
#endif // LMMS_DSPEFFECTLIBRARY_H

View File

@@ -22,13 +22,23 @@
*
*/
#ifndef DUMMY_EFFECT_H
#define DUMMY_EFFECT_H
#ifndef LMMS_DUMMY_EFFECT_H
#define LMMS_DUMMY_EFFECT_H
#include <QDomElement>
#include "Effect.h"
#include "EffectControls.h"
#include "EffectControlDialog.h"
namespace lmms
{
namespace gui
{
class Knob;
class DummyEffectControlDialog : public EffectControlDialog
{
@@ -40,6 +50,7 @@ public:
} ;
} // namespace gui
class DummyEffectControls : public EffectControls
{
@@ -49,9 +60,7 @@ public:
{
}
virtual ~DummyEffectControls()
{
}
~DummyEffectControls() override = default;
int controlCount() override
{
@@ -71,9 +80,9 @@ public:
return "DummyControls";
}
EffectControlDialog * createView() override
gui::EffectControlDialog * createView() override
{
return new DummyEffectControlDialog( this );
return new gui::DummyEffectControlDialog( this );
}
} ;
@@ -84,25 +93,24 @@ class DummyEffect : public Effect
Q_OBJECT
public:
DummyEffect( Model * _parent, const QDomElement& originalPluginData ) :
Effect( NULL, _parent, NULL ),
Effect( nullptr, _parent, nullptr ),
m_controls( this ),
m_originalPluginData( originalPluginData )
{
setName();
setDontRun(true);
}
virtual ~DummyEffect()
{
}
~DummyEffect() override = default;
EffectControls * controls() override
{
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
@@ -140,4 +148,6 @@ private:
} ;
#endif
} // namespace lmms
#endif // LMMS_DUMMY_EFFECT_H

View File

@@ -23,34 +23,35 @@
*
*/
#ifndef DUMMY_INSTRUMENT_H
#define DUMMY_INSTRUMENT_H
#ifndef LMMS_DUMMY_INSTRUMENT_H
#define LMMS_DUMMY_INSTRUMENT_H
#include "Instrument.h"
#include "InstrumentView.h"
#include "Engine.h"
#include <string.h>
#include <cstring>
#include "Mixer.h"
#include "AudioEngine.h"
namespace lmms
{
class DummyInstrument : public Instrument
{
public:
DummyInstrument( InstrumentTrack * _instrument_track ) :
Instrument( _instrument_track, NULL )
Instrument( _instrument_track, nullptr )
{
}
virtual ~DummyInstrument()
{
}
~DummyInstrument() override = default;
void playNote( NotePlayHandle *, sampleFrame * buffer ) override
void playNote( NotePlayHandle*, SampleFrame* buffer ) override
{
memset( buffer, 0, sizeof( sampleFrame ) *
Engine::mixer()->framesPerPeriod() );
zeroSampleFrames(buffer, Engine::audioEngine()->framesPerPeriod());
}
void saveSettings( QDomDocument &, QDomElement & ) override
@@ -66,11 +67,13 @@ public:
return "dummyinstrument";
}
PluginView * instantiateView( QWidget * _parent ) override
gui::PluginView * instantiateView( QWidget * _parent ) override
{
return new InstrumentViewFixedSize( this, _parent );
return new gui::InstrumentViewFixedSize( this, _parent );
}
} ;
#endif
} // namespace lmms
#endif // LMMS_DUMMY_INSTRUMENT_H

View File

@@ -23,24 +23,25 @@
*
*/
#ifndef DUMMY_PLUGIN_H
#define DUMMY_PLUGIN_H
#ifndef LMMS_DUMMY_PLUGIN_H
#define LMMS_DUMMY_PLUGIN_H
#include "Plugin.h"
#include "PluginView.h"
namespace lmms
{
class DummyPlugin : public Plugin
{
public:
DummyPlugin() :
Plugin( NULL, NULL )
Plugin( nullptr, nullptr )
{
}
virtual ~DummyPlugin()
{
}
~DummyPlugin() override = default;
void saveSettings( QDomDocument &, QDomElement & ) override
{
@@ -57,12 +58,14 @@ public:
protected:
PluginView * instantiateView( QWidget * _parent ) override
gui::PluginView * instantiateView( QWidget * _parent ) override
{
return new PluginView( this, _parent );
return new gui::PluginView( this, _parent );
}
} ;
#endif
} // namespace lmms
#endif // LMMS_DUMMY_PLUGIN_H

View File

@@ -22,20 +22,23 @@
*
*/
#ifndef EDITOR_COMMON_H
#define EDITOR_COMMON_H
#ifndef LMMS_GUI_EDITOR_H
#define LMMS_GUI_EDITOR_H
#include <QMainWindow>
#include <QToolBar>
class QAction;
namespace lmms::gui
{
static const int Quantizations[] = {
1, 2, 4, 8, 16, 32, 64,
3, 6, 12, 24, 48, 96, 192
};
class QAction;
class DropToolBar;
/// \brief Superclass for editors with a toolbar.
@@ -78,7 +81,7 @@ protected:
/// \param record If set true, the editor's toolbar will contain record
/// buttons in addition to the play and stop buttons.
Editor(bool record = false, bool record_step = false);
virtual ~Editor();
~Editor() override = default;
DropToolBar* m_toolBar;
@@ -108,4 +111,6 @@ protected:
};
#endif
} // namespace lmms::gui
#endif // LMMS_GUI_EDITOR_H

View File

@@ -23,29 +23,37 @@
*
*/
#ifndef EFFECT_H
#define EFFECT_H
#ifndef LMMS_EFFECT_H
#define LMMS_EFFECT_H
#include "Plugin.h"
#include "Engine.h"
#include "Mixer.h"
#include "AudioEngine.h"
#include "AutomatableModel.h"
#include "TempoSyncKnobModel.h"
#include "MemoryManager.h"
namespace lmms
{
class EffectChain;
class EffectControls;
namespace gui
{
class EffectView;
} // namespace gui
class LMMS_EXPORT Effect : public Plugin
{
MM_OPERATORS
Q_OBJECT
public:
Effect( const Plugin::Descriptor * _desc,
Model * _parent,
const Descriptor::SubPluginFeatures::Key * _key );
virtual ~Effect();
~Effect() override;
void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override;
void loadSettings( const QDomElement & _this ) override;
@@ -55,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
{
@@ -103,8 +110,8 @@ public:
inline f_cnt_t timeout() const
{
const float samples = Engine::mixer()->processingSampleRate() * m_autoQuitModel.value() / 1000.0f;
return 1 + ( static_cast<int>( samples ) / Engine::mixer()->framesPerPeriod() );
const float samples = Engine::audioEngine()->outputSampleRate() * m_autoQuitModel.value() / 1000.0f;
return 1 + ( static_cast<int>( samples ) / Engine::audioEngine()->framesPerPeriod() );
}
inline float wetLevel() const
@@ -147,6 +154,11 @@ public:
{
m_noRun = _state;
}
inline TempoSyncKnobModel* autoQuitModel()
{
return &m_autoQuitModel;
}
EffectChain * effectChain() const
{
@@ -161,46 +173,71 @@ 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() {}
gui::PluginView* instantiateView( QWidget * ) override;
// some effects might not be capable of higher sample-rates so they can
// sample it down before processing and back after processing
inline void sampleDown( const SampleFrame* _src_buf,
SampleFrame* _dst_buf,
sample_rate_t _dst_sr )
{
resample( 0, _src_buf,
Engine::audioEngine()->outputSampleRate(),
_dst_buf, _dst_sr,
Engine::audioEngine()->framesPerPeriod() );
}
inline void sampleBack( const SampleFrame* _src_buf,
SampleFrame* _dst_buf,
sample_rate_t _src_sr )
{
resample( 1, _src_buf, _src_sr, _dst_buf,
Engine::audioEngine()->outputSampleRate(),
Engine::audioEngine()->framesPerPeriod() * _src_sr /
Engine::audioEngine()->outputSampleRate() );
}
void reinitSRC();
virtual void onEnabledChanged() {}
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 _out_sum );
PluginView * instantiateView( QWidget * ) override;
// some effects might not be capable of higher sample-rates so they can
// sample it down before processing and back after processing
inline void sampleDown( const sampleFrame * _src_buf,
sampleFrame * _dst_buf,
sample_rate_t _dst_sr )
{
resample( 0, _src_buf,
Engine::mixer()->processingSampleRate(),
_dst_buf, _dst_sr,
Engine::mixer()->framesPerPeriod() );
}
inline void sampleBack( const sampleFrame * _src_buf,
sampleFrame * _dst_buf,
sample_rate_t _src_sr )
{
resample( 1, _src_buf, _src_sr, _dst_buf,
Engine::mixer()->processingSampleRate(),
Engine::mixer()->framesPerPeriod() * _src_sr /
Engine::mixer()->processingSampleRate() );
}
void reinitSRC();
void checkGate(double outSum);
private:
EffectChain * m_parent;
void resample( int _i, const sampleFrame * _src_buf,
void resample( int _i, const SampleFrame* _src_buf,
sample_rate_t _src_sr,
sampleFrame * _dst_buf, sample_rate_t _dst_sr,
SampleFrame* _dst_buf, sample_rate_t _dst_sr,
const f_cnt_t _frames );
ch_cnt_t m_processors;
@@ -221,14 +258,14 @@ private:
SRC_STATE * m_srcState[2];
friend class EffectView;
friend class gui::EffectView;
friend class EffectChain;
} ;
using EffectKey = Effect::Descriptor::SubPluginFeatures::Key;
using EffectKeyList = Effect::Descriptor::SubPluginFeatures::KeyList;
typedef Effect::Descriptor::SubPluginFeatures::Key EffectKey;
typedef Effect::Descriptor::SubPluginFeatures::KeyList EffectKeyList;
} // namespace lmms
#endif
#endif // LMMS_EFFECT_H

View File

@@ -23,14 +23,25 @@
*
*/
#ifndef EFFECT_CHAIN_H
#define EFFECT_CHAIN_H
#ifndef LMMS_EFFECT_CHAIN_H
#define LMMS_EFFECT_CHAIN_H
#include "Model.h"
#include "SerializingObject.h"
#include "AutomatableModel.h"
namespace lmms
{
class Effect;
class SampleFrame;
namespace gui
{
class EffectRackView;
} // namespace gui
class LMMS_EXPORT EffectChain : public Model, public SerializingObject
@@ -38,7 +49,7 @@ class LMMS_EXPORT EffectChain : public Model, public SerializingObject
Q_OBJECT
public:
EffectChain( Model * _parent );
virtual ~EffectChain();
~EffectChain() override;
void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override;
void loadSettings( const QDomElement & _this ) override;
@@ -52,20 +63,20 @@ public:
void removeEffect( Effect * _effect );
void moveDown( Effect * _effect );
void moveUp( Effect * _effect );
bool processAudioBuffer( sampleFrame * _buf, const fpp_t _frames, bool hasInputNoise );
bool processAudioBuffer( SampleFrame* _buf, const fpp_t _frames, bool hasInputNoise );
void startRunning();
void clear();
private:
typedef QVector<Effect *> EffectList;
using EffectList = std::vector<Effect*>;
EffectList m_effects;
BoolModel m_enabledModel;
friend class EffectRackView;
friend class gui::EffectRackView;
signals:
@@ -73,5 +84,6 @@ signals:
} ;
#endif
} // namespace lmms
#endif // LMMS_EFFECT_CHAIN_H

View File

@@ -23,22 +23,28 @@
*
*/
#ifndef EFFECT_CONTROL_DIALOG_H
#define EFFECT_CONTROL_DIALOG_H
#ifndef LMMS_GUI_EFFECT_CONTROL_DIALOG_H
#define LMMS_GUI_EFFECT_CONTROL_DIALOG_H
#include <QWidget>
#include "ModelView.h"
namespace lmms
{
class EffectControls;
namespace gui
{
class LMMS_EXPORT EffectControlDialog : public QWidget, public ModelView
{
Q_OBJECT
public:
EffectControlDialog( EffectControls * _controls );
virtual ~EffectControlDialog();
~EffectControlDialog() override = default;
virtual bool isResizable() const {return false;}
@@ -54,4 +60,8 @@ protected:
} ;
#endif
} // namespace gui
} // namespace lmms
#endif // LMMS_GUI_EFFECT_CONTROL_DIALOG_H

View File

@@ -22,15 +22,23 @@
*
*/
#ifndef EFFECT_CONTROLS_H
#define EFFECT_CONTROLS_H
#ifndef LMMS_EFFECT_CONTROLS_H
#define LMMS_EFFECT_CONTROLS_H
#include "Model.h"
#include "JournallingObject.h"
#include "Effect.h"
namespace lmms
{
namespace gui
{
class EffectControlDialog;
} // namespace gui
class EffectControls : public JournallingObject, public Model
{
@@ -43,12 +51,10 @@ public:
{
}
virtual ~EffectControls()
{
}
~EffectControls() override = default;
virtual int controlCount() = 0;
virtual EffectControlDialog * createView() = 0;
virtual gui::EffectControlDialog * createView() = 0;
void setViewVisible( bool _visible )
@@ -73,4 +79,7 @@ private:
} ;
#endif
} // namespace lmms
#endif // LMMS_EFFECT_CONTROLS_H

View File

@@ -23,8 +23,8 @@
*
*/
#ifndef EFFECT_RACK_VIEW_H
#define EFFECT_RACK_VIEW_H
#ifndef LMMS_GUI_EFFECT_RACK_VIEW_H
#define LMMS_GUI_EFFECT_RACK_VIEW_H
#include <QWidget>
@@ -35,6 +35,9 @@
class QScrollArea;
class QVBoxLayout;
namespace lmms::gui
{
class EffectView;
class GroupBox;
@@ -43,17 +46,16 @@ class EffectRackView : public QWidget, public ModelView
{
Q_OBJECT
public:
EffectRackView( EffectChain* model, QWidget* parent = NULL );
virtual ~EffectRackView();
EffectRackView( EffectChain* model, QWidget* parent = nullptr );
~EffectRackView() override;
static constexpr int DEFAULT_WIDTH = 245;
public slots:
void clearViews();
void moveUp( EffectView* view );
void moveDown( EffectView* view );
void deletePlugin( EffectView* view );
void moveUp(EffectView* view);
void moveDown(EffectView* view);
void deletePlugin(EffectView* view);
private slots:
virtual void update();
@@ -62,6 +64,8 @@ private slots:
private:
void modelChanged() override;
QSize sizeHint() const override;
QSize minimumSizeHint() const override { return sizeHint(); }
inline EffectChain* fxChain()
{
@@ -83,4 +87,6 @@ private:
} ;
#endif
} // namespace lmms::gui
#endif // LMMS_GUI_EFFECT_RACK_VIEW_H

View File

@@ -2,6 +2,7 @@
* EffectSelectDialog.h - dialog to choose effect plugin
*
* Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2023 Lost Robot <r94231/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
@@ -22,49 +23,98 @@
*
*/
#ifndef EFFECT_SELECT_DIALOG_H
#define EFFECT_SELECT_DIALOG_H
#ifndef LMMS_GUI_EFFECT_SELECT_DIALOG_H
#define LMMS_GUI_EFFECT_SELECT_DIALOG_H
#include <QDialog>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include "EffectChain.h"
#include "Effect.h"
#include <QDialog>
#include <QHeaderView>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QPushButton>
#include <QRegularExpression>
#include <QScrollArea>
#include <QSortFilterProxyModel>
#include <QStandardItemModel>
#include <QTableView>
namespace Ui { class EffectSelectDialog; }
namespace lmms::gui
{
class DualColumnFilterProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
DualColumnFilterProxyModel(QObject* parent = nullptr) : QSortFilterProxyModel(parent)
{
}
void setEffectTypeFilter(const QString& filter)
{
m_effectTypeFilter = filter;
invalidateFilter();
}
protected:
bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override
{
QModelIndex nameIndex = sourceModel()->index(source_row, 0, source_parent);
QModelIndex typeIndex = sourceModel()->index(source_row, 1, source_parent);
QString name = sourceModel()->data(nameIndex, Qt::DisplayRole).toString();
QString type = sourceModel()->data(typeIndex, Qt::DisplayRole).toString();
// TODO: cleanup once we drop Qt5 support
#if (QT_VERSION >= QT_VERSION_CHECK(5,12,0))
QRegularExpression nameRegularExpression(filterRegularExpression());
nameRegularExpression.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
bool nameFilterPassed = nameRegularExpression.match(name).capturedStart() != -1;
#else
QRegExp nameRegularExpression(filterRegExp());
nameRegularExpression.setCaseSensitivity(Qt::CaseInsensitive);
bool nameFilterPassed = nameRegularExpression.indexIn(name) != -1;
#endif
bool typeFilterPassed = type.contains(m_effectTypeFilter, Qt::CaseInsensitive);
return nameFilterPassed && typeFilterPassed;
}
private:
QString m_effectTypeFilter;
};
class EffectSelectDialog : public QDialog
{
Q_OBJECT
public:
EffectSelectDialog( QWidget * _parent );
virtual ~EffectSelectDialog();
Effect * instantiateSelectedPlugin( EffectChain * _parent );
EffectSelectDialog(QWidget* parent);
Effect* instantiateSelectedPlugin(EffectChain* parent);
protected slots:
void acceptSelection();
void rowChanged( const QModelIndex &, const QModelIndex & );
void sortAgain();
void rowChanged(const QModelIndex&, const QModelIndex&);
void updateSelection();
bool eventFilter(QObject* obj, QEvent* event) override;
private:
Ui::EffectSelectDialog * ui;
EffectKeyList m_effectKeys;
EffectKey m_currentSelection;
QStandardItemModel m_sourceModel;
QSortFilterProxyModel m_model;
QWidget * m_descriptionWidget;
} ;
DualColumnFilterProxyModel m_model;
QWidget* m_descriptionWidget;
QTableView* m_pluginList;
QScrollArea* m_scrollArea;
QLineEdit* m_filterEdit;
};
} // namespace lmms::gui
#endif

View File

@@ -23,18 +23,22 @@
*
*/
#ifndef EFFECT_VIEW_H
#define EFFECT_VIEW_H
#ifndef LMMS_GUI_EFFECT_VIEW_H
#define LMMS_GUI_EFFECT_VIEW_H
#include "AutomatableModel.h"
#include "PluginView.h"
#include "Effect.h"
class QGraphicsOpacityEffect;
class QGroupBox;
class QLabel;
class QPushButton;
class QMdiSubWindow;
namespace lmms::gui
{
class EffectControlDialog;
class Knob;
class LedCheckBox;
@@ -46,7 +50,7 @@ class EffectView : public PluginView
Q_OBJECT
public:
EffectView( Effect * _model, QWidget * _parent );
virtual ~EffectView();
~EffectView() override;
inline Effect * effect()
{
@@ -58,6 +62,11 @@ public:
}
static constexpr int DEFAULT_WIDTH = 215;
static constexpr int DEFAULT_HEIGHT = 60;
void mouseMoveEvent(QMouseEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
public slots:
void editControls();
@@ -68,10 +77,9 @@ public slots:
signals:
void moveUp( EffectView * _plugin );
void moveDown( EffectView * _plugin );
void deletePlugin( EffectView * _plugin );
void movedUp(EffectView* view);
void movedDown(EffectView* view);
void deletedPlugin(EffectView* view);
protected:
void contextMenuEvent( QContextMenuEvent * _me ) override;
@@ -87,7 +95,13 @@ private:
Knob * m_gate;
QMdiSubWindow * m_subWindow;
EffectControlDialog * m_controlView;
bool m_dragging;
QGraphicsOpacityEffect* m_opacityEffect;
} ;
#endif
} // namespace lmms::gui
#endif // LMMS_GUI_EFFECT_VIEW_H

View File

@@ -22,39 +22,33 @@
*
*/
#ifndef LMMS_ENGINE_H
#define LMMS_ENGINE_H
#ifndef ENGINE_H
#define ENGINE_H
#include <QtCore/QString>
#include <QtCore/QObject>
#include <QString>
#include <QObject>
#include "lmmsconfig.h"
#include "lmms_export.h"
#include "lmms_basics.h"
class BBTrackContainer;
class FxMixer;
class ProjectJournal;
namespace lmms
{
class AudioEngine;
class Mixer;
class PatternStore;
class ProjectJournal;
class Song;
class Ladspa2LMMS;
namespace gui
{
class GuiApplication;
}
// Note: This class is called 'LmmsCore' instead of 'Engine' because of naming
// conflicts caused by ZynAddSubFX. See https://github.com/LMMS/lmms/issues/2269
// and https://github.com/LMMS/lmms/pull/2118 for more details.
//
// The workaround was to rename Lmms' Engine so that it has a different symbol
// name in the object files, but typedef it back to 'Engine' and keep it inside
// of Engine.h so that the rest of the codebase can be oblivious to this issue
// (and it could be fixed without changing every single file).
class LmmsCore;
typedef LmmsCore Engine;
class LMMS_EXPORT LmmsCore : public QObject
class LMMS_EXPORT Engine : public QObject
{
Q_OBJECT
public:
@@ -62,14 +56,14 @@ public:
static void destroy();
// core
static Mixer *mixer()
static AudioEngine *audioEngine()
{
return s_mixer;
return s_audioEngine;
}
static FxMixer * fxMixer()
static Mixer * mixer()
{
return s_fxMixer;
return s_mixer;
}
static Song * getSong()
@@ -77,9 +71,9 @@ public:
return s_song;
}
static BBTrackContainer * getBBTrackContainer()
static PatternStore * patternStore()
{
return s_bbTrackContainer;
return s_patternStore;
}
static ProjectJournal * projectJournal()
@@ -87,8 +81,6 @@ public:
return s_projectJournal;
}
static bool ignorePluginBlacklist();
#ifdef LMMS_HAVE_LV2
static class Lv2Manager * getLv2Manager()
{
@@ -110,11 +102,11 @@ public:
static void updateFramesPerTick();
static inline LmmsCore * inst()
static inline Engine * inst()
{
if( s_instanceOfMe == NULL )
if( s_instanceOfMe == nullptr )
{
s_instanceOfMe = new LmmsCore();
s_instanceOfMe = new Engine();
}
return s_instanceOfMe;
}
@@ -130,34 +122,35 @@ private:
// small helper function which sets the pointer to NULL before actually deleting
// the object it refers to
template<class T>
static inline void deleteHelper( T * * ptr )
static inline void deleteHelper(T** ptr)
{
T * tmp = *ptr;
*ptr = NULL;
T* tmp = *ptr;
*ptr = nullptr;
delete tmp;
}
static float s_framesPerTick;
// core
static Mixer *s_mixer;
static FxMixer * s_fxMixer;
static AudioEngine *s_audioEngine;
static Mixer * s_mixer;
static Song * s_song;
static BBTrackContainer * s_bbTrackContainer;
static PatternStore * s_patternStore;
static ProjectJournal * s_projectJournal;
#ifdef LMMS_HAVE_LV2
static class Lv2Manager* s_lv2Manager;
#endif
static Ladspa2LMMS * s_ladspaManager;
static Ladspa2LMMS* s_ladspaManager;
static void* s_dndPluginKey;
// even though most methods are static, an instance is needed for Qt slots/signals
static LmmsCore * s_instanceOfMe;
static Engine* s_instanceOfMe;
friend class GuiApplication;
friend class gui::GuiApplication;
};
#endif
} // namespace lmms
#endif // LMMS_ENGINE_H

View File

@@ -22,10 +22,11 @@
*
*/
#ifndef ENVELOPE_AND_LFO_PARAMETERS_H
#define ENVELOPE_AND_LFO_PARAMETERS_H
#ifndef LMMS_ENVELOPE_AND_LFO_PARAMETERS_H
#define LMMS_ENVELOPE_AND_LFO_PARAMETERS_H
#include <QtCore/QVector>
#include <memory>
#include <vector>
#include "JournallingObject.h"
#include "AutomatableModel.h"
@@ -33,6 +34,15 @@
#include "TempoSyncKnobModel.h"
#include "lmms_basics.h"
namespace lmms
{
namespace gui
{
class EnvelopeAndLfoView;
}
class LMMS_EXPORT EnvelopeAndLfoParameters : public Model, public JournallingObject
{
@@ -41,13 +51,9 @@ public:
class LfoInstances
{
public:
LfoInstances()
{
}
LfoInstances() = default;
~LfoInstances()
{
}
~LfoInstances() = default;
inline bool isEmpty() const
{
@@ -62,14 +68,25 @@ public:
private:
QMutex m_lfoListMutex;
typedef QList<EnvelopeAndLfoParameters *> LfoList;
using LfoList = QList<EnvelopeAndLfoParameters*>;
LfoList m_lfos;
} ;
};
enum class LfoShape
{
SineWave,
TriangleWave,
SawWave,
SquareWave,
UserDefinedWave,
RandomWave,
Count
};
EnvelopeAndLfoParameters( float _value_for_zero_amount,
Model * _parent );
virtual ~EnvelopeAndLfoParameters();
~EnvelopeAndLfoParameters() override;
static inline float expKnobVal( float _val )
{
@@ -108,6 +125,28 @@ public:
return m_rFrames;
}
// Envelope
const FloatModel& getPredelayModel() const { return m_predelayModel; }
const FloatModel& getAttackModel() const { return m_attackModel; }
const FloatModel& getHoldModel() const { return m_holdModel; }
const FloatModel& getDecayModel() const { return m_decayModel; }
const FloatModel& getSustainModel() const { return m_sustainModel; }
const FloatModel& getReleaseModel() const { return m_releaseModel; }
const FloatModel& getAmountModel() const { return m_amountModel; }
FloatModel& getAmountModel() { return m_amountModel; }
// LFO
inline f_cnt_t getLfoPredelayFrames() const { return m_lfoPredelayFrames; }
inline f_cnt_t getLfoAttackFrames() const { return m_lfoAttackFrames; }
inline f_cnt_t getLfoOscillationFrames() const { return m_lfoOscillationFrames; }
const FloatModel& getLfoAmountModel() const { return m_lfoAmountModel; }
FloatModel& getLfoAmountModel() { return m_lfoAmountModel; }
const TempoSyncKnobModel& getLfoSpeedModel() const { return m_lfoSpeedModel; }
const BoolModel& getX100Model() const { return m_x100Model; }
const IntModel& getLfoWaveModel() const { return m_lfoWaveModel; }
std::shared_ptr<const SampleBuffer> getLfoUserWave() const { return m_userWave; }
public slots:
void updateSampleVars();
@@ -162,25 +201,18 @@ private:
sample_t * m_lfoShapeData;
sample_t m_random;
bool m_bad_lfoShapeData;
SampleBuffer m_userWave;
std::shared_ptr<const SampleBuffer> m_userWave = SampleBuffer::emptyBuffer();
enum LfoShapes
{
SineWave,
TriangleWave,
SawWave,
SquareWave,
UserDefinedWave,
RandomWave,
NumLfoShapes
} ;
constexpr static auto NumLfoShapes = static_cast<std::size_t>(LfoShape::Count);
sample_t lfoShapeSample( fpp_t _frame_offset );
void updateLfoShapeData();
friend class EnvelopeAndLfoView;
friend class gui::EnvelopeAndLfoView;
} ;
#endif
} // namespace lmms
#endif // LMMS_ENVELOPE_AND_LFO_PARAMETERS_H

View File

@@ -23,23 +23,28 @@
*
*/
#ifndef ENVELOPE_AND_LFO_VIEW_H
#define ENVELOPE_AND_LFO_VIEW_H
#ifndef LMMS_GUI_ENVELOPE_AND_LFO_VIEW_H
#define LMMS_GUI_ENVELOPE_AND_LFO_VIEW_H
#include <QWidget>
#include "ModelView.h"
class QPaintEvent;
class QPixmap;
namespace lmms
{
class EnvelopeAndLfoParameters;
namespace gui
{
class automatableButtonGroup;
class Knob;
class LedCheckBox;
class PixmapButton;
class TempoSyncKnob;
class EnvelopeGraph;
class LfoGraph;
@@ -48,7 +53,7 @@ class EnvelopeAndLfoView : public QWidget, public ModelView
Q_OBJECT
public:
EnvelopeAndLfoView( QWidget * _parent );
virtual ~EnvelopeAndLfoView();
~EnvelopeAndLfoView() override;
protected:
@@ -56,8 +61,6 @@ protected:
void dragEnterEvent( QDragEnterEvent * _dee ) override;
void dropEvent( QDropEvent * _de ) override;
void mousePressEvent( QMouseEvent * _me ) override;
void paintEvent( QPaintEvent * _pe ) override;
protected slots:
@@ -65,13 +68,10 @@ protected slots:
private:
static QPixmap * s_envGraph;
static QPixmap * s_lfoGraph;
EnvelopeAndLfoParameters * m_params;
// envelope stuff
EnvelopeGraph* m_envelopeGraph;
Knob * m_predelayKnob;
Knob * m_attackKnob;
Knob * m_holdKnob;
@@ -81,6 +81,7 @@ private:
Knob * m_amountKnob;
// LFO stuff
LfoGraph* m_lfoGraph;
Knob * m_lfoPredelayKnob;
Knob * m_lfoAttackKnob;
TempoSyncKnob * m_lfoSpeedKnob;
@@ -90,8 +91,10 @@ private:
LedCheckBox * m_x100Cb;
LedCheckBox * m_controlEnvAmountCb;
float m_randomGraph;
} ;
#endif
} // namespace gui
} // namespace lmms
#endif // LMMS_GUI_ENVELOPE_AND_LFO_VIEW_H

77
include/EnvelopeGraph.h Normal file
View File

@@ -0,0 +1,77 @@
/*
* EnvelopeGraph.h - Displays envelope graphs
*
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2024- Michael Gregorius
*
* 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_GUI_ENVELOPE_GRAPH_H
#define LMMS_GUI_ENVELOPE_GRAPH_H
#include <QWidget>
#include "ModelView.h"
#include "embed.h"
namespace lmms
{
class EnvelopeAndLfoParameters;
namespace gui
{
class EnvelopeGraph : public QWidget, public ModelView
{
public:
enum class ScalingMode
{
Dynamic,
Absolute,
Relative
};
public:
EnvelopeGraph(QWidget* parent);
protected:
void modelChanged() override;
void mousePressEvent(QMouseEvent*) override;
void contextMenuEvent(QContextMenuEvent*) override;
void paintEvent(QPaintEvent*) override;
private:
void toggleAmountModel();
private:
QPixmap m_envGraph = embed::getIconPixmap("envelope_graph");
EnvelopeAndLfoParameters* m_params = nullptr;
ScalingMode m_scaling = ScalingMode::Dynamic;
};
} // namespace gui
} // namespace lmms
#endif // LMMS_GUI_ENVELOPE_GRAPH_H

View File

@@ -23,24 +23,28 @@
*
*/
#ifndef EXPORT_FILTER_H
#define EXPORT_FILTER_H
#ifndef LMMS_EXPORT_FILTER_H
#define LMMS_EXPORT_FILTER_H
#include <QtCore/QFile>
#include <QFile>
#include "TrackContainer.h"
#include "Plugin.h"
namespace lmms
{
class LMMS_EXPORT ExportFilter : public Plugin
{
public:
ExportFilter( const Descriptor * _descriptor ) : Plugin( _descriptor, NULL ) {}
virtual ~ExportFilter() {}
ExportFilter( const Descriptor * _descriptor ) : Plugin( _descriptor, nullptr ) {}
~ExportFilter() override = default;
virtual bool tryExport(const TrackContainer::TrackList &tracks,
const TrackContainer::TrackList &tracksBB,
const TrackContainer::TrackList &patternTracks,
int tempo, int masterPitch, const QString &filename ) = 0;
protected:
@@ -63,4 +67,6 @@ private:
} ;
#endif
} // namespace lmms
#endif // LMMS_EXPORT_FILTER_H

View File

@@ -23,9 +23,8 @@
*
*/
#ifndef EXPORT_PROJECT_DIALOG_H
#define EXPORT_PROJECT_DIALOG_H
#ifndef LMMS_GUI_EXPORT_PROJECT_DIALOG_H
#define LMMS_GUI_EXPORT_PROJECT_DIALOG_H
#include <QDialog>
#include <memory>
@@ -34,6 +33,10 @@
#include "ProjectRenderer.h"
#include "RenderManager.h"
namespace lmms::gui
{
class ExportProjectDialog : public QDialog, public Ui::ExportProjectDialog
{
Q_OBJECT
@@ -41,12 +44,12 @@ public:
ExportProjectDialog( const QString & _file_name, QWidget * _parent, bool multi_export );
protected:
void reject( void ) override;
void reject() override;
void closeEvent( QCloseEvent * _ce ) override;
private slots:
void startBtnClicked( void );
void startBtnClicked();
void updateTitleBar( int );
void accept() override;
void startExport();
@@ -59,8 +62,11 @@ private:
QString m_fileExtension;
bool m_multiExport;
ProjectRenderer::ExportFileFormats m_ft;
ProjectRenderer::ExportFileFormat m_ft;
std::unique_ptr<RenderManager> m_renderManager;
} ;
#endif
} // namespace lmms::gui
#endif // LMMS_GUI_EXPORT_PROJECT_DIALOG_H

View File

@@ -22,15 +22,18 @@
*
*/
#ifndef FADE_BUTTON_H
#define FADE_BUTTON_H
#ifndef LMMS_GUI_FADE_BUTTON_H
#define LMMS_GUI_FADE_BUTTON_H
#include <QAbstractButton>
#include <QColor>
#include <QElapsedTimer>
namespace lmms::gui
{
class FadeButton : public QAbstractButton
{
Q_OBJECT
@@ -40,7 +43,7 @@ public:
const QColor & _hold_color,
QWidget * _parent );
virtual ~FadeButton();
~FadeButton() override = default;
void setActiveColor( const QColor & activated_color );
@@ -71,4 +74,6 @@ private:
} ;
#endif
} // namespace lmms::gui
#endif // LMMS_GUI_FADE_BUTTON_H

View File

@@ -1,5 +1,5 @@
/*
* Fader.h - fader-widget used in FX-mixer - partly taken from Hydrogen
* Fader.h - fader-widget used in Mixer - partly taken from Hydrogen
*
* Copyright (c) 2008-2012 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
@@ -44,9 +44,8 @@
*
*/
#ifndef FADER_H
#define FADER_H
#ifndef LMMS_GUI_FADER_H
#define LMMS_GUI_FADER_H
#include <QElapsedTimer>
#include <QPixmap>
@@ -54,29 +53,35 @@
#include "AutomatableModelView.h"
#include "embed.h"
#include "lmms_math.h"
class TextFloat;
namespace lmms::gui
{
class SimpleTextFloat;
class LMMS_EXPORT Fader : public QWidget, public FloatModelView
{
Q_OBJECT
public:
Q_PROPERTY( QColor peakGreen READ peakGreen WRITE setPeakGreen )
Q_PROPERTY( QColor peakRed READ peakRed WRITE setPeakRed )
Q_PROPERTY( QColor peakYellow READ peakYellow WRITE setPeakYellow )
Q_PROPERTY( bool levelsDisplayedInDBFS READ getLevelsDisplayedInDBFS WRITE setLevelsDisplayedInDBFS )
Q_PROPERTY(QColor peakOk MEMBER m_peakOk)
Q_PROPERTY(QColor peakClip MEMBER m_peakClip)
Q_PROPERTY(QColor peakWarn MEMBER m_peakWarn)
Q_PROPERTY(bool levelsDisplayedInDBFS MEMBER m_levelsDisplayedInDBFS)
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, QPixmap * back, QPixmap * leds, QPixmap * knob );
virtual ~Fader() = default;
Fader(FloatModel* model, const QString& name, QWidget* parent);
Fader(FloatModel* model, const QString& name, QWidget* parent, const QPixmap& knob);
~Fader() override = default;
void init(FloatModel * model, QString const & name);
void setPeak_L( float fPeak );
void setPeak_L(float fPeak);
float getPeak_L() { return m_fPeakValue_L; }
void setPeak_R( float fPeak );
void setPeak_R(float fPeak);
float getPeak_R() { return m_fPeakValue_R; }
inline float getMinPeak() const { return m_fMinPeak; }
@@ -85,88 +90,77 @@ public:
inline float getMaxPeak() const { return m_fMaxPeak; }
inline void setMaxPeak(float maxPeak) { m_fMaxPeak = maxPeak; }
QColor const & peakGreen() const;
void setPeakGreen( const QColor & c );
inline bool getRenderUnityLine() const { return m_renderUnityLine; }
inline void setRenderUnityLine(bool value = true) { m_renderUnityLine = value; }
QColor const & peakRed() const;
void setPeakRed( const QColor & c );
QColor const & peakYellow() const;
void setPeakYellow( const QColor & c );
inline bool getLevelsDisplayedInDBFS() const { return m_levelsDisplayedInDBFS; }
inline void setLevelsDisplayedInDBFS(bool value = true) { m_levelsDisplayedInDBFS = value; }
void setDisplayConversion( bool b )
void setDisplayConversion(bool b)
{
m_conversionFactor = b ? 100.0 : 1.0;
}
inline void setHintText( const QString & _txt_before,
const QString & _txt_after )
inline void setHintText(const QString& txt_before,
const QString& txt_after)
{
setDescription( _txt_before );
setUnit( _txt_after );
setDescription(txt_before);
setUnit(txt_after);
}
signals:
void peakChanged(float peak);
private:
void contextMenuEvent( QContextMenuEvent * _me ) override;
void mousePressEvent( QMouseEvent *ev ) override;
void mouseDoubleClickEvent( QMouseEvent* mouseEvent ) override;
void mouseMoveEvent( QMouseEvent *ev ) override;
void mouseReleaseEvent( QMouseEvent * _me ) override;
void wheelEvent( QWheelEvent *ev ) override;
void paintEvent( QPaintEvent *ev ) override;
void contextMenuEvent(QContextMenuEvent* me) override;
void mousePressEvent(QMouseEvent* ev) override;
void mouseDoubleClickEvent(QMouseEvent* mouseEvent) override;
void mouseMoveEvent(QMouseEvent* ev) override;
void mouseReleaseEvent(QMouseEvent* me) override;
void wheelEvent(QWheelEvent* ev) override;
void paintEvent(QPaintEvent* ev) override;
inline bool clips(float const & value) const { return value >= 1.0f; }
void paintDBFSLevels(QPaintEvent *ev, QPainter & painter);
void paintLinearLevels(QPaintEvent *ev, QPainter & painter);
void paintLevels(QPaintEvent* ev, QPainter& painter, bool linear = false);
int knobPosY() const
{
float fRange = model()->maxValue() - model()->minValue();
float realVal = model()->value() - model()->minValue();
return height() - ( ( height() - m_knob->height() ) * ( realVal / fRange ) );
return height() - ((height() - m_knob.height()) * (realVal / fRange));
}
void setPeak( float fPeak, float &targetPeak, float &persistentPeak, QElapsedTimer &lastPeakTimer );
int calculateDisplayPeak( float fPeak );
void setPeak(float fPeak, float& targetPeak, float& persistentPeak, QElapsedTimer& lastPeakTimer);
void updateTextFloat();
// Private members
private:
float m_fPeakValue_L;
float m_fPeakValue_R;
float m_persistentPeak_L;
float m_persistentPeak_R;
float m_fMinPeak;
float m_fMaxPeak;
float m_fPeakValue_L {0.};
float m_fPeakValue_R {0.};
float m_persistentPeak_L {0.};
float m_persistentPeak_R {0.};
float m_fMinPeak {dbfsToAmp(-42)};
float m_fMaxPeak {dbfsToAmp(9)};
QElapsedTimer m_lastPeakTimer_L;
QElapsedTimer m_lastPeakTimer_R;
static QPixmap * s_back;
static QPixmap * s_leds;
static QPixmap * s_knob;
QPixmap * m_back;
QPixmap * m_leds;
QPixmap * m_knob;
QPixmap m_knob {embed::getIconPixmap("fader_knob")};
bool m_levelsDisplayedInDBFS;
bool m_levelsDisplayedInDBFS {true};
int m_moveStartPoint;
float m_startValue;
int m_moveStartPoint {-1};
float m_startValue {0.};
static TextFloat * s_textFloat;
static SimpleTextFloat* s_textFloat;
QColor m_peakGreen;
QColor m_peakRed;
QColor m_peakYellow;
QColor m_peakOk {10, 212, 92};
QColor m_peakClip {193, 32, 56};
QColor m_peakWarn {214, 236, 82};
QColor m_unityMarker {63, 63, 63, 255};
bool m_renderUnityLine {true};
} ;
#endif
} // namespace lmms::gui
#endif // LMMS_GUI_FADER_H

View File

@@ -1,5 +1,5 @@
/*
* fifo_buffer.h - FIFO fixed-size buffer
* FifoBuffer.h - FIFO fixed-size buffer
*
* Copyright (c) 2007 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
*
@@ -22,73 +22,76 @@
*
*/
#ifndef FIFO_BUFFER_H
#define FIFO_BUFFER_H
#ifndef LMMS_FIFO_BUFFER_H
#define LMMS_FIFO_BUFFER_H
#include <QtCore/QSemaphore>
#include <QSemaphore>
namespace lmms
{
template<typename T>
class fifoBuffer
class FifoBuffer
{
public:
fifoBuffer( int _size ) :
m_reader_sem( _size ),
m_writer_sem( _size ),
m_reader_index( 0 ),
m_writer_index( 0 ),
m_size( _size )
FifoBuffer(int size) :
m_readSem(size),
m_writeSem(size),
m_readIndex(0),
m_writeIndex(0),
m_size(size)
{
m_buffer = new T[_size];
m_reader_sem.acquire( _size );
m_buffer = new T[size];
m_readSem.acquire(size);
}
~fifoBuffer()
~FifoBuffer()
{
delete[] m_buffer;
m_reader_sem.release( m_size );
m_readSem.release(m_size);
}
void write( T _element )
void write(T element)
{
m_writer_sem.acquire();
m_buffer[m_writer_index++] = _element;
m_writer_index %= m_size;
m_reader_sem.release();
m_writeSem.acquire();
m_buffer[m_writeIndex++] = element;
m_writeIndex %= m_size;
m_readSem.release();
}
T read()
{
m_reader_sem.acquire();
T element = m_buffer[m_reader_index++];
m_reader_index %= m_size;
m_writer_sem.release();
return( element );
m_readSem.acquire();
T element = m_buffer[m_readIndex++];
m_readIndex %= m_size;
m_writeSem.release();
return element;
}
void waitUntilRead()
{
m_writer_sem.acquire( m_size );
m_writer_sem.release( m_size );
m_writeSem.acquire(m_size);
m_writeSem.release(m_size);
}
bool available()
{
return( m_reader_sem.available() );
return m_readSem.available();
}
private:
QSemaphore m_reader_sem;
QSemaphore m_writer_sem;
int m_reader_index;
int m_writer_index;
QSemaphore m_readSem;
QSemaphore m_writeSem;
int m_readIndex;
int m_writeIndex;
int m_size;
T * m_buffer;
} ;
} // namespace lmms
#endif
#endif // LMMS_FIFO_BUFFER_H

View File

@@ -22,28 +22,40 @@
*
*/
#ifndef FILE_BROWSER_H
#define FILE_BROWSER_H
#ifndef LMMS_GUI_FILE_BROWSER_H
#define LMMS_GUI_FILE_BROWSER_H
#include <QCheckBox>
#include <QtCore/QDir>
#include <QtCore/QMutex>
#include <QDir>
#include <QMutex>
#include <QProgressBar>
#include <memory>
#include "FileSearch.h"
#include "embed.h"
#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0))
#include <QRecursiveMutex>
#endif
#include <QTreeWidget>
#include "SideBarWidget.h"
#include "lmmsconfig.h"
class QLineEdit;
class FileItem;
namespace lmms
{
class InstrumentTrack;
class FileBrowserTreeWidget;
class PlayHandle;
class TrackContainer;
namespace gui
{
class FileItem;
class FileBrowserTreeWidget;
class FileBrowser : public SideBarWidget
{
@@ -59,17 +71,31 @@ public:
*/
FileBrowser( const QString & directories, const QString & filter,
const QString & title, const QPixmap & pm,
QWidget * parent, bool dirs_as_items = false, bool recurse = false,
QWidget * parent, bool dirs_as_items = false,
const QString& userDir = "",
const QString& factoryDir = "");
virtual ~FileBrowser() = default;
~FileBrowser() override = default;
static QStringList excludedPaths()
{
static auto s_excludedPaths = QStringList{
#ifdef LMMS_BUILD_LINUX
"/bin", "/boot", "/dev", "/etc", "/proc", "/run", "/sbin",
"/sys"
#endif
#ifdef LMMS_BUILD_WIN32
"C:\\Windows"
#endif
};
return s_excludedPaths;
}
static QDir::Filters dirFilters() { return QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden; }
static QDir::SortFlags sortFlags() { return QDir::LocaleAware | QDir::DirsFirst | QDir::Name | QDir::IgnoreCase; }
private slots:
void reloadTree( void );
void expandItems( QTreeWidgetItem * item=nullptr, QList<QString> expandedDirs = QList<QString>() );
// call with item=NULL to filter the entire tree
bool filterItems( const QString & filter, QTreeWidgetItem * item=nullptr );
void reloadTree();
void expandItems(const QList<QString>& expandedDirs, QTreeWidgetItem* item = nullptr);
void giveFocusToFilter();
private:
@@ -77,21 +103,40 @@ private:
void addItems( const QString & path );
void saveDirectoriesStates();
void restoreDirectoriesStates();
void foundSearchMatch(FileSearch* search, const QString& match);
void searchCompleted(FileSearch* search);
void onSearch(const QString& filter);
void displaySearch(bool on);
void addContentCheckBox();
FileBrowserTreeWidget * m_fileBrowserTreeWidget;
FileBrowserTreeWidget * m_searchTreeWidget;
QLineEdit * m_filterEdit;
std::shared_ptr<FileSearch> m_currentSearch;
QProgressBar* m_searchIndicator = nullptr;
QString m_directories; //!< Directories to search, split with '*'
QString m_filter; //!< Filter as used in QDir::match()
bool m_dirsAsItems;
bool m_recurse;
void addContentCheckBox();
QCheckBox* m_showUserContent = nullptr;
QCheckBox* m_showFactoryContent = nullptr;
QCheckBox* m_showHiddenContent = nullptr;
QBoxLayout *filterWidgetLayout = nullptr;
QBoxLayout *hiddenWidgetLayout = nullptr;
QBoxLayout *outerLayout = nullptr;
QString m_userDir;
QString m_factoryDir;
QList<QString> m_savedExpandedDirs;
QString m_previousFilterValue;
} ;
@@ -102,13 +147,12 @@ class FileBrowserTreeWidget : public QTreeWidget
Q_OBJECT
public:
FileBrowserTreeWidget( QWidget * parent );
virtual ~FileBrowserTreeWidget() = default;
~FileBrowserTreeWidget() override = default;
//! This method returns a QList with paths (QString's) of all directories
//! that are expanded in the tree.
QList<QString> expandedDirs( QTreeWidgetItem * item = nullptr ) const;
protected:
void contextMenuEvent( QContextMenuEvent * e ) override;
void mousePressEvent( QMouseEvent * me ) override;
@@ -135,31 +179,34 @@ private:
//! This should only be accessed or modified when m_pphMutex is held
PlayHandle* m_previewPlayHandle;
#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0))
QRecursiveMutex m_pphMutex;
#else
QMutex m_pphMutex;
#endif
QList<QAction*> getContextActions(FileItem* item, bool songEditor);
private slots:
void activateListItem( QTreeWidgetItem * item, int column );
void openInNewInstrumentTrack( FileItem* item, bool songEditor );
bool openInNewSampleTrack( FileItem* item );
void sendToActiveInstrumentTrack( FileItem* item );
void openInNewInstrumentTrack( lmms::gui::FileItem* item, bool songEditor );
bool openInNewSampleTrack( lmms::gui::FileItem* item );
void sendToActiveInstrumentTrack( lmms::gui::FileItem* item );
void updateDirectory( QTreeWidgetItem * item );
void openContainingFolder( FileItem* item );
void openContainingFolder( lmms::gui::FileItem* item );
} ;
class Directory : public QTreeWidgetItem
{
public:
Directory( const QString & filename, const QString & path,
const QString & filter );
Directory(const QString& filename, const QString& path, const QString& filter, bool disableEntryPopulation = false);
void update( void );
void update();
inline QString fullName( QString path = QString() )
{
@@ -182,14 +229,12 @@ public:
private:
void initPixmaps( void );
bool addItems( const QString & path );
static QPixmap * s_folderPixmap;
static QPixmap * s_folderOpenedPixmap;
static QPixmap * s_folderLockedPixmap;
QPixmap m_folderPixmap = embed::getIconPixmap("folder");
QPixmap m_folderOpenedPixmap = embed::getIconPixmap("folder_opened");
QPixmap m_folderLockedPixmap = embed::getIconPixmap("folder_locked");
//! Directories that lead here
//! Initially, this is just set to the current path of a directory
@@ -202,7 +247,7 @@ private:
QString m_filter;
int m_dirCount;
bool m_disableEntryPopulation = false;
} ;
@@ -211,20 +256,19 @@ private:
class FileItem : public QTreeWidgetItem
{
public:
enum FileTypes
enum class FileType
{
ProjectFile,
PresetFile,
SampleFile,
SoundFontFile,
PatchFile,
MidiFile,
VstPluginFile,
UnknownFile,
NumFileTypes
Project,
Preset,
Sample,
SoundFont,
Patch,
Midi,
VstPlugin,
Unknown
} ;
enum FileHandling
enum class FileHandling
{
NotSupported,
LoadAsProject,
@@ -243,42 +287,39 @@ public:
return QFileInfo(m_path, text(0)).absoluteFilePath();
}
inline FileTypes type( void ) const
inline FileType type() const
{
return( m_type );
}
inline FileHandling handling( void ) const
inline FileHandling handling() const
{
return( m_handling );
}
inline bool isTrack( void ) const
inline bool isTrack() const
{
return m_handling == LoadAsPreset || m_handling == LoadByPlugin;
return m_handling == FileHandling::LoadAsPreset || m_handling == FileHandling::LoadByPlugin;
}
QString extension( void );
QString extension();
static QString extension( const QString & file );
static QString defaultFilters();
private:
void initPixmaps( void );
void determineFileType( void );
static QPixmap * s_projectFilePixmap;
static QPixmap * s_presetFilePixmap;
static QPixmap * s_sampleFilePixmap;
static QPixmap * s_soundfontFilePixmap;
static QPixmap * s_vstPluginFilePixmap;
static QPixmap * s_midiFilePixmap;
static QPixmap * s_unknownFilePixmap;
void initPixmaps();
void determineFileType();
QString m_path;
FileTypes m_type;
FileType m_type;
FileHandling m_handling;
} ;
#endif
} // namespace gui
} // namespace lmms
#endif // LMMS_GUI_FILE_BROWSER_H

View File

@@ -22,14 +22,17 @@
*
*/
#ifndef FILEDIALOG_H
#define FILEDIALOG_H
#ifndef LMMS_GUI_FILE_DIALOG_H
#define LMMS_GUI_FILE_DIALOG_H
#include <QFileDialog>
#include "lmms_export.h"
namespace lmms::gui
{
class LMMS_EXPORT FileDialog : public QFileDialog
{
Q_OBJECT
@@ -50,4 +53,7 @@ public:
void clearSelection();
};
#endif // FILEDIALOG_HPP
} // namespace lmms::gui
#endif // LMMS_GUI_FILE_DIALOG_H

73
include/FileSearch.h Normal file
View File

@@ -0,0 +1,73 @@
/*
* FileSearch.h - File system search task
*
* Copyright (c) 2024 saker
*
* 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_SEARCH_H
#define LMMS_FILE_SEARCH_H
#include <QDir>
#include <QObject>
#include <atomic>
namespace lmms {
//! A Qt object that encapsulates the operation of searching the file system.
class FileSearch : public QObject
{
Q_OBJECT
public:
//! Number of milliseconds the search waits before signaling a matching result.
static constexpr int MillisecondsBetweenResults = 1;
//! Create a `FileSearch` object that uses the specified string filter `filter` and extension filters in
//! `extensions` to search within the given `paths`.
//! `excludedPaths`, `dirFilters`, and `sortFlags` can optionally be specified to exclude certain directories, filter
//! out certain types of entries, and sort the matches.
FileSearch(const QString& filter, const QStringList& paths, const QStringList& extensions,
const QStringList& excludedPaths = {}, QDir::Filters dirFilters = QDir::Filters{},
QDir::SortFlags sortFlags = QDir::SortFlags{});
//! Execute the search, emitting the `foundResult` signal when matches are found.
void operator()();
//! Cancel the search.
void cancel();
signals:
//! Emitted when a result is found when searching the file system.
void foundMatch(FileSearch* search, const QString& match);
//! Emitted when the search completes.
void searchCompleted(FileSearch* search);
private:
static auto isPathExcluded(const QString& path) -> bool;
QString m_filter;
QStringList m_paths;
QStringList m_extensions;
QStringList m_excludedPaths;
QDir::Filters m_dirFilters;
QDir::SortFlags m_sortFlags;
std::atomic<bool> m_cancel = false;
};
} // namespace lmms
#endif // LMMS_FILE_SEARCH_H

83
include/Flags.h Normal file
View File

@@ -0,0 +1,83 @@
/*
* Flags.h - class to make flags from enums
*
* Copyright (c) 2023 Dominic Clark
*
* 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_FLAGS_H
#define LMMS_FLAGS_H
#include <type_traits>
namespace lmms {
template<typename T>
class Flags
{
static_assert(std::is_enum_v<T>, "lmms::Flags can only be used with enum types");
public:
using EnumType = T;
using UnderlyingType = std::underlying_type_t<T>;
constexpr Flags() = default;
constexpr Flags(T value) : // Intentionally not explicit
m_value{static_cast<UnderlyingType>(value)}
{}
constexpr explicit Flags(UnderlyingType value) :
m_value{value}
{}
constexpr auto testAll(Flags flags) const -> bool { return (*this & flags) == flags; }
constexpr auto testAny(Flags flags) const -> bool { return (*this & flags) != Flags{}; }
constexpr auto testFlag(EnumType flag) const -> bool { return static_cast<bool>(*this & flag); }
constexpr auto operator~() const -> Flags { return Flags{~m_value}; }
friend constexpr auto operator&(Flags l, Flags r) -> Flags { return Flags{l.m_value & r.m_value}; }
friend constexpr auto operator|(Flags l, Flags r) -> Flags { return Flags{l.m_value | r.m_value}; }
friend constexpr auto operator^(Flags l, Flags r) -> Flags { return Flags{l.m_value ^ r.m_value}; }
friend constexpr auto operator+(Flags l, Flags r) -> Flags { return Flags{l.m_value | r.m_value}; }
friend constexpr auto operator-(Flags l, Flags r) -> Flags { return Flags{l.m_value & ~r.m_value}; }
constexpr auto operator&=(Flags f) -> Flags& { m_value &= f.m_value; return *this; }
constexpr auto operator|=(Flags f) -> Flags& { m_value |= f.m_value; return *this; }
constexpr auto operator^=(Flags f) -> Flags& { m_value ^= f.m_value; return *this; }
constexpr auto operator+=(Flags f) -> Flags& { m_value |= f.m_value; return *this; }
constexpr auto operator-=(Flags f) -> Flags& { m_value &= ~f.m_value; return *this; }
constexpr explicit operator UnderlyingType() const { return m_value; } // TODO C++23: explicit(std::is_scoped_enum<T>)
constexpr explicit operator bool() const { return m_value != 0; }
friend constexpr auto operator==(Flags l, Flags r) -> bool { return l.m_value == r.m_value; } // TODO C++20: = default
friend constexpr auto operator!=(Flags l, Flags r) -> bool { return l.m_value != r.m_value; } // TODO C++20: Remove
private:
UnderlyingType m_value = 0;
};
#define LMMS_DECLARE_OPERATORS_FOR_FLAGS(type) \
constexpr inline auto operator|(type l, type r) -> ::lmms::Flags<type> { return ::lmms::Flags{l} | ::lmms::Flags{r}; }
} // namespace lmms
#endif // LMMS_FLAGS_H

View File

@@ -0,0 +1,121 @@
/*
* FloatModelEditorBase.h - Base editor for float models
*
* Copyright (c) 2004-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2023 Michael Gregorius
*
* 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_GUI_FLOAT_MODEL_EDITOR_BASE_H
#define LMMS_GUI_FLOAT_MODEL_EDITOR_BASE_H
#include <QWidget>
#include <QPoint>
#include "AutomatableModelView.h"
namespace lmms::gui
{
class SimpleTextFloat;
class LMMS_EXPORT FloatModelEditorBase : public QWidget, public FloatModelView
{
Q_OBJECT
mapPropertyFromModel(bool, isVolumeKnob, setVolumeKnob, m_volumeKnob);
mapPropertyFromModel(float, volumeRatio, setVolumeRatio, m_volumeRatio);
void initUi(const QString & name); //!< to be called by ctors
public:
enum class DirectionOfManipulation
{
Vertical,
Horizontal
};
FloatModelEditorBase(DirectionOfManipulation directionOfManipulation = DirectionOfManipulation::Vertical, QWidget * _parent = nullptr, const QString & _name = QString()); //!< default ctor
FloatModelEditorBase(const FloatModelEditorBase& other) = delete;
// TODO: remove
inline void setHintText(const QString & txt_before, const QString & txt_after)
{
setDescription(txt_before);
setUnit(txt_after);
}
signals:
void sliderPressed();
void sliderReleased();
void sliderMoved(float value);
protected:
void contextMenuEvent(QContextMenuEvent * me) override;
void dragEnterEvent(QDragEnterEvent * dee) override;
void dropEvent(QDropEvent * de) override;
void focusOutEvent(QFocusEvent * fe) override;
void mousePressEvent(QMouseEvent * me) override;
void mouseReleaseEvent(QMouseEvent * me) override;
void mouseMoveEvent(QMouseEvent * me) override;
void mouseDoubleClickEvent(QMouseEvent * me) override;
void paintEvent(QPaintEvent * me) override;
void wheelEvent(QWheelEvent * me) override;
void enterEvent(QEvent *event) override;
void leaveEvent(QEvent *event) override;
virtual float getValue(const QPoint & p);
private slots:
virtual void enterValue();
void friendlyUpdate();
void toggleScale();
private:
virtual QString displayValue() const;
void doConnections() override;
void showTextFloat(int msecBeforeDisplay, int msecDisplayTime);
void setPosition(const QPoint & p);
inline float pageSize() const
{
return (model()->maxValue() - model()->minValue()) / 100.0f;
}
static SimpleTextFloat * s_textFloat;
BoolModel m_volumeKnob;
FloatModel m_volumeRatio;
QPoint m_lastMousePos; //!< mouse position in last mouseMoveEvent
float m_leftOver;
bool m_buttonPressed;
DirectionOfManipulation m_directionOfManipulation;
};
} // namespace lmms::gui
#endif // LMMS_GUI_FLOAT_MODEL_EDITOR_BASE_H

View File

@@ -1,5 +1,5 @@
/*
* ToolTip.h - namespace toolTip, a tooltip-wrapper for LMMS
* FontHelper.h - Header function to help with fonts
*
* Copyright (c) 2005-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
@@ -22,21 +22,26 @@
*
*/
#ifndef LMMS_FONT_HELPER_H
#define LMMS_FONT_HELPER_H
#ifndef TOOLTIP_H
#define TOOLTIP_H
#include <QApplication>
#include <QFont>
#include <qstring.h>
constexpr int DEFAULT_FONT_SIZE = 12;
constexpr int SMALL_FONT_SIZE = 10;
constexpr int LARGE_FONT_SIZE = 14;
#include "lmms_export.h"
class QWidget;
struct ToolTip
namespace lmms::gui
{
static void LMMS_EXPORT add( QWidget * _w, const QString & _txt );
} ;
// Convenience method to set the font size in pixels
inline QFont adjustedToPixelSize(QFont font, int size)
{
font.setPixelSize(size);
return font;
}
#endif
} // namespace lmms::gui
#endif // LMMS_FONT_HELPER_H

View File

@@ -1,119 +0,0 @@
/*
* FxLine.h - FX line widget
*
* Copyright (c) 2009 Andrew Kelley <superjoe30/at/gmail/dot/com>
* Copyright (c) 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 FX_LINE_H
#define FX_LINE_H
#include <QColorDialog>
#include <QGraphicsView>
#include <QLineEdit>
#include <QWidget>
#include "ColorChooser.h"
#include "Knob.h"
#include "LcdWidget.h"
#include "SendButtonIndicator.h"
class FxMixerView;
class SendButtonIndicator;
class FxLine : public QWidget
{
Q_OBJECT
public:
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 )
FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex);
~FxLine();
void paintEvent( QPaintEvent * ) override;
void mousePressEvent( QMouseEvent * ) override;
void mouseDoubleClickEvent( QMouseEvent * ) override;
void contextMenuEvent( QContextMenuEvent * ) override;
inline int channelIndex() { return m_channelIndex; }
void setChannelIndex(int index);
Knob * m_sendKnob;
SendButtonIndicator * m_sendBtn;
QBrush backgroundActive() const;
void setBackgroundActive( const QBrush & c );
QColor strokeOuterActive() const;
void setStrokeOuterActive( const QColor & c );
QColor strokeOuterInactive() const;
void setStrokeOuterInactive( const QColor & c );
QColor strokeInnerActive() const;
void setStrokeInnerActive( const QColor & c );
QColor strokeInnerInactive() const;
void setStrokeInnerInactive( const QColor & c );
static const int FxLineHeight;
bool eventFilter (QObject *dist, QEvent *event) override;
private:
void drawFxLine( QPainter* p, const FxLine *fxLine, bool isActive, bool sendToThis, bool receiveFromThis );
QString elideName( const QString & name );
FxMixerView * m_mv;
LcdWidget* m_lcd;
int m_channelIndex;
QBrush m_backgroundActive;
QColor m_strokeOuterActive;
QColor m_strokeOuterInactive;
QColor m_strokeInnerActive;
QColor m_strokeInnerInactive;
static QPixmap * s_sendBgArrow;
static QPixmap * s_receiveBgArrow;
bool m_inRename;
QLineEdit * m_renameLineEdit;
QGraphicsView * m_view;
public slots:
void renameChannel();
void resetColor();
void changeColor();
void randomColor();
private slots:
void renameFinished();
void removeChannel();
void removeUnusedChannels();
void moveChannelLeft();
void moveChannelRight();
};
#endif // FXLINE_H

View File

@@ -1,224 +0,0 @@
/*
* FxMixer.h - effect-mixer for LMMS
*
* Copyright (c) 2008-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 FX_MIXER_H
#define FX_MIXER_H
#include "Model.h"
#include "EffectChain.h"
#include "JournallingObject.h"
#include "ThreadableJob.h"
#include <atomic>
#include <QColor>
class FxRoute;
typedef QVector<FxRoute *> FxRouteVector;
class FxChannel : public ThreadableJob
{
public:
FxChannel( int idx, Model * _parent );
virtual ~FxChannel();
EffectChain m_fxChain;
// set to true when input fed from mixToChannel or child channel
bool m_hasInput;
// set to true if any effect in the channel is enabled and running
bool m_stillRunning;
float m_peakLeft;
float m_peakRight;
sampleFrame * m_buffer;
bool m_muteBeforeSolo;
BoolModel m_muteModel;
BoolModel m_soloModel;
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
// pointers to other channels that this one sends to
FxRouteVector m_sends;
// pointers to other channels that send to this one
FxRouteVector m_receives;
bool requiresProcessing() const override { return true; }
void unmuteForSolo();
void setColor (QColor newColor)
{
m_color = newColor;
m_hasColor = true;
}
// TODO C++17 and above: use std::optional instead
QColor m_color;
bool m_hasColor;
std::atomic_int m_dependenciesMet;
void incrementDeps();
void processed();
private:
void doProcessing() override;
};
class FxRoute : public QObject
{
Q_OBJECT
public:
FxRoute( FxChannel * from, FxChannel * to, float amount );
virtual ~FxRoute();
fx_ch_t senderIndex() const
{
return m_from->m_channelIndex;
}
fx_ch_t receiverIndex() const
{
return m_to->m_channelIndex;
}
FloatModel * amount()
{
return &m_amount;
}
FxChannel * sender() const
{
return m_from;
}
FxChannel * receiver() const
{
return m_to;
}
void updateName();
private:
FxChannel * m_from;
FxChannel * m_to;
FloatModel m_amount;
};
class LMMS_EXPORT FxMixer : public Model, public JournallingObject
{
Q_OBJECT
public:
FxMixer();
virtual ~FxMixer();
void mixToChannel( const sampleFrame * _buf, fx_ch_t _ch );
void prepareMasterMix();
void masterMix( sampleFrame * _buf );
void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override;
void loadSettings( const QDomElement & _this ) override;
QString nodeName() const override
{
return "fxmixer";
}
FxChannel * effectChannel( int _ch )
{
return m_fxChannels[_ch];
}
// make the output of channel fromChannel go to the input of channel toChannel
// it is safe to call even if the send already exists
FxRoute * createChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel,
float amount = 1.0f);
FxRoute * createRoute( FxChannel * from, FxChannel * to, float amount );
// delete the connection made by createChannelSend
void deleteChannelSend(fx_ch_t fromChannel, fx_ch_t toChannel);
void deleteChannelSend( FxRoute * route );
// determine if adding a send from sendFrom to
// sendTo would result in an infinite mixer loop.
bool isInfiniteLoop(fx_ch_t fromChannel, fx_ch_t toChannel);
bool checkInfiniteLoop( FxChannel * from, FxChannel * to );
// return the FloatModel of fromChannel sending its output to the input of
// toChannel. NULL if there is no send.
FloatModel * channelSendModel(fx_ch_t fromChannel, fx_ch_t toChannel);
// add a new channel to the Fx Mixer.
// returns the index of the channel that was just added
int createChannel();
// delete a channel from the FX mixer.
void deleteChannel(int index);
// delete all the mixer channels except master and remove all effects
void clear();
// re-arrange channels
void moveChannelLeft(int index);
void moveChannelRight(int index);
// reset a channel's name, fx, sends, etc
void clearChannel(fx_ch_t channelIndex);
// rename channels when moving etc. if they still have their original name
void validateChannelName( int index, int oldIndex );
void toggledSolo();
void activateSolo();
void deactivateSolo();
inline fx_ch_t numChannels() const
{
return m_fxChannels.size();
}
FxRouteVector m_fxRoutes;
private:
// the fx channels in the mixer. index 0 is always master.
QVector<FxChannel *> m_fxChannels;
// make sure we have at least num channels
void allocateChannelsTo(int num);
int m_lastSoloed;
} ;
#endif

View File

@@ -23,9 +23,8 @@
*
*/
#ifndef GRAPH_H
#define GRAPH_H
#ifndef LMMS_GUI_GRAPH_H
#define LMMS_GUI_GRAPH_H
#include <QWidget>
#include <QPixmap>
@@ -35,20 +34,26 @@
#include "ModelView.h"
#include "lmms_basics.h"
namespace lmms
{
class graphModel;
namespace gui
{
class LMMS_EXPORT Graph : public QWidget, public ModelView
{
Q_OBJECT
public:
enum graphStyle
enum class Style
{
NearestStyle, //!< draw as stairs
LinearStyle, //!< connect each 2 samples with a line, with wrapping
LinearNonCyclicStyle, //!< LinearStyle without wrapping
BarStyle, //!< draw thick bars
NumGraphStyles
Nearest, //!< draw as stairs
Linear, //!< connect each 2 samples with a line, with wrapping
LinearNonCyclic, //!< Linear without wrapping
Bar, //!< draw thick bars
};
/**
@@ -56,11 +61,11 @@ public:
* @param _width Pixel width of widget
* @param _height Pixel height of widget
*/
Graph( QWidget * _parent, graphStyle _style = Graph::LinearStyle,
Graph( QWidget * _parent, Style _style = Style::Linear,
int _width = 132,
int _height = 104
);
virtual ~Graph() = default;
~Graph() override = default;
void setForeground( const QPixmap & _pixmap );
@@ -72,13 +77,13 @@ public:
return castModel<graphModel>();
}
inline graphStyle getGraphStyle()
inline Style getGraphStyle()
{
return m_graphStyle;
}
inline void setGraphStyle( graphStyle _s )
inline void setGraphStyle( Style _s )
{
m_graphStyle = _s;
update();
@@ -108,7 +113,7 @@ private:
QPixmap m_foreground;
QColor m_graphColor;
graphStyle m_graphStyle;
Style m_graphStyle;
bool m_mouseDown;
int m_lastCursorX;
@@ -116,6 +121,9 @@ private:
} ;
} // namespace gui
/**
@brief 2 dimensional function plot
@@ -137,11 +145,11 @@ public:
graphModel( float _min,
float _max,
int _size,
:: Model * _parent,
Model * _parent,
bool _default_constructed = false,
float _step = 0.0 );
virtual ~graphModel() = default;
~graphModel() override = default;
// TODO: saveSettings, loadSettings?
@@ -187,7 +195,7 @@ public slots:
void setWaveToSaw();
void setWaveToSquare();
void setWaveToNoise();
QString setWaveToUser( );
QString setWaveToUser();
void smooth();
void smoothNonCyclic();
@@ -211,8 +219,11 @@ private:
float m_maxValue;
float m_step;
friend class Graph;
friend class gui::Graph;
};
#endif
} // namespace lmms
#endif // LMMS_GUI_GRAPH_H

View File

@@ -22,9 +22,8 @@
*
*/
#ifndef GROUP_BOX_H
#define GROUP_BOX_H
#ifndef LMMS_GUI_GROUP_BOX_H
#define LMMS_GUI_GROUP_BOX_H
#include <QWidget>
@@ -34,13 +33,15 @@
class QPixmap;
namespace lmms::gui
{
class GroupBox : public QWidget, public BoolModelView
{
Q_OBJECT
public:
GroupBox( const QString & _caption, QWidget * _parent = NULL );
virtual ~GroupBox();
GroupBox( const QString & _caption, QWidget * _parent = nullptr );
~GroupBox() override;
void modelChanged() override;
@@ -49,6 +50,21 @@ public:
return m_led;
}
/**
* @brief Returns whether the LED button is shown or not
*
* @return true LED button is shown
* @return false LED button is hidden
*/
bool ledButtonShown() const;
/**
* @brief Sets if the LED check box is shown or not
*
* @param value Set to true to show the LED check box or to false to hide it.
*/
void setLedButtonShown(bool value);
int titleBarHeight() const
{
return m_titleBarHeight;
@@ -70,6 +86,6 @@ private:
} ;
} // namespace lmms::gui
#endif
#endif // LMMS_GUI_GROUP_BOX_H

View File

@@ -22,21 +22,25 @@
*
*/
#ifndef GUIAPPLICATION_H
#define GUIAPPLICATION_H
#ifndef LMMS_GUI_GUI_APPLICATION_H
#define LMMS_GUI_GUI_APPLICATION_H
#include <QtCore/QObject>
#include <QObject>
#include "lmms_export.h"
#include "lmmsconfig.h"
class QLabel;
namespace lmms::gui
{
class AutomationEditorWindow;
class BBEditor;
class ControllerRackView;
class FxMixerView;
class MixerView;
class MainWindow;
class MicrotunerConfig;
class PatternEditorWindow;
class PianoRollWindow;
class ProjectNotes;
class SongEditorWindow;
@@ -46,7 +50,7 @@ class LMMS_EXPORT GuiApplication : public QObject
Q_OBJECT;
public:
explicit GuiApplication();
~GuiApplication();
~GuiApplication() override;
static GuiApplication* instance();
#ifdef LMMS_BUILD_WIN32
@@ -54,11 +58,12 @@ public:
#endif
MainWindow* mainWindow() { return m_mainWindow; }
FxMixerView* fxMixerView() { return m_fxMixerView; }
MixerView* mixerView() { return m_mixerView; }
SongEditorWindow* songEditor() { return m_songEditor; }
BBEditor* getBBEditor() { return m_bbEditor; }
PatternEditorWindow* patternEditor() { return m_patternEditor; }
PianoRollWindow* pianoRoll() { return m_pianoRoll; }
ProjectNotes* getProjectNotes() { return m_projectNotes; }
MicrotunerConfig* getMicrotunerConfig() { return m_microtunerConfig; }
AutomationEditorWindow* automationEditor() { return m_automationEditor; }
ControllerRackView* getControllerRackView() { return m_controllerRackView; }
@@ -72,16 +77,20 @@ private:
static GuiApplication* s_instance;
MainWindow* m_mainWindow;
FxMixerView* m_fxMixerView;
MixerView* m_mixerView;
SongEditorWindow* m_songEditor;
AutomationEditorWindow* m_automationEditor;
BBEditor* m_bbEditor;
PatternEditorWindow* m_patternEditor;
PianoRollWindow* m_pianoRoll;
ProjectNotes* m_projectNotes;
MicrotunerConfig* m_microtunerConfig;
ControllerRackView* m_controllerRackView;
QLabel* m_loadingProgressLabel;
};
#define gui GuiApplication::instance()
// Short-hand function
LMMS_EXPORT GuiApplication* getGUI();
#endif // GUIAPPLICATION_H
} // namespace lmms::gui
#endif // LMMS_GUI_GUI_APPLICATION_H

View File

@@ -23,13 +23,16 @@
*
*/
#ifndef IMPORT_FILTER_H
#define IMPORT_FILTER_H
#ifndef LMMS_IMPORT_FILTER_H
#define LMMS_IMPORT_FILTER_H
#include <QtCore/QFile>
#include <QFile>
#include "Plugin.h"
namespace lmms
{
class TrackContainer;
@@ -39,7 +42,7 @@ class LMMS_EXPORT ImportFilter : public Plugin
public:
ImportFilter( const QString & _file_name,
const Descriptor * _descriptor );
virtual ~ImportFilter();
~ImportFilter() override = default;
// tries to import given file to given track-container by having all
@@ -109,4 +112,6 @@ private:
} ;
#endif
} // namespace lmms
#endif // LMMS_IMPORT_FILTER_H

Some files were not shown because too many files have changed in this diff Show More