Merge remote-tracking branch 'upstream/master' into dynamic-effect-dialog-merge_upstream_20230923
This commit is contained in:
@@ -7,6 +7,13 @@ SET(LMMS_BINARY_DIR ${CMAKE_BINARY_DIR})
|
||||
SET(LMMS_SOURCE_DIR ${CMAKE_SOURCE_DIR})
|
||||
|
||||
# CMAKE_POLICY Section
|
||||
IF(COMMAND CMAKE_POLICY)
|
||||
# TODO: Keep CMP0074 but remove this condition when cmake 3.12+ is guaranteed
|
||||
IF(${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.12)
|
||||
# Needed for the SWH Ladspa plugins. See below.
|
||||
CMAKE_POLICY(SET CMP0074 NEW) # find_package() uses <PackageName>_ROOT variables
|
||||
ENDIF()
|
||||
ENDIF(COMMAND CMAKE_POLICY)
|
||||
|
||||
# Import of windows.h breaks min()/max()
|
||||
ADD_DEFINITIONS(-DNOMINMAX)
|
||||
@@ -78,6 +85,10 @@ OPTION(WANT_VST_32 "Include 32-bit VST support" ON)
|
||||
OPTION(WANT_VST_64 "Include 64-bit VST support" ON)
|
||||
OPTION(WANT_WINMM "Include WinMM MIDI support" OFF)
|
||||
OPTION(WANT_DEBUG_FPE "Debug floating point exceptions" OFF)
|
||||
option(WANT_DEBUG_ASAN "Enable AddressSanitizer" OFF)
|
||||
option(WANT_DEBUG_TSAN "Enable ThreadSanitizer" OFF)
|
||||
option(WANT_DEBUG_MSAN "Enable MemorySanitizer" OFF)
|
||||
option(WANT_DEBUG_UBSAN "Enable UndefinedBehaviorSanitizer" OFF)
|
||||
OPTION(BUNDLE_QT_TRANSLATIONS "Install Qt translation files for LMMS" OFF)
|
||||
|
||||
|
||||
@@ -262,8 +273,17 @@ ELSE(WANT_CMT)
|
||||
ENDIF(WANT_CMT)
|
||||
|
||||
IF(WANT_SWH)
|
||||
SET(LMMS_HAVE_SWH TRUE)
|
||||
SET(STATUS_SWH "OK")
|
||||
IF(LMMS_BUILD_APPLE)
|
||||
# Prefer system perl over Homebrew, MacPorts, etc
|
||||
SET(Perl_ROOT "/usr/bin")
|
||||
ENDIF()
|
||||
FIND_PACKAGE(Perl)
|
||||
IF(PERL_FOUND)
|
||||
SET(LMMS_HAVE_SWH TRUE)
|
||||
SET(STATUS_SWH "OK")
|
||||
ELSE()
|
||||
SET(STATUS_SWH "Skipping, perl is missing")
|
||||
ENDIF()
|
||||
ELSE(WANT_SWH)
|
||||
SET(STATUS_SWH "not built as requested")
|
||||
ENDIF(WANT_SWH)
|
||||
@@ -647,8 +667,36 @@ IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
ELSE(WIN32)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -DPIC")
|
||||
ENDIF(WIN32)
|
||||
elseif(MSVC)
|
||||
# Use UTF-8 as the source and execution character set
|
||||
add_compile_options("/utf-8")
|
||||
ENDIF()
|
||||
|
||||
# add enabled sanitizers
|
||||
function(add_sanitizer sanitizer supported_compilers want_flag status_flag)
|
||||
if(${want_flag})
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "${supported_compilers}")
|
||||
set("${status_flag}" "Enabled" PARENT_SCOPE)
|
||||
string(REPLACE ";" " " additional_flags "${ARGN}")
|
||||
# todo CMake 3.13: use add_compile_options/add_link_options instead
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=${sanitizer} ${additional_flags}" PARENT_SCOPE)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=${sanitizer} ${additional_flags}" PARENT_SCOPE)
|
||||
else()
|
||||
set("${status_flag}" "Wanted but disabled due to unsupported compiler" PARENT_SCOPE)
|
||||
endif()
|
||||
else()
|
||||
set("${status_flag}" "Disabled" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
add_sanitizer(address "GNU|Clang|MSVC" WANT_DEBUG_ASAN STATUS_DEBUG_ASAN)
|
||||
add_sanitizer(thread "GNU|Clang" WANT_DEBUG_TSAN STATUS_DEBUG_TSAN)
|
||||
add_sanitizer(memory "Clang" WANT_DEBUG_MSAN STATUS_DEBUG_MSAN -fno-omit-frame-pointer)
|
||||
# UBSan does not link with vptr enabled due to a problem with references from PeakControllerEffect
|
||||
# not being found by PeakController
|
||||
add_sanitizer(undefined "GNU|Clang" WANT_DEBUG_UBSAN STATUS_DEBUG_UBSAN -fno-sanitize=vptr)
|
||||
|
||||
|
||||
# use ccache
|
||||
include(CompileCache)
|
||||
|
||||
@@ -717,7 +765,6 @@ ADD_CUSTOM_TARGET(uninstall
|
||||
COMMAND ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}" -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/uninstall.cmake"
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# display configuration information
|
||||
#
|
||||
@@ -783,7 +830,11 @@ MESSAGE(
|
||||
MESSAGE(
|
||||
"Developer options\n"
|
||||
"-----------------------------------------\n"
|
||||
"* Debug FP exceptions : ${STATUS_DEBUG_FPE}\n"
|
||||
"* Debug FP exceptions : ${STATUS_DEBUG_FPE}\n"
|
||||
"* Debug using AddressSanitizer : ${STATUS_DEBUG_ASAN}\n"
|
||||
"* Debug using ThreadSanitizer : ${STATUS_DEBUG_TSAN}\n"
|
||||
"* Debug using MemorySanitizer : ${STATUS_DEBUG_MSAN}\n"
|
||||
"* Debug using UBSanitizer : ${STATUS_DEBUG_UBSAN}\n"
|
||||
)
|
||||
|
||||
MESSAGE(
|
||||
|
||||
@@ -82,7 +82,6 @@ lmms--gui--TextFloat, lmms--gui--SimpleTextFloat {
|
||||
QMenu {
|
||||
border:1px solid #747474;
|
||||
background-color: #c9c9c9;
|
||||
font-size:11px;
|
||||
}
|
||||
|
||||
QMenu::separator {
|
||||
@@ -98,15 +97,12 @@ QMenu::item {
|
||||
|
||||
QMenu::item:selected {
|
||||
color: white;
|
||||
font-weight:bold;
|
||||
background-color: #747474;
|
||||
}
|
||||
|
||||
QMenu::item:disabled {
|
||||
color: #747474;
|
||||
background-color: #c9c9c9;
|
||||
font-size:12px;
|
||||
font-weight: normal;
|
||||
padding: 4px 32px 4px 20px;
|
||||
}
|
||||
|
||||
@@ -132,7 +128,7 @@ QMenu::indicator:selected {
|
||||
|
||||
lmms--gui--FileBrowser QCheckBox
|
||||
{
|
||||
font-size: 10px;
|
||||
font-size: 8pt;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ QLabel, QTreeWidget, QListWidget, QGroupBox, QMenuBar {
|
||||
|
||||
QTreeView {
|
||||
outline: none;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
QTreeWidget::item {
|
||||
@@ -42,7 +41,7 @@ QMdiArea {
|
||||
|
||||
lmms--gui--FileBrowser QCheckBox
|
||||
{
|
||||
font-size: 10px;
|
||||
font-size: 8pt;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@@ -115,7 +114,6 @@ QSplashScreen QLabel {
|
||||
QMenu {
|
||||
border-top: 2px solid #08993E;
|
||||
background-color: #15191c;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
QMenu::separator {
|
||||
@@ -133,15 +131,12 @@ QMenu::item {
|
||||
|
||||
QMenu::item:selected {
|
||||
color: #d1d8e4;
|
||||
font-weight: normal;
|
||||
background-color: #21272b;
|
||||
}
|
||||
|
||||
QMenu::item:disabled {
|
||||
color: #515459;
|
||||
background-color: #262b30;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
padding: 4px 32px 4px 20px;
|
||||
}
|
||||
|
||||
|
||||
@@ -126,6 +126,8 @@ private:
|
||||
void upgrade_defaultTripleOscillatorHQ();
|
||||
void upgrade_mixerRename();
|
||||
void upgrade_bbTcoRename();
|
||||
void upgrade_sampleAndHold();
|
||||
void upgrade_midiCCIndexing();
|
||||
|
||||
// List of all upgrade methods
|
||||
static const std::vector<UpgradeMethod> UPGRADE_METHODS;
|
||||
|
||||
@@ -75,8 +75,7 @@ public:
|
||||
private slots:
|
||||
void reloadTree();
|
||||
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 );
|
||||
bool filterAndExpandItems(const QString & filter, QTreeWidgetItem * item = nullptr);
|
||||
void giveFocusToFilter();
|
||||
|
||||
private:
|
||||
@@ -84,6 +83,9 @@ private:
|
||||
|
||||
void addItems( const QString & path );
|
||||
|
||||
void saveDirectoriesStates();
|
||||
void restoreDirectoriesStates();
|
||||
|
||||
FileBrowserTreeWidget * m_fileBrowserTreeWidget;
|
||||
|
||||
QLineEdit * m_filterEdit;
|
||||
@@ -99,6 +101,8 @@ private:
|
||||
QCheckBox* m_showFactoryContent = nullptr;
|
||||
QString m_userDir;
|
||||
QString m_factoryDir;
|
||||
QList<QString> m_savedExpandedDirs;
|
||||
QString m_previousFilterValue;
|
||||
} ;
|
||||
|
||||
|
||||
@@ -115,7 +119,6 @@ public:
|
||||
//! that are expanded in the tree.
|
||||
QList<QString> expandedDirs( QTreeWidgetItem * item = nullptr ) const;
|
||||
|
||||
|
||||
protected:
|
||||
void contextMenuEvent( QContextMenuEvent * e ) override;
|
||||
void mousePressEvent( QMouseEvent * me ) override;
|
||||
|
||||
@@ -26,62 +26,33 @@
|
||||
#define LMMS_INSTRUMENT_PLAY_HANDLE_H
|
||||
|
||||
#include "PlayHandle.h"
|
||||
#include "Instrument.h"
|
||||
#include "NotePlayHandle.h"
|
||||
#include "lmms_export.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class Instrument;
|
||||
class InstrumentTrack;
|
||||
|
||||
class LMMS_EXPORT InstrumentPlayHandle : public PlayHandle
|
||||
{
|
||||
public:
|
||||
InstrumentPlayHandle( Instrument * instrument, InstrumentTrack* instrumentTrack );
|
||||
InstrumentPlayHandle(Instrument * instrument, InstrumentTrack* instrumentTrack);
|
||||
|
||||
~InstrumentPlayHandle() override = default;
|
||||
|
||||
|
||||
void play( sampleFrame * _working_buffer ) override
|
||||
{
|
||||
// ensure that all our nph's have been processed first
|
||||
ConstNotePlayHandleList nphv = NotePlayHandle::nphsOfInstrumentTrack( m_instrument->instrumentTrack(), true );
|
||||
|
||||
bool nphsLeft;
|
||||
do
|
||||
{
|
||||
nphsLeft = false;
|
||||
for( const NotePlayHandle * constNotePlayHandle : nphv )
|
||||
{
|
||||
NotePlayHandle * notePlayHandle = const_cast<NotePlayHandle *>( constNotePlayHandle );
|
||||
if( notePlayHandle->state() != ThreadableJob::ProcessingState::Done &&
|
||||
!notePlayHandle->isFinished())
|
||||
{
|
||||
nphsLeft = true;
|
||||
notePlayHandle->process();
|
||||
}
|
||||
}
|
||||
}
|
||||
while( nphsLeft );
|
||||
|
||||
m_instrument->play( _working_buffer );
|
||||
}
|
||||
void play(sampleFrame * working_buffer) override;
|
||||
|
||||
bool isFinished() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isFromTrack( const Track* _track ) const override
|
||||
{
|
||||
return m_instrument->isFromTrack( _track );
|
||||
}
|
||||
|
||||
bool isFromTrack(const Track* track) const override;
|
||||
|
||||
private:
|
||||
Instrument* m_instrument;
|
||||
|
||||
} ;
|
||||
|
||||
};
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ protected:
|
||||
sample_t (*m_sampleFunction)( const float );
|
||||
|
||||
private:
|
||||
float m_heldSample;
|
||||
SampleBuffer * m_userDefSampleBuffer;
|
||||
|
||||
protected slots:
|
||||
|
||||
@@ -48,6 +48,7 @@ class MidiController : public Controller, public MidiEventProcessor
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static constexpr int NONE = -1;
|
||||
MidiController( Model * _parent );
|
||||
~MidiController() override = default;
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
|
||||
public slots:
|
||||
void setSampleBuffer( lmms::SampleBuffer* sb );
|
||||
void setSampleFile( const QString & _sf );
|
||||
void setSampleFile( const QString & sf );
|
||||
void updateLength();
|
||||
void toggleRecord();
|
||||
void playbackPositionChanged();
|
||||
|
||||
@@ -171,9 +171,6 @@ void AudioFileProcessor::playNote( NotePlayHandle * _n,
|
||||
static_cast<SampleBuffer::LoopMode>( m_loopModel.value() ) ) )
|
||||
{
|
||||
applyRelease( _working_buffer, _n );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer,
|
||||
frames + offset, _n );
|
||||
|
||||
emit isPlaying( ((handleState *)_n->m_pluginData)->frameIndex() );
|
||||
}
|
||||
else
|
||||
|
||||
@@ -307,8 +307,6 @@ void BitInvader::playNote( NotePlayHandle * _n,
|
||||
}
|
||||
|
||||
applyRelease( _working_buffer, _n );
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -508,7 +508,6 @@ void CarlaInstrument::play(sampleFrame* workingBuffer)
|
||||
|
||||
if (fHandle == nullptr)
|
||||
{
|
||||
instrumentTrack()->processAudioBuffer(workingBuffer, bufsize, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -556,8 +555,6 @@ void CarlaInstrument::play(sampleFrame* workingBuffer)
|
||||
workingBuffer[i][0] = buf1[i];
|
||||
workingBuffer[i][1] = buf2[i];
|
||||
}
|
||||
|
||||
instrumentTrack()->processAudioBuffer(workingBuffer, bufsize, nullptr);
|
||||
}
|
||||
|
||||
bool CarlaInstrument::handleMidiEvent(const MidiEvent& event, const TimePos&, f_cnt_t offset)
|
||||
|
||||
@@ -320,8 +320,8 @@ bool CompressorEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames)
|
||||
float inputValue = feedback ? m_prevOut[i] : s[i];
|
||||
|
||||
// Calculate the crest factor of the audio by diving the peak by the RMS
|
||||
m_crestPeakVal[i] = qMax(inputValue * inputValue, m_crestTimeConst * m_crestPeakVal[i] + (1 - m_crestTimeConst) * (inputValue * inputValue));
|
||||
m_crestRmsVal[i] = m_crestTimeConst * m_crestRmsVal[i] + ((1 - m_crestTimeConst) * (inputValue * inputValue));
|
||||
m_crestPeakVal[i] = qMax(qMax(COMP_NOISE_FLOOR, inputValue * inputValue), m_crestTimeConst * m_crestPeakVal[i] + (1 - m_crestTimeConst) * (inputValue * inputValue));
|
||||
m_crestRmsVal[i] = qMax(COMP_NOISE_FLOOR, m_crestTimeConst * m_crestRmsVal[i] + ((1 - m_crestTimeConst) * (inputValue * inputValue)));
|
||||
m_crestFactorVal[i] = m_crestPeakVal[i] / m_crestRmsVal[i];
|
||||
|
||||
m_rmsVal[i] = m_rmsTimeConst * m_rmsVal[i] + ((1 - m_rmsTimeConst) * (inputValue * inputValue));
|
||||
|
||||
@@ -419,7 +419,6 @@ void FreeBoyInstrument::playNote(NotePlayHandle* nph, sampleFrame* workingBuffer
|
||||
}
|
||||
framesLeft -= count;
|
||||
}
|
||||
instrumentTrack()->processAudioBuffer(workingBuffer, frames + offset, nph);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -494,8 +494,6 @@ void GigInstrument::play( sampleFrame * _working_buffer )
|
||||
_working_buffer[i][0] *= m_gain.value();
|
||||
_working_buffer[i][1] *= m_gain.value();
|
||||
}
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames, nullptr );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -197,8 +197,6 @@ void KickerInstrument::playNote( NotePlayHandle * _n,
|
||||
_working_buffer[f+offset][1] *= fac;
|
||||
}
|
||||
}
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -36,9 +36,9 @@ TARGET_COMPILE_DEFINITIONS(veal PRIVATE DISABLE_OSC=1)
|
||||
|
||||
SET(INLINE_FLAGS "")
|
||||
IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
SET(INLINE_FLAGS "-finline-functions-called-once -finline-limit=80")
|
||||
SET(INLINE_FLAGS -finline-functions-called-once -finline-limit=80)
|
||||
ENDIF()
|
||||
SET_TARGET_PROPERTIES(veal PROPERTIES COMPILE_FLAGS "-fexceptions -O2 -finline-functions ${INLINE_FLAGS}")
|
||||
target_compile_options(veal PRIVATE -fexceptions -O2 -finline-functions ${INLINE_FLAGS})
|
||||
|
||||
if(LMMS_BUILD_WIN32)
|
||||
add_custom_command(
|
||||
@@ -50,5 +50,5 @@ if(LMMS_BUILD_WIN32)
|
||||
)
|
||||
endif()
|
||||
IF(NOT LMMS_BUILD_APPLE AND NOT LMMS_BUILD_OPENBSD)
|
||||
SET_TARGET_PROPERTIES(veal PROPERTIES LINK_FLAGS "${LINK_FLAGS} -shared -Wl,-no-undefined")
|
||||
target_link_libraries(veal PRIVATE -shared)
|
||||
ENDIF()
|
||||
|
||||
@@ -5,7 +5,7 @@ ADD_LIBRARY(cmt MODULE ${SOURCES})
|
||||
INSTALL(TARGETS cmt LIBRARY DESTINATION "${PLUGIN_DIR}/ladspa")
|
||||
|
||||
SET_TARGET_PROPERTIES(cmt PROPERTIES PREFIX "")
|
||||
SET_TARGET_PROPERTIES(cmt PROPERTIES COMPILE_FLAGS "-Wall -O3 -fno-strict-aliasing")
|
||||
target_compile_options(cmt PRIVATE -Wall -O3 -fno-strict-aliasing)
|
||||
|
||||
if(LMMS_BUILD_WIN32)
|
||||
add_custom_command(
|
||||
@@ -18,10 +18,10 @@ if(LMMS_BUILD_WIN32)
|
||||
endif()
|
||||
|
||||
if(NOT LMMS_BUILD_WIN32)
|
||||
set_target_properties(cmt PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -fPIC")
|
||||
target_compile_options(cmt PRIVATE -fPIC)
|
||||
endif()
|
||||
|
||||
IF(NOT LMMS_BUILD_APPLE AND NOT LMMS_BUILD_OPENBSD)
|
||||
SET_TARGET_PROPERTIES(cmt PROPERTIES LINK_FLAGS "${LINK_FLAGS} -shared -Wl,-no-undefined")
|
||||
target_link_libraries(cmt PRIVATE -shared)
|
||||
ENDIF()
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ SET(COMPILE_FLAGS "${COMPILE_FLAGS} ${PIC_FLAGS}")
|
||||
# Loop over every XML file
|
||||
FILE(GLOB XML_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/ladspa/*.xml")
|
||||
LIST(SORT XML_SOURCES)
|
||||
|
||||
FOREACH(_item ${XML_SOURCES})
|
||||
# Get library name and (soon to be) C file
|
||||
GET_FILENAME_COMPONENT(_plugin "${_item}" NAME_WE)
|
||||
@@ -24,7 +25,7 @@ FOREACH(_item ${XML_SOURCES})
|
||||
# Coerce XML source file to C
|
||||
ADD_CUSTOM_COMMAND(
|
||||
OUTPUT "${_out_file}"
|
||||
COMMAND ./makestub.pl "${_item}" > "${_out_file}"
|
||||
COMMAND "${PERL_EXECUTABLE}" ./makestub.pl "${_item}" > "${_out_file}"
|
||||
DEPENDS "${_item}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ladspa"
|
||||
VERBATIM
|
||||
|
||||
@@ -790,7 +790,6 @@ void Lb302Synth::play( sampleFrame * _working_buffer )
|
||||
const fpp_t frames = Engine::audioEngine()->framesPerPeriod();
|
||||
|
||||
process( _working_buffer, frames );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames, nullptr );
|
||||
// release_frame = 0; //removed for issue # 1432
|
||||
}
|
||||
|
||||
|
||||
@@ -197,8 +197,6 @@ void Lv2Instrument::play(sampleFrame *buf)
|
||||
|
||||
copyModelsToLmms();
|
||||
copyBuffersToLmms(buf, fpp);
|
||||
|
||||
instrumentTrack()->processAudioBuffer(buf, fpp, nullptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1040,8 +1040,6 @@ void MonstroInstrument::playNote( NotePlayHandle * _n,
|
||||
ms->renderOutput( frames, _working_buffer + offset );
|
||||
|
||||
//applyRelease( _working_buffer, _n ); // we have our own release
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
void MonstroInstrument::deleteNotePluginData( NotePlayHandle * _n )
|
||||
|
||||
@@ -561,8 +561,6 @@ void NesInstrument::playNote( NotePlayHandle * n, sampleFrame * workingBuffer )
|
||||
nes->renderOutput( workingBuffer + offset, frames );
|
||||
|
||||
applyRelease( workingBuffer, n );
|
||||
|
||||
instrumentTrack()->processAudioBuffer( workingBuffer, frames + offset, n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -412,10 +412,6 @@ void OpulenzInstrument::play( sampleFrame * _working_buffer )
|
||||
}
|
||||
}
|
||||
emulatorMutex.unlock();
|
||||
|
||||
// Throw the data to the track...
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frameCount, nullptr );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -312,8 +312,6 @@ void OrganicInstrument::playNote( NotePlayHandle * _n,
|
||||
}
|
||||
|
||||
// -- --
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -157,8 +157,6 @@ void PatmanInstrument::playNote( NotePlayHandle * _n,
|
||||
play_freq, m_loopedModel.value() ? SampleBuffer::LoopMode::On : SampleBuffer::LoopMode::Off ) )
|
||||
{
|
||||
applyRelease( _working_buffer, _n );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer,
|
||||
frames + offset, _n );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -446,7 +444,7 @@ namespace gui
|
||||
|
||||
PatmanView::PatmanView( Instrument * _instrument, QWidget * _parent ) :
|
||||
InstrumentViewFixedSize( _instrument, _parent ),
|
||||
m_pi( nullptr )
|
||||
m_pi(castModel<PatmanInstrument>())
|
||||
{
|
||||
setAutoFillBackground( true );
|
||||
QPalette pal;
|
||||
@@ -487,7 +485,15 @@ PatmanView::PatmanView( Instrument * _instrument, QWidget * _parent ) :
|
||||
"tune_off" ) );
|
||||
m_tuneButton->setToolTip(tr("Tune mode"));
|
||||
|
||||
m_displayFilename = tr( "No file selected" );
|
||||
|
||||
if (m_pi->m_patchFile.isEmpty())
|
||||
{
|
||||
m_displayFilename = tr("No file selected");
|
||||
}
|
||||
else
|
||||
{
|
||||
updateFilename();
|
||||
}
|
||||
|
||||
setAcceptDrops( true );
|
||||
}
|
||||
|
||||
@@ -848,7 +848,6 @@ void Sf2Instrument::play( sampleFrame * _working_buffer )
|
||||
if( m_playingNotes.isEmpty() )
|
||||
{
|
||||
renderFrames( frames, _working_buffer );
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames, nullptr );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -906,7 +905,6 @@ void Sf2Instrument::play( sampleFrame * _working_buffer )
|
||||
{
|
||||
renderFrames( frames - currentFrame, _working_buffer + currentFrame );
|
||||
}
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames, nullptr );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -480,9 +480,6 @@ void SfxrInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffe
|
||||
delete[] pitchedBuffer;
|
||||
|
||||
applyRelease( _working_buffer, _n );
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frameNum + offset, _n );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -429,8 +429,6 @@ void SidInstrument::playNote( NotePlayHandle * _n,
|
||||
_working_buffer[frame+offset][ch] = s;
|
||||
}
|
||||
}
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -359,8 +359,6 @@ void MalletsInstrument::playNote( NotePlayHandle * _n,
|
||||
_working_buffer[frame][1] = ps->nextSampleRight() *
|
||||
( m_scalers[p] + add_scale );
|
||||
}
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -380,8 +380,6 @@ void TripleOscillator::playNote( NotePlayHandle * _n,
|
||||
|
||||
applyFadeIn(_working_buffer, _n);
|
||||
applyRelease( _working_buffer, _n );
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -399,8 +399,6 @@ void VestigeInstrument::play( sampleFrame * _buf )
|
||||
{
|
||||
if (!m_pluginMutex.tryLock(Engine::getSong()->isExporting() ? -1 : 0)) {return;}
|
||||
|
||||
const fpp_t frames = Engine::audioEngine()->framesPerPeriod();
|
||||
|
||||
if( m_plugin == nullptr )
|
||||
{
|
||||
m_pluginMutex.unlock();
|
||||
@@ -409,8 +407,6 @@ void VestigeInstrument::play( sampleFrame * _buf )
|
||||
|
||||
m_plugin->process( nullptr, _buf );
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _buf, frames, nullptr );
|
||||
|
||||
m_pluginMutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
@@ -251,8 +251,6 @@ void Vibed::playNote(NotePlayHandle* n, sampleFrame* workingBuffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instrumentTrack()->processAudioBuffer(workingBuffer, frames + offset, n);
|
||||
}
|
||||
|
||||
void Vibed::deleteNotePluginData(NotePlayHandle* n)
|
||||
|
||||
@@ -82,7 +82,11 @@ void VstSubPluginFeatures::addPluginsFromDir( QStringList* filenames, QString pa
|
||||
}
|
||||
}
|
||||
QStringList dlls = QDir( ConfigManager::inst()->vstDir() + path ).
|
||||
entryList( QStringList() << "*.dll",
|
||||
entryList( QStringList() << "*.dll"
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
<< "*.so"
|
||||
#endif
|
||||
,
|
||||
QDir::Files, QDir::Name );
|
||||
for( int i = 0; i < dlls.size(); i++ )
|
||||
{
|
||||
|
||||
@@ -445,8 +445,6 @@ void WatsynInstrument::playNote( NotePlayHandle * _n,
|
||||
}
|
||||
|
||||
applyRelease( _working_buffer, _n );
|
||||
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -233,8 +233,6 @@ void Xpressive::playNote(NotePlayHandle* nph, sampleFrame* working_buffer) {
|
||||
const f_cnt_t offset = nph->noteOffset();
|
||||
|
||||
ps->renderOutput(frames, working_buffer + offset);
|
||||
|
||||
instrumentTrack()->processAudioBuffer(working_buffer, frames + offset, nph);
|
||||
}
|
||||
|
||||
void Xpressive::deleteNotePluginData(NotePlayHandle* nph) {
|
||||
|
||||
@@ -341,7 +341,6 @@ void ZynAddSubFxInstrument::play( sampleFrame * _buf )
|
||||
m_plugin->processAudio( _buf );
|
||||
}
|
||||
m_pluginMutex.unlock();
|
||||
instrumentTrack()->processAudioBuffer( _buf, Engine::audioEngine()->framesPerPeriod(), nullptr );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -701,7 +701,10 @@ void AudioEngine::removeAudioPort(AudioPort * port)
|
||||
|
||||
bool AudioEngine::addPlayHandle( PlayHandle* handle )
|
||||
{
|
||||
if( criticalXRuns() == false )
|
||||
// Only add play handles if we have the CPU capacity to process them.
|
||||
// Instrument play handles are not added during playback, but when the
|
||||
// associated instrument is created, so add those unconditionally.
|
||||
if (handle->type() == PlayHandle::Type::InstrumentPlayHandle || !criticalXRuns())
|
||||
{
|
||||
m_newPlayHandles.push( handle );
|
||||
handle->audioPort()->addPlayHandle( handle );
|
||||
|
||||
@@ -79,6 +79,7 @@ const std::vector<DataFile::UpgradeMethod> DataFile::UPGRADE_METHODS = {
|
||||
&DataFile::upgrade_automationNodes , &DataFile::upgrade_extendedNoteRange,
|
||||
&DataFile::upgrade_defaultTripleOscillatorHQ,
|
||||
&DataFile::upgrade_mixerRename , &DataFile::upgrade_bbTcoRename,
|
||||
&DataFile::upgrade_sampleAndHold , &DataFile::upgrade_midiCCIndexing
|
||||
};
|
||||
|
||||
// Vector of all versions that have upgrade routines.
|
||||
@@ -1703,26 +1704,56 @@ void DataFile::upgrade_mixerRename()
|
||||
{
|
||||
// Change nodename <fxmixer> to <mixer>
|
||||
QDomNodeList fxmixer = elementsByTagName("fxmixer");
|
||||
for (int i = 0; !fxmixer.item(i).isNull(); ++i)
|
||||
for (int i = 0; i < fxmixer.length(); ++i)
|
||||
{
|
||||
fxmixer.item(i).toElement().setTagName("mixer");
|
||||
auto item = fxmixer.item(i).toElement();
|
||||
if (item.isNull())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
item.setTagName("mixer");
|
||||
}
|
||||
|
||||
// Change nodename <fxchannel> to <mixerchannel>
|
||||
QDomNodeList fxchannel = elementsByTagName("fxchannel");
|
||||
for (int i = 0; !fxchannel.item(i).isNull(); ++i)
|
||||
for (int i = 0; i < fxchannel.length(); ++i)
|
||||
{
|
||||
fxchannel.item(i).toElement().setTagName("mixerchannel");
|
||||
auto item = fxchannel.item(i).toElement();
|
||||
if (item.isNull())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
item.setTagName("mixerchannel");
|
||||
}
|
||||
|
||||
// Change the attribute fxch of element <instrumenttrack> to mixch
|
||||
QDomNodeList fxch = elementsByTagName("instrumenttrack");
|
||||
for(int i = 0; !fxch.item(i).isNull(); ++i)
|
||||
for (int i = 0; i < fxch.length(); ++i)
|
||||
{
|
||||
if(fxch.item(i).toElement().hasAttribute("fxch"))
|
||||
auto item = fxch.item(i).toElement();
|
||||
if (item.isNull())
|
||||
{
|
||||
fxch.item(i).toElement().setAttribute("mixch", fxch.item(i).toElement().attribute("fxch"));
|
||||
fxch.item(i).toElement().removeAttribute("fxch");
|
||||
continue;
|
||||
}
|
||||
if (item.hasAttribute("fxch"))
|
||||
{
|
||||
item.setAttribute("mixch", item.attribute("fxch"));
|
||||
item.removeAttribute("fxch");
|
||||
}
|
||||
}
|
||||
// Change the attribute fxch of element <sampletrack> to mixch
|
||||
fxch = elementsByTagName("sampletrack");
|
||||
for (int i = 0; i < fxch.length(); ++i)
|
||||
{
|
||||
auto item = fxch.item(i).toElement();
|
||||
if (item.isNull())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (item.hasAttribute("fxch"))
|
||||
{
|
||||
item.setAttribute("mixch", item.attribute("fxch"));
|
||||
item.removeAttribute("fxch");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1762,6 +1793,44 @@ void DataFile::upgrade_bbTcoRename()
|
||||
}
|
||||
|
||||
|
||||
// Set LFO speed to 0.01 on projects made before sample-and-hold PR
|
||||
void DataFile::upgrade_sampleAndHold()
|
||||
{
|
||||
QDomNodeList elements = elementsByTagName("lfocontroller");
|
||||
for (int i = 0; i < elements.length(); ++i)
|
||||
{
|
||||
if (elements.item(i).isNull()) { continue; }
|
||||
auto e = elements.item(i).toElement();
|
||||
// Correct old random wave LFO speeds
|
||||
if (e.attribute("wave").toInt() == 6)
|
||||
{
|
||||
e.setAttribute("speed",0.01f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Update MIDI CC indexes, so that they are counted from 0. Older releases of LMMS
|
||||
//! count the CCs from 1.
|
||||
void DataFile::upgrade_midiCCIndexing()
|
||||
{
|
||||
static constexpr std::array attributesToUpdate{"inputcontroller", "outputcontroller"};
|
||||
|
||||
QDomNodeList elements = elementsByTagName("Midicontroller");
|
||||
for(int i = 0; i < elements.length(); i++)
|
||||
{
|
||||
if (elements.item(i).isNull()) { continue; }
|
||||
auto element = elements.item(i).toElement();
|
||||
for (const char* attrName : attributesToUpdate)
|
||||
{
|
||||
if (element.hasAttribute(attrName))
|
||||
{
|
||||
int cc = element.attribute(attrName).toInt();
|
||||
element.setAttribute(attrName, cc - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DataFile::upgrade()
|
||||
{
|
||||
// Runs all necessary upgrade methods
|
||||
|
||||
@@ -24,18 +24,57 @@
|
||||
|
||||
|
||||
#include "InstrumentPlayHandle.h"
|
||||
#include "Instrument.h"
|
||||
#include "InstrumentTrack.h"
|
||||
#include "Engine.h"
|
||||
#include "AudioEngine.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
InstrumentPlayHandle::InstrumentPlayHandle( Instrument * instrument, InstrumentTrack* instrumentTrack ) :
|
||||
PlayHandle( Type::InstrumentPlayHandle ),
|
||||
m_instrument( instrument )
|
||||
InstrumentPlayHandle::InstrumentPlayHandle(Instrument * instrument, InstrumentTrack* instrumentTrack) :
|
||||
PlayHandle(Type::InstrumentPlayHandle),
|
||||
m_instrument(instrument)
|
||||
{
|
||||
setAudioPort( instrumentTrack->audioPort() );
|
||||
setAudioPort(instrumentTrack->audioPort());
|
||||
}
|
||||
|
||||
void InstrumentPlayHandle::play(sampleFrame * working_buffer)
|
||||
{
|
||||
InstrumentTrack * instrumentTrack = m_instrument->instrumentTrack();
|
||||
|
||||
// ensure that all our nph's have been processed first
|
||||
auto nphv = NotePlayHandle::nphsOfInstrumentTrack(instrumentTrack, true);
|
||||
|
||||
bool nphsLeft;
|
||||
do
|
||||
{
|
||||
nphsLeft = false;
|
||||
for (const NotePlayHandle * constNotePlayHandle : nphv)
|
||||
{
|
||||
if (constNotePlayHandle->state() != ThreadableJob::ProcessingState::Done &&
|
||||
!constNotePlayHandle->isFinished())
|
||||
{
|
||||
nphsLeft = true;
|
||||
NotePlayHandle * notePlayHandle = const_cast<NotePlayHandle *>(constNotePlayHandle);
|
||||
notePlayHandle->process();
|
||||
}
|
||||
}
|
||||
}
|
||||
while (nphsLeft);
|
||||
|
||||
m_instrument->play(working_buffer);
|
||||
|
||||
// Process the audio buffer that the instrument has just worked on...
|
||||
const fpp_t frames = Engine::audioEngine()->framesPerPeriod();
|
||||
instrumentTrack->processAudioBuffer(working_buffer, frames, nullptr);
|
||||
}
|
||||
|
||||
bool InstrumentPlayHandle::isFromTrack(const Track* track) const
|
||||
{
|
||||
return m_instrument->isFromTrack(track);
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
} // namespace lmms
|
||||
|
||||
@@ -88,6 +88,7 @@ void LfoController::updateValueBuffer()
|
||||
{
|
||||
m_phaseOffset = m_phaseModel.value() / 360.0;
|
||||
float phase = m_currentPhase + m_phaseOffset;
|
||||
float phasePrev = 0.0f;
|
||||
|
||||
// roll phase up until we're in sync with period counter
|
||||
m_bufferLastUpdated++;
|
||||
@@ -102,20 +103,45 @@ void LfoController::updateValueBuffer()
|
||||
ValueBuffer *amountBuffer = m_amountModel.valueBuffer();
|
||||
int amountInc = amountBuffer ? 1 : 0;
|
||||
float *amountPtr = amountBuffer ? &(amountBuffer->values()[ 0 ] ) : &amount;
|
||||
Oscillator::WaveShape waveshape = static_cast<Oscillator::WaveShape>(m_waveModel.value());
|
||||
|
||||
for( float& f : m_valueBuffer )
|
||||
{
|
||||
const float currentSample = m_sampleFunction != nullptr
|
||||
? m_sampleFunction( phase )
|
||||
: m_userDefSampleBuffer->userWaveSample( phase );
|
||||
float currentSample = 0;
|
||||
switch (waveshape)
|
||||
{
|
||||
case Oscillator::WaveShape::WhiteNoise:
|
||||
{
|
||||
if (absFraction(phase) < absFraction(phasePrev))
|
||||
{
|
||||
// Resample when phase period has completed
|
||||
m_heldSample = m_sampleFunction(phase);
|
||||
}
|
||||
currentSample = m_heldSample;
|
||||
break;
|
||||
}
|
||||
case Oscillator::WaveShape::UserDefined:
|
||||
{
|
||||
currentSample = m_userDefSampleBuffer->userWaveSample(phase);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (m_sampleFunction != nullptr)
|
||||
{
|
||||
currentSample = m_sampleFunction(phase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f = std::clamp(m_baseModel.value() + (*amountPtr * currentSample / 2.0f), 0.0f, 1.0f);
|
||||
|
||||
phasePrev = phase;
|
||||
phase += 1.0 / m_duration;
|
||||
amountPtr += amountInc;
|
||||
}
|
||||
|
||||
m_currentPhase = absFraction( phase - m_phaseOffset );
|
||||
m_currentPhase = absFraction(phase - m_phaseOffset);
|
||||
m_bufferLastUpdated = s_periods;
|
||||
}
|
||||
|
||||
|
||||
@@ -247,7 +247,15 @@ void SampleBuffer::update(bool keepSettings)
|
||||
const int fileSizeMax = 300; // MB
|
||||
const int sampleLengthMax = 90; // Minutes
|
||||
|
||||
bool fileLoadError = false;
|
||||
enum class FileLoadError
|
||||
{
|
||||
None,
|
||||
ReadPermissionDenied,
|
||||
TooLarge,
|
||||
Invalid
|
||||
};
|
||||
FileLoadError fileLoadError = FileLoadError::None;
|
||||
|
||||
if (m_audioFile.isEmpty() && m_origData != nullptr && m_origFrames > 0)
|
||||
{
|
||||
// TODO: reverse- and amplification-property is not covered
|
||||
@@ -271,31 +279,40 @@ void SampleBuffer::update(bool keepSettings)
|
||||
m_frames = 0;
|
||||
|
||||
const QFileInfo fileInfo(file);
|
||||
if (fileInfo.size() > fileSizeMax * 1024 * 1024)
|
||||
if (!fileInfo.isReadable())
|
||||
{
|
||||
fileLoadError = true;
|
||||
fileLoadError = FileLoadError::ReadPermissionDenied;
|
||||
}
|
||||
else if (fileInfo.size() > fileSizeMax * 1024 * 1024)
|
||||
{
|
||||
fileLoadError = FileLoadError::TooLarge;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use QFile to handle unicode file names on Windows
|
||||
QFile f(file);
|
||||
SNDFILE * sndFile;
|
||||
SNDFILE * sndFile = nullptr;
|
||||
SF_INFO sfInfo;
|
||||
sfInfo.format = 0;
|
||||
|
||||
if (f.open(QIODevice::ReadOnly) && (sndFile = sf_open_fd(f.handle(), SFM_READ, &sfInfo, false)))
|
||||
{
|
||||
f_cnt_t frames = sfInfo.frames;
|
||||
int rate = sfInfo.samplerate;
|
||||
if (frames / rate > sampleLengthMax * 60)
|
||||
{
|
||||
fileLoadError = true;
|
||||
fileLoadError = FileLoadError::TooLarge;
|
||||
}
|
||||
sf_close(sndFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileLoadError = FileLoadError::Invalid;
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
|
||||
if (!fileLoadError)
|
||||
if (fileLoadError == FileLoadError::None)
|
||||
{
|
||||
#ifdef LMMS_HAVE_OGGVORBIS
|
||||
// workaround for a bug in libsndfile or our libsndfile decoder
|
||||
@@ -322,7 +339,7 @@ void SampleBuffer::update(bool keepSettings)
|
||||
}
|
||||
}
|
||||
|
||||
if (m_frames == 0 || fileLoadError) // if still no frames, bail
|
||||
if (m_frames == 0 || fileLoadError != FileLoadError::None) // if still no frames, bail
|
||||
{
|
||||
// sample couldn't be decoded, create buffer containing
|
||||
// one sample-frame
|
||||
@@ -363,16 +380,35 @@ void SampleBuffer::update(bool keepSettings)
|
||||
}
|
||||
Oscillator::generateAntiAliasUserWaveTable(this);
|
||||
|
||||
if (fileLoadError)
|
||||
if (fileLoadError != FileLoadError::None)
|
||||
{
|
||||
QString title = tr("Fail to open file");
|
||||
QString message = tr("Audio files are limited to %1 MB "
|
||||
"in size and %2 minutes of playing time"
|
||||
).arg(fileSizeMax).arg(sampleLengthMax);
|
||||
QString message;
|
||||
|
||||
switch (fileLoadError)
|
||||
{
|
||||
case FileLoadError::None:
|
||||
// present just to avoid a compiler warning
|
||||
break;
|
||||
|
||||
case FileLoadError::ReadPermissionDenied:
|
||||
message = tr("Read permission denied");
|
||||
break;
|
||||
|
||||
case FileLoadError::TooLarge:
|
||||
message = tr("Audio files are limited to %1 MB "
|
||||
"in size and %2 minutes of playing time"
|
||||
).arg(fileSizeMax).arg(sampleLengthMax);
|
||||
break;
|
||||
|
||||
case FileLoadError::Invalid:
|
||||
message = tr("Invalid audio file");
|
||||
break;
|
||||
}
|
||||
|
||||
if (gui::getGUI() != nullptr)
|
||||
{
|
||||
QMessageBox::information(nullptr,
|
||||
title, message, QMessageBox::Ok);
|
||||
QMessageBox::information(nullptr, title, message, QMessageBox::Ok);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -143,23 +143,26 @@ void SampleClip::setSampleBuffer( SampleBuffer* sb )
|
||||
|
||||
|
||||
|
||||
void SampleClip::setSampleFile( const QString & _sf )
|
||||
void SampleClip::setSampleFile(const QString & sf)
|
||||
{
|
||||
int length;
|
||||
if ( _sf.isEmpty() )
|
||||
{ //When creating an empty sample clip make it a bar long
|
||||
float nom = Engine::getSong()->getTimeSigModel().getNumerator();
|
||||
float den = Engine::getSong()->getTimeSigModel().getDenominator();
|
||||
length = DefaultTicksPerBar * ( nom / den );
|
||||
}
|
||||
else
|
||||
{ //Otherwise set it to the sample's length
|
||||
m_sampleBuffer->setAudioFile( _sf );
|
||||
int length = 0;
|
||||
|
||||
if (!sf.isEmpty())
|
||||
{
|
||||
m_sampleBuffer->setAudioFile(sf);
|
||||
length = sampleLength();
|
||||
}
|
||||
changeLength(length);
|
||||
|
||||
setStartTimeOffset( 0 );
|
||||
if (length == 0)
|
||||
{
|
||||
//If there is no sample, make the clip a bar long
|
||||
float nom = Engine::getSong()->getTimeSigModel().getNumerator();
|
||||
float den = Engine::getSong()->getTimeSigModel().getDenominator();
|
||||
length = DefaultTicksPerBar * (nom / den);
|
||||
}
|
||||
|
||||
changeLength(length);
|
||||
setStartTimeOffset(0);
|
||||
|
||||
emit sampleChanged();
|
||||
emit playbackPositionChanged();
|
||||
|
||||
@@ -72,21 +72,20 @@ void MidiController::updateName()
|
||||
|
||||
|
||||
|
||||
void MidiController::processInEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset )
|
||||
void MidiController::processInEvent(const MidiEvent& event, const TimePos& time, f_cnt_t offset)
|
||||
{
|
||||
unsigned char controllerNum;
|
||||
switch( event.type() )
|
||||
switch(event.type())
|
||||
{
|
||||
case MidiControlChange:
|
||||
controllerNum = event.controllerNumber();
|
||||
|
||||
if( m_midiPort.inputController() == controllerNum + 1 &&
|
||||
( m_midiPort.inputChannel() == event.channel() + 1 ||
|
||||
m_midiPort.inputChannel() == 0 ) )
|
||||
if (m_midiPort.inputController() == controllerNum &&
|
||||
(m_midiPort.inputChannel() == event.channel() + 1 || m_midiPort.inputChannel() == 0))
|
||||
{
|
||||
unsigned char val = event.controllerValue();
|
||||
m_previousValue = m_lastValue;
|
||||
m_lastValue = (float)( val ) / 127.0f;
|
||||
m_lastValue = static_cast<float>(val) / 127.0f;
|
||||
emit valueChanged();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "MidiEventProcessor.h"
|
||||
#include "Note.h"
|
||||
#include "Song.h"
|
||||
#include "MidiController.h"
|
||||
|
||||
|
||||
namespace lmms
|
||||
@@ -54,8 +55,8 @@ MidiPort::MidiPort( const QString& name,
|
||||
m_mode( mode ),
|
||||
m_inputChannelModel( 0, 0, MidiChannelCount, this, tr( "Input channel" ) ),
|
||||
m_outputChannelModel( 1, 0, MidiChannelCount, this, tr( "Output channel" ) ),
|
||||
m_inputControllerModel( 0, 0, MidiControllerCount, this, tr( "Input controller" ) ),
|
||||
m_outputControllerModel( 0, 0, MidiControllerCount, this, tr( "Output controller" ) ),
|
||||
m_inputControllerModel(MidiController::NONE, MidiController::NONE, MidiControllerCount - 1, this, tr( "Input controller" )),
|
||||
m_outputControllerModel(MidiController::NONE, MidiController::NONE, MidiControllerCount - 1, this, tr( "Output controller" )),
|
||||
m_fixedInputVelocityModel( -1, -1, MidiMaxVelocity, this, tr( "Fixed input velocity" ) ),
|
||||
m_fixedOutputVelocityModel( -1, -1, MidiMaxVelocity, this, tr( "Fixed output velocity" ) ),
|
||||
m_fixedOutputNoteModel( -1, -1, MidiMaxKey, this, tr( "Fixed output note" ) ),
|
||||
@@ -436,4 +437,4 @@ void MidiPort::invalidateCilent()
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
} // namespace lmms
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDesktopServices>
|
||||
#include <QHBoxLayout>
|
||||
@@ -126,7 +125,7 @@ FileBrowser::FileBrowser(const QString & directories, const QString & filter,
|
||||
m_filterEdit->setPlaceholderText( tr("Search") );
|
||||
m_filterEdit->setClearButtonEnabled( true );
|
||||
connect( m_filterEdit, SIGNAL( textEdited( const QString& ) ),
|
||||
this, SLOT( filterItems( const QString& ) ) );
|
||||
this, SLOT( filterAndExpandItems( const QString& ) ) );
|
||||
|
||||
auto reload_btn = new QPushButton(embed::getIconPixmap("reload"), QString(), searchWidget);
|
||||
reload_btn->setToolTip( tr( "Refresh list" ) );
|
||||
@@ -145,53 +144,95 @@ FileBrowser::FileBrowser(const QString & directories, const QString & filter,
|
||||
auto filterFocusShortcut = new QShortcut(QKeySequence(QKeySequence::Find), this, SLOT(giveFocusToFilter()));
|
||||
filterFocusShortcut->setContext(Qt::WidgetWithChildrenShortcut);
|
||||
|
||||
m_previousFilterValue = "";
|
||||
|
||||
reloadTree();
|
||||
show();
|
||||
}
|
||||
|
||||
bool FileBrowser::filterItems( const QString & filter, QTreeWidgetItem * item )
|
||||
void FileBrowser::saveDirectoriesStates()
|
||||
{
|
||||
m_savedExpandedDirs = m_fileBrowserTreeWidget->expandedDirs();
|
||||
}
|
||||
|
||||
void FileBrowser::restoreDirectoriesStates()
|
||||
{
|
||||
// call with item=NULL to filter the entire tree
|
||||
expandItems(nullptr, m_savedExpandedDirs);
|
||||
}
|
||||
|
||||
bool FileBrowser::filterAndExpandItems(const QString & filter, QTreeWidgetItem * item)
|
||||
{
|
||||
// Call with item = nullptr to filter the entire tree
|
||||
|
||||
if (item == nullptr)
|
||||
{
|
||||
// First search character so need to save current expanded directories
|
||||
if (m_previousFilterValue.isEmpty())
|
||||
{
|
||||
saveDirectoriesStates();
|
||||
}
|
||||
|
||||
m_previousFilterValue = filter;
|
||||
}
|
||||
|
||||
if (filter.isEmpty())
|
||||
{
|
||||
// Restore previous expanded directories
|
||||
if (item == nullptr)
|
||||
{
|
||||
restoreDirectoriesStates();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool anyMatched = false;
|
||||
|
||||
int numChildren = item ? item->childCount() : m_fileBrowserTreeWidget->topLevelItemCount();
|
||||
for( int i = 0; i < numChildren; ++i )
|
||||
|
||||
for (int i = 0; i < numChildren; ++i)
|
||||
{
|
||||
QTreeWidgetItem * it = item ? item->child( i ) : m_fileBrowserTreeWidget->topLevelItem(i);
|
||||
|
||||
// is directory?
|
||||
if( it->childCount() )
|
||||
auto d = dynamic_cast<Directory*>(it);
|
||||
if (d)
|
||||
{
|
||||
// matches filter?
|
||||
if( it->text( 0 ).
|
||||
contains( filter, Qt::CaseInsensitive ) )
|
||||
if (it->text(0).contains(filter, Qt::CaseInsensitive))
|
||||
{
|
||||
// yes, then show everything below
|
||||
it->setHidden( false );
|
||||
filterItems( QString(), it );
|
||||
it->setHidden(false);
|
||||
it->setExpanded(true);
|
||||
filterAndExpandItems(QString(), it);
|
||||
anyMatched = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// only show if item below matches filter
|
||||
bool didMatch = filterItems( filter, it );
|
||||
it->setHidden( !didMatch );
|
||||
// Expanding is required when recursive to load in its contents, even if it's collapsed right afterward
|
||||
it->setExpanded(true);
|
||||
|
||||
bool didMatch = filterAndExpandItems(filter, it);
|
||||
it->setHidden(!didMatch);
|
||||
it->setExpanded(didMatch);
|
||||
anyMatched = anyMatched || didMatch;
|
||||
}
|
||||
}
|
||||
// a standard item (i.e. no file or directory item?)
|
||||
else if( it->type() == QTreeWidgetItem::Type )
|
||||
{
|
||||
// hide if there's any filter
|
||||
it->setHidden( !filter.isEmpty() );
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// file matches filter?
|
||||
bool didMatch = it->text( 0 ).
|
||||
contains( filter, Qt::CaseInsensitive );
|
||||
it->setHidden( !didMatch );
|
||||
anyMatched = anyMatched || didMatch;
|
||||
auto f = dynamic_cast<FileItem*>(it);
|
||||
if (f)
|
||||
{
|
||||
// File
|
||||
bool didMatch = it->text(0).contains(filter, Qt::CaseInsensitive);
|
||||
it->setHidden(!didMatch);
|
||||
anyMatched = anyMatched || didMatch;
|
||||
}
|
||||
|
||||
// A standard item (i.e. no file or directory item?)
|
||||
else
|
||||
{
|
||||
// Hide if there's any filter
|
||||
it->setHidden(!filter.isEmpty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,15 +242,20 @@ bool FileBrowser::filterItems( const QString & filter, QTreeWidgetItem * item )
|
||||
|
||||
void FileBrowser::reloadTree()
|
||||
{
|
||||
QList<QString> expandedDirs = m_fileBrowserTreeWidget->expandedDirs();
|
||||
const QString text = m_filterEdit->text();
|
||||
m_filterEdit->clear();
|
||||
if (m_filterEdit->text().isEmpty())
|
||||
{
|
||||
saveDirectoriesStates();
|
||||
}
|
||||
|
||||
m_fileBrowserTreeWidget->clear();
|
||||
|
||||
QStringList paths = m_directories.split('*');
|
||||
|
||||
if (m_showUserContent && !m_showUserContent->isChecked())
|
||||
{
|
||||
paths.removeAll(m_userDir);
|
||||
}
|
||||
|
||||
if (m_showFactoryContent && !m_showFactoryContent->isChecked())
|
||||
{
|
||||
paths.removeAll(m_factoryDir);
|
||||
@@ -222,9 +268,15 @@ void FileBrowser::reloadTree()
|
||||
addItems(path);
|
||||
}
|
||||
}
|
||||
expandItems(nullptr, expandedDirs);
|
||||
m_filterEdit->setText( text );
|
||||
filterItems( text );
|
||||
|
||||
if (m_filterEdit->text().isEmpty())
|
||||
{
|
||||
restoreDirectoriesStates();
|
||||
}
|
||||
else
|
||||
{
|
||||
filterAndExpandItems(m_filterEdit->text());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -240,12 +292,16 @@ void FileBrowser::expandItems(QTreeWidgetItem* item, QList<QString> expandedDirs
|
||||
{
|
||||
// Expanding is required when recursive to load in its contents, even if it's collapsed right afterward
|
||||
if (m_recurse) { d->setExpanded(true); }
|
||||
|
||||
d->setExpanded(expandedDirs.contains(d->fullName()));
|
||||
|
||||
if (m_recurse && it->childCount())
|
||||
{
|
||||
expandItems(it, expandedDirs);
|
||||
}
|
||||
}
|
||||
|
||||
it->setHidden(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,8 +472,6 @@ QList<QString> FileBrowserTreeWidget::expandedDirs( QTreeWidgetItem * item ) con
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void FileBrowserTreeWidget::keyPressEvent(QKeyEvent * ke )
|
||||
{
|
||||
// Shorter names for some commonly used properties of the event
|
||||
|
||||
@@ -84,7 +84,6 @@ SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) :
|
||||
|
||||
// setup line edit for changing sample track name
|
||||
m_nameLineEdit = new QLineEdit;
|
||||
m_nameLineEdit->setFont(pointSize<9>(m_nameLineEdit->font()));
|
||||
connect(m_nameLineEdit, SIGNAL(textChanged(const QString&)),
|
||||
this, SLOT(textChanged(const QString&)));
|
||||
|
||||
|
||||
@@ -523,7 +523,8 @@ void MidiClipView::paintEvent( QPaintEvent * )
|
||||
p.scale(width(), height() - distanceToTop - 2 * notesBorder);
|
||||
|
||||
// set colour based on mute status
|
||||
QColor noteFillColor = muted ? getMutedNoteFillColor() : getNoteFillColor();
|
||||
QColor noteFillColor = muted ? getMutedNoteFillColor().lighter(200)
|
||||
: (c.lightness() > 175 ? getNoteFillColor().darker(400) : getNoteFillColor());
|
||||
QColor noteBorderColor = muted ? getMutedNoteBorderColor()
|
||||
: ( m_clip->hasColor() ? c.lighter( 200 ) : getNoteBorderColor() );
|
||||
|
||||
|
||||
@@ -531,7 +531,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent )
|
||||
: POS(clickedNode)
|
||||
),
|
||||
level,
|
||||
true,
|
||||
clickedNode == tm.end(),
|
||||
mouseEvent->modifiers() & Qt::ControlModifier
|
||||
);
|
||||
|
||||
|
||||
@@ -130,7 +130,10 @@ QPixmap* PianoRoll::s_toolKnife = nullptr;
|
||||
|
||||
SimpleTextFloat * PianoRoll::s_textFloat = nullptr;
|
||||
|
||||
static std::array<QString, 12> s_noteStrings {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};
|
||||
static std::array<QString, 12> s_noteStrings {
|
||||
"C", "C\u266F / D\u266D", "D", "D\u266F / E\u266D", "E", "F", "F\u266F / G\u266D",
|
||||
"G", "G\u266F / A\u266D", "A", "A\u266F / B\u266D", "B"
|
||||
};
|
||||
|
||||
static QString getNoteString(int key)
|
||||
{
|
||||
@@ -1719,10 +1722,10 @@ void PianoRoll::mousePressEvent(QMouseEvent * me )
|
||||
const NoteVector & notes = m_midiClip->notes();
|
||||
|
||||
// will be our iterator in the following loop
|
||||
auto it = notes.begin() + notes.size() - 1;
|
||||
auto it = notes.rbegin();
|
||||
|
||||
// loop through whole note-vector...
|
||||
for( int i = 0; i < notes.size(); ++i )
|
||||
while (it != notes.rend())
|
||||
{
|
||||
Note *note = *it;
|
||||
TimePos len = note->length();
|
||||
@@ -1747,7 +1750,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me )
|
||||
{
|
||||
break;
|
||||
}
|
||||
--it;
|
||||
++it;
|
||||
}
|
||||
|
||||
// first check whether the user clicked in note-edit-
|
||||
@@ -1769,7 +1772,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me )
|
||||
Note * created_new_note = nullptr;
|
||||
// did it reach end of vector because
|
||||
// there's no note??
|
||||
if( it == notes.begin()-1 )
|
||||
if (it == notes.rend())
|
||||
{
|
||||
is_new_note = true;
|
||||
m_midiClip->addJournalCheckPoint();
|
||||
@@ -1816,8 +1819,8 @@ void PianoRoll::mousePressEvent(QMouseEvent * me )
|
||||
// reset it so that it can be used for
|
||||
// ops (move, resize) after this
|
||||
// code-block
|
||||
it = notes.begin();
|
||||
while( it != notes.end() && *it != created_new_note )
|
||||
it = notes.rbegin();
|
||||
while (it != notes.rend() && *it != created_new_note)
|
||||
{
|
||||
++it;
|
||||
}
|
||||
@@ -1933,7 +1936,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me )
|
||||
{
|
||||
// erase single note
|
||||
m_mouseDownRight = true;
|
||||
if( it != notes.begin()-1 )
|
||||
if (it != notes.rend())
|
||||
{
|
||||
m_midiClip->addJournalCheckPoint();
|
||||
m_midiClip->removeNote( *it );
|
||||
@@ -2513,7 +2516,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me )
|
||||
bool altPressed = me->modifiers() & Qt::AltModifier;
|
||||
// We iterate from last note in MIDI clip to the first,
|
||||
// chronologically
|
||||
auto it = notes.begin() + notes.size() - 1;
|
||||
auto it = notes.rbegin();
|
||||
for( int i = 0; i < notes.size(); ++i )
|
||||
{
|
||||
Note* n = *it;
|
||||
@@ -2556,7 +2559,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me )
|
||||
}
|
||||
|
||||
|
||||
--it;
|
||||
++it;
|
||||
}
|
||||
|
||||
// Emit MIDI clip has changed
|
||||
@@ -2575,10 +2578,10 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me )
|
||||
const NoteVector & notes = m_midiClip->notes();
|
||||
|
||||
// will be our iterator in the following loop
|
||||
auto it = notes.begin() + notes.size() - 1;
|
||||
auto it = notes.rbegin();
|
||||
|
||||
// loop through whole note-vector...
|
||||
for( int i = 0; i < notes.size(); ++i )
|
||||
while (it != notes.rend())
|
||||
{
|
||||
Note *note = *it;
|
||||
// and check whether the cursor is over an
|
||||
@@ -2591,12 +2594,12 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me )
|
||||
{
|
||||
break;
|
||||
}
|
||||
--it;
|
||||
++it;
|
||||
}
|
||||
|
||||
// did it reach end of vector because there's
|
||||
// no note??
|
||||
if( it != notes.begin()-1 )
|
||||
if (it != notes.rend())
|
||||
{
|
||||
Note *note = *it;
|
||||
// x coordinate of the right edge of the note
|
||||
|
||||
@@ -34,7 +34,6 @@ MidiPortMenu::MidiPortMenu( MidiPort::Mode _mode ) :
|
||||
ModelView( nullptr, this ),
|
||||
m_mode( _mode )
|
||||
{
|
||||
setFont( pointSize<9>( font() ) );
|
||||
connect( this, SIGNAL(triggered(QAction*)),
|
||||
this, SLOT(activatedPort(QAction*)));
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
AutoDetectMidiController( Model* parent ) :
|
||||
MidiController( parent ),
|
||||
m_detectedMidiChannel( 0 ),
|
||||
m_detectedMidiController( 0 )
|
||||
m_detectedMidiController(NONE)
|
||||
{
|
||||
updateName();
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
( m_midiPort.inputChannel() == 0 || m_midiPort.inputChannel() == event.channel() + 1 ) )
|
||||
{
|
||||
m_detectedMidiChannel = event.channel() + 1;
|
||||
m_detectedMidiController = event.controllerNumber() + 1;
|
||||
m_detectedMidiController = event.controllerNumber();
|
||||
m_detectedMidiPort = Engine::audioEngine()->midiClient()->sourcePortName( event );
|
||||
|
||||
emit valueChanged();
|
||||
@@ -152,7 +152,7 @@ ControllerConnectionDialog::ControllerConnectionDialog( QWidget * _parent,
|
||||
|
||||
m_midiControllerSpinBox = new LcdSpinBox( 3, m_midiGroupBox,
|
||||
tr( "Input controller" ) );
|
||||
m_midiControllerSpinBox->addTextForValue( 0, "---" );
|
||||
m_midiControllerSpinBox->addTextForValue(MidiController::NONE, "---" );
|
||||
m_midiControllerSpinBox->setLabel( tr( "CONTROLLER" ) );
|
||||
m_midiControllerSpinBox->move( 68, 24 );
|
||||
|
||||
|
||||
@@ -26,11 +26,12 @@
|
||||
#include <QUrl>
|
||||
#include <QListView>
|
||||
#include <QStandardPaths>
|
||||
#include <QStorageInfo>
|
||||
#include <QStringList>
|
||||
|
||||
#include "ConfigManager.h"
|
||||
#include "FileDialog.h"
|
||||
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
@@ -45,19 +46,38 @@ FileDialog::FileDialog( QWidget *parent, const QString &caption,
|
||||
|
||||
setOption( QFileDialog::DontUseNativeDialog );
|
||||
|
||||
// Add additional locations to the sidebar
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
QList<QUrl> urls;
|
||||
#else
|
||||
QList<QUrl> urls = sidebarUrls();
|
||||
urls << QUrl::fromLocalFile( QStandardPaths::writableLocation( QStandardPaths::DesktopLocation ) );
|
||||
// Find downloads directory
|
||||
QDir downloadDir( QDir::homePath() + "/Downloads" );
|
||||
if ( ! downloadDir.exists() )
|
||||
downloadDir.setPath(QStandardPaths::writableLocation( QStandardPaths::DownloadLocation ));
|
||||
if ( downloadDir.exists() )
|
||||
urls << QUrl::fromLocalFile( downloadDir.absolutePath() );
|
||||
#endif
|
||||
|
||||
urls << QUrl::fromLocalFile( QStandardPaths::writableLocation( QStandardPaths::MusicLocation ) );
|
||||
urls << QUrl::fromLocalFile( ConfigManager::inst()->workingDir() );
|
||||
QDir desktopDir;
|
||||
desktopDir.setPath(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
|
||||
if (desktopDir.exists())
|
||||
{
|
||||
urls << QUrl::fromLocalFile(desktopDir.absolutePath());
|
||||
}
|
||||
|
||||
QDir downloadDir(QDir::homePath() + "/Downloads");
|
||||
if (!downloadDir.exists())
|
||||
{
|
||||
downloadDir.setPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation));
|
||||
}
|
||||
if (downloadDir.exists())
|
||||
{
|
||||
urls << QUrl::fromLocalFile(downloadDir.absolutePath());
|
||||
}
|
||||
|
||||
QDir musicDir;
|
||||
musicDir.setPath(QStandardPaths::writableLocation(QStandardPaths::MusicLocation));
|
||||
if (musicDir.exists())
|
||||
{
|
||||
urls << QUrl::fromLocalFile(musicDir.absolutePath());
|
||||
}
|
||||
|
||||
urls << QUrl::fromLocalFile(ConfigManager::inst()->workingDir());
|
||||
|
||||
// Add `/Volumes` directory on OS X systems, this allows the user to browse
|
||||
// external disk drives.
|
||||
#ifdef LMMS_BUILD_APPLE
|
||||
@@ -66,6 +86,22 @@ FileDialog::FileDialog( QWidget *parent, const QString &caption,
|
||||
urls << QUrl::fromLocalFile( volumesDir.absolutePath() );
|
||||
#endif
|
||||
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
|
||||
// FileSystem types : https://www.javatpoint.com/linux-file-system
|
||||
QStringList usableFileSystems = {"ext", "ext2", "ext3", "ext4", "jfs", "reiserfs", "ntfs3", "fuse.sshfs", "fuseblk"};
|
||||
|
||||
for(QStorageInfo storage : QStorageInfo::mountedVolumes())
|
||||
{
|
||||
storage.refresh();
|
||||
|
||||
if (usableFileSystems.contains(QString(storage.fileSystemType()), Qt::CaseInsensitive) && storage.isValid() && storage.isReady())
|
||||
{
|
||||
urls << QUrl::fromLocalFile(storage.rootPath());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
setSidebarUrls(urls);
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,6 @@ TrackOperationsWidget::TrackOperationsWidget( TrackView * parent ) :
|
||||
"to begin a new drag'n'drop action." ).arg(UI_CTRL_KEY) );
|
||||
|
||||
auto toMenu = new QMenu(this);
|
||||
toMenu->setFont( pointSize<9>( toMenu->font() ) );
|
||||
connect( toMenu, SIGNAL(aboutToShow()), this, SLOT(updateMenu()));
|
||||
|
||||
|
||||
|
||||
@@ -70,7 +70,6 @@ ComboBox::ComboBox( QWidget * _parent, const QString & _name ) :
|
||||
}
|
||||
|
||||
setFont( pointSize<9>( font() ) );
|
||||
m_menu.setFont( pointSize<8>( m_menu.font() ) );
|
||||
|
||||
connect( &m_menu, SIGNAL(triggered(QAction*)),
|
||||
this, SLOT(setItem(QAction*)));
|
||||
|
||||
@@ -49,6 +49,7 @@ namespace lmms::gui
|
||||
|
||||
|
||||
LcdFloatSpinBox::LcdFloatSpinBox(int numWhole, int numFrac, const QString& name, QWidget* parent) :
|
||||
QWidget(parent),
|
||||
FloatModelView(new FloatModel(0, 0, 0, 0, nullptr, name, true), this),
|
||||
m_wholeDisplay(numWhole, parent, name, false),
|
||||
m_fractionDisplay(numFrac, parent, name, true),
|
||||
@@ -62,6 +63,7 @@ LcdFloatSpinBox::LcdFloatSpinBox(int numWhole, int numFrac, const QString& name,
|
||||
|
||||
|
||||
LcdFloatSpinBox::LcdFloatSpinBox(int numWhole, int numFrac, const QString& style, const QString& name, QWidget* parent) :
|
||||
QWidget(parent),
|
||||
FloatModelView(new FloatModel(0, 0, 0, 0, nullptr, name, true), this),
|
||||
m_wholeDisplay(numWhole, style, parent, name, false),
|
||||
m_fractionDisplay(numFrac, style, parent, name, true),
|
||||
@@ -101,6 +103,7 @@ void LcdFloatSpinBox::layoutSetup(const QString &style)
|
||||
outerLayout->setContentsMargins(0, 0, 0, 0);
|
||||
outerLayout->setSizeConstraint(QLayout::SetFixedSize);
|
||||
this->setLayout(outerLayout);
|
||||
this->setFixedHeight(32);
|
||||
}
|
||||
|
||||
|
||||
@@ -240,9 +243,9 @@ void LcdFloatSpinBox::paintEvent(QPaintEvent*)
|
||||
{
|
||||
p.setFont(pointSizeF(p.font(), 6.5));
|
||||
p.setPen(m_wholeDisplay.textShadowColor());
|
||||
p.drawText(width() / 2 - horizontalAdvance(p.fontMetrics(), m_label) / 2 + 1, height(), m_label);
|
||||
p.drawText(width() / 2 - p.fontMetrics().boundingRect(m_label).width() / 2 + 1, height(), m_label);
|
||||
p.setPen(m_wholeDisplay.textColor());
|
||||
p.drawText(width() / 2 - horizontalAdvance(p.fontMetrics(), m_label) / 2, height() - 1, m_label);
|
||||
p.drawText(width() / 2 - p.fontMetrics().boundingRect(m_label).width() / 2, height() - 1, m_label);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -570,6 +570,10 @@ f_cnt_t InstrumentTrack::beatLen( NotePlayHandle * _n ) const
|
||||
|
||||
void InstrumentTrack::playNote( NotePlayHandle* n, sampleFrame* workingBuffer )
|
||||
{
|
||||
// Note: under certain circumstances the working buffer is a nullptr.
|
||||
// These cases are triggered in PlayHandle::doProcessing when the play method is called with a nullptr.
|
||||
// TODO: Find out if we can skip processing at a higher level if the buffer is nullptr.
|
||||
|
||||
// arpeggio- and chord-widget has to do its work -> adding sub-notes
|
||||
// for chords/arpeggios
|
||||
m_noteStacking.processNote( n );
|
||||
@@ -579,6 +583,15 @@ void InstrumentTrack::playNote( NotePlayHandle* n, sampleFrame* workingBuffer )
|
||||
{
|
||||
// all is done, so now lets play the note!
|
||||
m_instrument->playNote( n, workingBuffer );
|
||||
|
||||
// This is effectively the same as checking if workingBuffer is not a nullptr.
|
||||
// Calling processAudioBuffer with a nullptr leads to crashes. Hence the check.
|
||||
if (n->usesBuffer())
|
||||
{
|
||||
const fpp_t frames = n->framesLeftForCurrentPeriod();
|
||||
const f_cnt_t offset = n->noteOffset();
|
||||
processAudioBuffer(workingBuffer, frames + offset, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user