Merge remote-tracking branch 'upstream/master' into refactor-samplebuffer

This commit is contained in:
sakertooth
2023-09-25 21:04:44 -04:00
58 changed files with 520 additions and 278 deletions

6
.gitmodules vendored
View File

@@ -40,9 +40,9 @@
[submodule "plugins/CarlaBase/carla"]
path = plugins/CarlaBase/carla
url = https://github.com/falktx/carla
[submodule "plugins/Sid/resid"]
path = plugins/Sid/resid
url = https://github.com/simonowen/resid
[submodule "plugins/Sid/resid/resid"]
path = plugins/Sid/resid/resid
url = https://github.com/libsidplayfp/resid
[submodule "src/3rdparty/jack2"]
path = src/3rdparty/jack2
url = https://github.com/jackaudio/jack2

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)
@@ -70,6 +77,7 @@ OPTION(WANT_SOUNDIO "Include libsoundio support" ON)
OPTION(WANT_SDL "Include SDL (Simple DirectMedia Layer) support" ON)
OPTION(WANT_SF2 "Include SoundFont2 player plugin" ON)
OPTION(WANT_GIG "Include GIG player plugin" ON)
option(WANT_SID "Include Sid instrument" ON)
OPTION(WANT_STK "Include Stk (Synthesis Toolkit) support" ON)
OPTION(WANT_SWH "Include Steve Harris's LADSPA plugins" ON)
OPTION(WANT_TAP "Include Tom's Audio Processing LADSPA plugins" ON)
@@ -204,6 +212,13 @@ CHECK_CXX_SOURCE_COMPILES(
LMMS_HAVE_SF_COMPLEVEL
)
# check for perl
if(LMMS_BUILD_APPLE)
# Prefer system perl over Homebrew, MacPorts, etc
set(Perl_ROOT "/usr/bin")
endif()
find_package(Perl)
IF(WANT_LV2)
IF(PKG_CONFIG_FOUND)
PKG_CHECK_MODULES(LV2 lv2)
@@ -266,8 +281,12 @@ ELSE(WANT_CMT)
ENDIF(WANT_CMT)
IF(WANT_SWH)
SET(LMMS_HAVE_SWH TRUE)
SET(STATUS_SWH "OK")
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)
@@ -333,6 +352,16 @@ IF(WANT_SDL AND NOT LMMS_HAVE_SDL2)
ENDIF()
ENDIF()
# check for Sid
if(WANT_SID)
if(PERL_FOUND)
set(LMMS_HAVE_SID TRUE)
set(STATUS_SID "OK")
else()
set(STATUS_SID "not found, please install perl if you require the Sid instrument")
endif()
endif()
# check for Stk
IF(WANT_STK)
FIND_PACKAGE(STK)
@@ -651,6 +680,9 @@ 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
@@ -797,6 +829,7 @@ MESSAGE(
"* ZynAddSubFX instrument : ${STATUS_ZYN}\n"
"* Carla Patchbay & Rack : ${STATUS_CARLA}\n"
"* SoundFont2 player : ${STATUS_FLUIDSYNTH}\n"
"* Sid instrument : ${STATUS_SID}\n"
"* Stk Mallets : ${STATUS_STK}\n"
"* VST-instrument hoster : ${STATUS_VST}\n"
"* VST-effect hoster : ${STATUS_VST}\n"

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;
std::shared_ptr<const 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

@@ -79,8 +79,8 @@ public:
void setIsPlaying(bool isPlaying);
public slots:
void setSampleBuffer(SampleBuffer* sb);
void setSampleFile( const QString & _sf );
void setSampleBuffer( lmms::SampleBuffer* sb );
void setSampleFile( const QString & sf );
void updateLength();
void toggleRecord();
void playbackPositionChanged();

View File

@@ -172,9 +172,6 @@ void AudioFileProcessor::playNote( NotePlayHandle * _n,
static_cast<Sample::Loop>(m_loopModel.value())))
{
applyRelease( _working_buffer, _n );
instrumentTrack()->processAudioBuffer( _working_buffer,
frames + offset, _n );
emit isPlaying((static_cast<Sample::PlaybackState*>(_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

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

@@ -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() ? Sample::Loop::On : Sample::Loop::Off))
{
applyRelease( _working_buffer, _n );
instrumentTrack()->processAudioBuffer( _working_buffer,
frames + offset, _n );
}
else
{
@@ -436,7 +434,7 @@ namespace gui
PatmanView::PatmanView( Instrument * _instrument, QWidget * _parent ) :
InstrumentViewFixedSize( _instrument, _parent ),
m_pi( nullptr )
m_pi(castModel<PatmanInstrument>())
{
setAutoFillBackground( true );
QPalette pal;
@@ -477,7 +475,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

@@ -1,51 +1,14 @@
INCLUDE(BuildPlugin)
INCLUDE_DIRECTORIES(resid)
if(NOT LMMS_HAVE_SID)
return()
endif()
BUILD_PLUGIN(sid
SidInstrument.cpp
SidInstrument.h
resid/envelope.h
resid/extfilt.h
resid/filter.h
resid/pot.h
resid/siddefs.h
resid/sid.h
resid/spline.h
resid/voice.h
resid/wave.h
resid/envelope.cc
resid/extfilt.cc
resid/filter.cc
resid/pot.cc
resid/sid.cc
resid/version.cc
resid/voice.cc
resid/wave6581_PS_.cc
resid/wave6581_PST.cc
resid/wave6581_P_T.cc
resid/wave6581__ST.cc
resid/wave8580_PS_.cc
resid/wave8580_PST.cc
resid/wave8580_P_T.cc
resid/wave8580__ST.cc
resid/wave.cc
MOCFILES SidInstrument.h
EMBEDDED_RESOURCES *.png)
# Parse VERSION
FILE(READ "resid/CMakeLists.txt" lines)
STRING(REGEX MATCH "set\\(MAJOR_VER [A-Za-z0-9_]*\\)" MAJOR_RAW ${lines})
STRING(REGEX MATCH "set\\(MINOR_VER [A-Za-z0-9_]*\\)" MINOR_RAW ${lines})
STRING(REGEX MATCH "set\\(PATCH_VER [A-Za-z0-9_]*\\)" PATCH_RAW ${lines})
SEPARATE_ARGUMENTS(MAJOR_RAW)
SEPARATE_ARGUMENTS(MINOR_RAW)
SEPARATE_ARGUMENTS(PATCH_RAW)
LIST(GET MAJOR_RAW 1 MAJOR_RAW)
LIST(GET MINOR_RAW 1 MINOR_RAW)
LIST(GET PATCH_RAW 1 PATCH_RAW)
STRING(REPLACE ")" "" MAJOR_VER "${MAJOR_RAW}")
STRING(REPLACE ")" "" MINOR_VER "${MINOR_RAW}")
STRING(REPLACE ")" "" PATCH_VER "${PATCH_RAW}")
TARGET_COMPILE_DEFINITIONS(sid PRIVATE VERSION="${MAJOR_VER}.${MINOR_VER}.${PATCH_VER}")
add_subdirectory(resid)
target_link_libraries(sid resid)

View File

@@ -239,7 +239,7 @@ f_cnt_t SidInstrument::desiredReleaseFrames() const
static int sid_fillbuffer(unsigned char* sidreg, SID *sid, int tdelta, short *ptr, int samples)
static int sid_fillbuffer(unsigned char* sidreg, reSID::SID *sid, int tdelta, short *ptr, int samples)
{
int tdelta2;
int result;
@@ -302,9 +302,9 @@ void SidInstrument::playNote( NotePlayHandle * _n,
if (!_n->m_pluginData)
{
SID *sid = new SID();
sid->set_sampling_parameters( clockrate, SAMPLE_FAST, samplerate );
sid->set_chip_model( MOS8580 );
auto sid = new reSID::SID();
sid->set_sampling_parameters(clockrate, reSID::SAMPLE_FAST, samplerate);
sid->set_chip_model(reSID::MOS8580);
sid->enable_filter( true );
sid->reset();
_n->m_pluginData = sid;
@@ -312,7 +312,7 @@ void SidInstrument::playNote( NotePlayHandle * _n,
const fpp_t frames = _n->framesLeftForCurrentPeriod();
const f_cnt_t offset = _n->noteOffset();
SID *sid = static_cast<SID *>( _n->m_pluginData );
auto sid = static_cast<reSID::SID*>(_n->m_pluginData);
int delta_t = clockrate * frames / samplerate + 4;
// avoid variable length array for msvc compat
auto buf = reinterpret_cast<short*>(_working_buffer + offset);
@@ -325,20 +325,20 @@ void SidInstrument::playNote( NotePlayHandle * _n,
if( (ChipModel)m_chipModel.value() == ChipModel::MOS6581 )
{
sid->set_chip_model( MOS6581 );
sid->set_chip_model(reSID::MOS6581);
}
else
{
sid->set_chip_model( MOS8580 );
sid->set_chip_model(reSID::MOS8580);
}
// voices
reg8 data8 = 0;
reg8 data16 = 0;
reg8 base = 0;
reSID::reg8 data8 = 0;
reSID::reg16 data16 = 0;
size_t base = 0;
float freq = 0.0;
float note = 0.0;
for( reg8 i = 0 ; i < 3 ; ++i )
for (size_t i = 0; i < 3; ++i)
{
base = i*7;
// freq ( Fn = Fout / Fclk * 16777216 ) + coarse detuning
@@ -429,8 +429,6 @@ void SidInstrument::playNote( NotePlayHandle * _n,
_working_buffer[frame+offset][ch] = s;
}
}
instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
}
@@ -438,7 +436,7 @@ void SidInstrument::playNote( NotePlayHandle * _n,
void SidInstrument::deleteNotePluginData( NotePlayHandle * _n )
{
delete static_cast<SID *>( _n->m_pluginData );
delete static_cast<reSID::SID*>(_n->m_pluginData);
}

View File

@@ -0,0 +1,68 @@
# These are the defaults
set(RESID_INLINING 1)
set(RESID_INLINE inline)
set(RESID_BRANCH_HINTS 1)
set(NEW_8580_FILTER 0)
set(HAVE_BOOL 1)
set(HAVE_LOG1P 1)
if(CMAKE_CXX_COMPILER_ID MATCHES "GCC|Clang")
set(HAVE_BUILTIN_EXPECT 1)
else()
set(HAVE_BUILTIN_EXPECT 0)
endif()
configure_file(resid/siddefs.h.in resid/siddefs.h @ONLY)
add_library(resid_objects OBJECT
resid/sid.cc
resid/voice.cc
resid/wave.cc
resid/envelope.cc
resid/filter.cc
resid/dac.cc
resid/extfilt.cc
resid/pot.cc
resid/version.cc
)
target_include_directories(resid_objects PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/resid"
"${CMAKE_CURRENT_BINARY_DIR}/resid"
)
target_compile_definitions(resid_objects PUBLIC VERSION="1.0")
set(RESID_WAVES
wave6581_PST
wave6581_PS_
wave6581_P_T
wave6581__ST
wave8580_PST
wave8580_PS_
wave8580_P_T
wave8580__ST
)
set(RESID_SAMP2SRC_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/resid/samp2src.pl)
foreach(WAVE_DATA IN LISTS RESID_WAVES)
set(WAVE_DATA_IN ${CMAKE_CURRENT_SOURCE_DIR}/resid/${WAVE_DATA}.dat)
set(WAVE_SRC_OUT ${CMAKE_CURRENT_BINARY_DIR}/resid/${WAVE_DATA}.h)
set(WAVE_COMMAND
"${PERL_EXECUTABLE}"
"${RESID_SAMP2SRC_SCRIPT}"
"${WAVE_DATA}"
"${WAVE_DATA_IN}"
"${WAVE_SRC_OUT}"
)
add_custom_command(OUTPUT ${WAVE_SRC_OUT} COMMAND ${WAVE_COMMAND} VERBATIM)
target_sources(resid_objects PUBLIC ${WAVE_SRC_OUT})
endforeach()
# TODO CMake 3.12: Use target_link_libraries() to propagate usage requirements directly to sid plugin
add_library(resid INTERFACE)
target_sources(resid INTERFACE $<TARGET_OBJECTS:resid_objects>)
get_target_property(resid_includes resid_objects INCLUDE_DIRECTORIES)
target_include_directories(resid INTERFACE ${resid_includes})

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

@@ -387,8 +387,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,21 +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 )
// TODO C++20: Deprecated, use std::atomic<std::shared_ptr> instead
: Oscillator::userWaveSample(std::atomic_load(&m_userDefSampleBuffer).get(), 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 = Oscillator::userWaveSample(std::atomic_load(&m_userDefSampleBuffer).get(), 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

@@ -196,4 +196,4 @@ auto SampleBuffer::empty() const -> bool
return m_data.empty();
}
} // namespace lmms
} // namespace lmms

View File

@@ -137,25 +137,29 @@ 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
auto buffer = gui::SampleLoader::createBufferFromFile(_sf);
int length = 0;
if (!sf.isEmpty())
{
//Otherwise set it to the sample's length
auto buffer = gui::SampleLoader::createBufferFromFile(sf);
// TODO C++20: Deprecated, use std::atomic<std::shared_ptr> instead
std::atomic_store(&m_sample, std::make_shared<Sample>(std::move(buffer)));
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

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