Merge remote-tracking branch 'upstream/master' into dynamic-effect-dialog-merge_upstream_20230923

This commit is contained in:
Michael Gregorius
2023-09-23 10:18:57 +02:00
57 changed files with 506 additions and 241 deletions

View File

@@ -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(

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -86,6 +86,7 @@ protected:
sample_t (*m_sampleFunction)( const float );
private:
float m_heldSample;
SampleBuffer * m_userDefSampleBuffer;
protected slots:

View File

@@ -48,6 +48,7 @@ class MidiController : public Controller, public MidiEventProcessor
{
Q_OBJECT
public:
static constexpr int NONE = -1;
MidiController( Model * _parent );
~MidiController() override = default;

View File

@@ -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();

View File

@@ -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

View File

@@ -307,8 +307,6 @@ void BitInvader::playNote( NotePlayHandle * _n,
}
applyRelease( _working_buffer, _n );
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
}

View File

@@ -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)

View File

@@ -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));

View File

@@ -419,7 +419,6 @@ void FreeBoyInstrument::playNote(NotePlayHandle* nph, sampleFrame* workingBuffer
}
framesLeft -= count;
}
instrumentTrack()->processAudioBuffer(workingBuffer, frames + offset, nph);
}

View File

@@ -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 );
}

View File

@@ -197,8 +197,6 @@ void KickerInstrument::playNote( NotePlayHandle * _n,
_working_buffer[f+offset][1] *= fac;
}
}
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
}

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

View File

@@ -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
}

View File

@@ -197,8 +197,6 @@ void Lv2Instrument::play(sampleFrame *buf)
copyModelsToLmms();
copyBuffersToLmms(buf, fpp);
instrumentTrack()->processAudioBuffer(buf, fpp, nullptr);
}

View File

@@ -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 )

View File

@@ -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 );
}

View File

@@ -412,10 +412,6 @@ void OpulenzInstrument::play( sampleFrame * _working_buffer )
}
}
emulatorMutex.unlock();
// Throw the data to the track...
instrumentTrack()->processAudioBuffer( _working_buffer, frameCount, nullptr );
}

View File

@@ -312,8 +312,6 @@ void OrganicInstrument::playNote( NotePlayHandle * _n,
}
// -- --
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
}

View File

@@ -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 );
}

View File

@@ -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 );
}

View File

@@ -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 );
}

View File

@@ -429,8 +429,6 @@ void SidInstrument::playNote( NotePlayHandle * _n,
_working_buffer[frame+offset][ch] = s;
}
}
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
}

View File

@@ -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 );
}

View File

@@ -380,8 +380,6 @@ void TripleOscillator::playNote( NotePlayHandle * _n,
applyFadeIn(_working_buffer, _n);
applyRelease( _working_buffer, _n );
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
}

View File

@@ -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();
}

View File

@@ -251,8 +251,6 @@ void Vibed::playNote(NotePlayHandle* n, sampleFrame* workingBuffer)
}
}
}
instrumentTrack()->processAudioBuffer(workingBuffer, frames + offset, n);
}
void Vibed::deleteNotePluginData(NotePlayHandle* n)

View File

@@ -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++ )
{

View File

@@ -445,8 +445,6 @@ void WatsynInstrument::playNote( NotePlayHandle * _n,
}
applyRelease( _working_buffer, _n );
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
}

View File

@@ -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) {

View File

@@ -341,7 +341,6 @@ void ZynAddSubFxInstrument::play( sampleFrame * _buf )
m_plugin->processAudio( _buf );
}
m_pluginMutex.unlock();
instrumentTrack()->processAudioBuffer( _buf, Engine::audioEngine()->framesPerPeriod(), nullptr );
}

View File

@@ -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 );

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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
{

View File

@@ -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();

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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&)));

View File

@@ -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() );

View File

@@ -531,7 +531,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent )
: POS(clickedNode)
),
level,
true,
clickedNode == tm.end(),
mouseEvent->modifiers() & Qt::ControlModifier
);

View File

@@ -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

View File

@@ -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*)));
}

View File

@@ -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 );

View File

@@ -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);
}

View File

@@ -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()));

View File

@@ -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*)));

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}