Merge pull request #7 from LMMS/master

Master
This commit is contained in:
Lost Robot
2019-05-30 18:55:33 -06:00
committed by GitHub
49 changed files with 712 additions and 149 deletions

View File

@@ -1,2 +1,3 @@
314ef4af137903dfb13e8c3ef1e6ea56cfdb23808d52ec4f5f50e288c73610c5 pbuilder_0.229.1_all.deb
fa82aa8ed3055c6f6330104deedf080b26778295e589426d4c4dd0f2c2a5defa debootstrap_1.0.95_all.deb
2ef4c09f7841b72f93412803ddd142f72658536dbfabe00e449eb548f432f3f8 debian-archive-keyring_2017.7ubuntu1_all.deb

View File

@@ -2,15 +2,16 @@
set -e
sudo apt-get install -y \
debian-archive-keyring \
dpkg \
pbuilder
# work around a pbuilder bug which breaks ccache
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=666525
# and also missing signing keys in Trusty's debian-archive-keyring
cd /tmp
wget http://archive.ubuntu.com/ubuntu/pool/main/p/pbuilder/pbuilder_0.229.1_all.deb
wget http://archive.ubuntu.com/ubuntu/pool/main/d/debootstrap/debootstrap_1.0.95_all.deb
wget http://archive.ubuntu.com/ubuntu/pool/universe/d/debian-archive-keyring/debian-archive-keyring_2017.7ubuntu1_all.deb
sha256sum -c "$TRAVIS_BUILD_DIR/.travis/debian_pkgs.sha256"
sudo dpkg -i pbuilder_0.229.1_all.deb debootstrap_1.0.95_all.deb
sudo dpkg -i pbuilder_0.229.1_all.deb debootstrap_1.0.95_all.deb debian-archive-keyring_2017.7ubuntu1_all.deb
cd "$OLDPWD"

View File

@@ -157,7 +157,7 @@ SET(QT_LIBRARIES
Qt5::Xml
)
IF(LMMS_BUILD_LINUX)
IF(LMMS_BUILD_LINUX AND WANT_VST)
FIND_PACKAGE(Qt5 COMPONENTS X11Extras REQUIRED)
LIST(APPEND QT_LIBRARIES Qt5::X11Extras)
ENDIF()

View File

@@ -74,8 +74,10 @@ else
success "Downloaded $LINUXDEPLOYQT"
# Extract AppImage and replace LINUXDEPLOYQT variable with extracted binary
# to support systems without fuse
# Also, we need to set LD_LIBRARY_PATH, but linuxdepoyqt's AppRun unsets it
# See https://github.com/probonopd/linuxdeployqt/pull/370/
"$LINUXDEPLOYQT" --appimage-extract > /dev/null 2>&1
LINUXDEPLOYQT="squashfs-root/AppRun"
LINUXDEPLOYQT="squashfs-root/usr/bin/linuxdeployqt"
success "Extracted $APPIMAGETOOL"
fi

View File

@@ -33,6 +33,7 @@
#include "MidiTime.h"
#include "ValueBuffer.h"
#include "MemoryManager.h"
#include "ModelVisitor.h"
// simple way to map a property of a view to a model
#define mapPropertyFromModelPtr(type,getfunc,setfunc,modelname) \
@@ -59,6 +60,11 @@
modelname.setValue( (float) val ); \
}
// use this to make subclasses visitable
#define MODEL_IS_VISITABLE \
void accept(ModelVisitor& v) override { v.visit(*this); } \
void accept(ConstModelVisitor& v) const override { v.visit(*this); }
class ControllerConnection;
@@ -68,6 +74,7 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject
Q_OBJECT
MM_OPERATORS
public:
typedef QVector<AutomatableModel *> AutoModelVector;
enum ScaleType
@@ -80,6 +87,35 @@ public:
virtual ~AutomatableModel();
// Implement those by using the MODEL_IS_VISITABLE macro
virtual void accept(ModelVisitor& v) = 0;
virtual void accept(ConstModelVisitor& v) const = 0;
public:
/**
@brief Return this class casted to Target
@test AutomatableModelTest.cpp
@param doThrow throw an assertion if the cast fails, instead of
returning a nullptr
@return the casted class if Target is the exact or a base class of
*this, nullptr otherwise
*/
template<class Target>
Target* dynamicCast(bool doThrow = false)
{
DCastVisitor<Target> vis; accept(vis);
if (doThrow && !vis.result) { Q_ASSERT(false); }
return vis.result;
}
//! const overload, see overloaded function
template<class Target>
const Target* dynamicCast(bool doThrow = false) const
{
ConstDCastVisitor<Target> vis; accept(vis);
if (doThrow && !vis.result) { Q_ASSERT(false); }
return vis.result;
}
bool isAutomated() const;
bool isAutomatedOrControlled() const
@@ -283,6 +319,22 @@ protected:
private:
// dynamicCast implementation
template<class Target>
struct DCastVisitor : public ModelVisitor
{
Target* result = nullptr;
void visit(Target& tar) { result = &tar; }
};
// dynamicCast implementation
template<class Target>
struct ConstDCastVisitor : public ConstModelVisitor
{
const Target* result = nullptr;
void visit(const Target& tar) { result = &tar; }
};
static bool mustQuoteName(const QString &name);
virtual void saveSettings( QDomDocument& doc, QDomElement& element )
@@ -382,6 +434,7 @@ public:
class LMMS_EXPORT FloatModel : public TypedAutomatableModel<float>
{
Q_OBJECT
MODEL_IS_VISITABLE
public:
FloatModel( float val = 0, float min = 0, float max = 0, float step = 0,
Model * parent = NULL,
@@ -399,6 +452,7 @@ public:
class LMMS_EXPORT IntModel : public TypedAutomatableModel<int>
{
Q_OBJECT
MODEL_IS_VISITABLE
public:
IntModel( int val = 0, int min = 0, int max = 0,
Model* parent = NULL,
@@ -414,6 +468,7 @@ public:
class LMMS_EXPORT BoolModel : public TypedAutomatableModel<bool>
{
Q_OBJECT
MODEL_IS_VISITABLE
public:
BoolModel( const bool val = false,
Model* parent = NULL,

View File

@@ -36,6 +36,7 @@
class LMMS_EXPORT ComboBoxModel : public IntModel
{
Q_OBJECT
MODEL_IS_VISITABLE
public:
ComboBoxModel( Model* parent = NULL,
const QString& displayName = QString(),

View File

@@ -161,6 +161,13 @@ public:
protected:
/**
Effects should call this at the end of audio processing
If the setting "Keep effects running even without input" is disabled,
after "decay" ms of a signal below "gate", the effect is turned off
and won't be processed again until it receives new audio input
*/
void checkGate( double _out_sum );
virtual PluginView * instantiateView( QWidget * );

View File

@@ -55,7 +55,7 @@ public:
private slots:
void reloadTree( void );
void expandItems( QTreeWidgetItem * item=NULL );
void expandItems( QTreeWidgetItem * item=NULL, QList<QString> expandedDirs = QList<QString>() );
// call with item=NULL to filter the entire tree
bool filterItems( const QString & filter, QTreeWidgetItem * item=NULL );
void giveFocusToFilter();
@@ -87,6 +87,10 @@ public:
FileBrowserTreeWidget( QWidget * parent );
virtual ~FileBrowserTreeWidget() = default;
//! This method returns a QList with paths (QString's) of all directories
//! that are expanded in the tree.
QList<QString> expandedDirs( QTreeWidgetItem * item = nullptr ) const;
protected:
virtual void contextMenuEvent( QContextMenuEvent * e );

View File

@@ -44,13 +44,18 @@ class LMMS_EXPORT Graph : public QWidget, public ModelView
public:
enum graphStyle
{
NearestStyle,
LinearStyle,
LinearNonCyclicStyle,
BarStyle,
NearestStyle, //!< draw as stairs
LinearStyle, //!< connect each 2 samples with a line, with wrapping
LinearNonCyclicStyle, //!< LinearStyle without wrapping
BarStyle, //!< draw thick bars
NumGraphStyles
};
/**
* @brief Constructor
* @param _width Pixel width of widget
* @param _height Pixel height of widget
*/
Graph( QWidget * _parent, graphStyle _style = Graph::LinearStyle,
int _width = 132,
int _height = 104
@@ -111,10 +116,24 @@ private:
} ;
/**
@brief 2 dimensional function plot
Function plot graph with discrete x scale and continous y scale
This makes it possible to display "#x" samples
*/
class LMMS_EXPORT graphModel : public Model
{
Q_OBJECT
public:
/**
* @brief Constructor
* @param _min Minimum y value to display
* @param _max Maximum y value to display
* @param _size Number of samples (e.g. x value)
* @param _step Step size on y axis where values snap to, or 0.0f
* for "no snapping"
*/
graphModel( float _min,
float _max,
int _size,
@@ -146,14 +165,21 @@ public:
return( m_samples.data() );
}
void convolve(const float *convolution, const int convolutionLength, const int centerOffset);
//! Make cyclic convolution
//! @param convolution Samples to convolve with
//! @param convolutionLength Number of samples to take for each sum
//! @param centerOffset Offset for resulting values
void convolve(const float *convolution,
const int convolutionLength, const int centerOffset);
public slots:
//! Set range of y values
void setRange( float _min, float _max );
void setLength( int _size );
//! Update one sample
void setSampleAt( int x, float val );
//! Update samples array
void setSamples( const float * _value );
void setWaveToSine();

View File

@@ -431,6 +431,9 @@ protected slots:
private:
virtual void modelChanged();
void viewInstrumentInDirection(int d);
//! adjust size of any child widget of the main tab
//! required to keep the old look when using a variable sized tab widget
void adjustTabSize(QWidget *w);
InstrumentTrack * m_track;
InstrumentTrackView * m_itv;

64
include/ModelVisitor.h Normal file
View File

@@ -0,0 +1,64 @@
/*
* ModelVisitor.h - visitors for automatable models
*
* Copyright (c) 2019-2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef MODELVISITOR_H
#define MODELVISITOR_H
class AutomatableModel;
class BoolModel;
class IntModel;
class FloatModel;
class ComboBoxModel;
class TempoSyncKnobModel;
class ModelVisitor
{
template<class ParentType = AutomatableModel, class ModelType>
void up(ModelType& m) { visit(static_cast<ParentType&>(m)); }
public:
virtual void visit(AutomatableModel& ) {}
virtual void visit(BoolModel& m);
virtual void visit(IntModel& m);
virtual void visit(FloatModel& m);
virtual void visit(ComboBoxModel& m);
virtual void visit(TempoSyncKnobModel& m);
virtual ~ModelVisitor();
};
class ConstModelVisitor
{
template<class ParentType = AutomatableModel, class ModelType>
void up(const ModelType& m) {
visit(static_cast<const ParentType&>(m)); }
public:
virtual void visit(const AutomatableModel& ) {}
virtual void visit(const BoolModel& m);
virtual void visit(const IntModel& m);
virtual void visit(const FloatModel& m);
virtual void visit(const ComboBoxModel& m);
virtual void visit(const TempoSyncKnobModel& m);
virtual ~ConstModelVisitor();
};
#endif // MODELVISITOR_H

View File

@@ -303,6 +303,7 @@ private:
NotePlayHandleList m_subNotes; // used for chords and arpeggios
volatile bool m_released; // indicates whether note is released
bool m_releaseStarted;
bool m_hasMidiNote;
bool m_hasParent; // indicates whether note has parent
NotePlayHandle * m_parent; // parent note
bool m_hadChildren;

View File

@@ -296,6 +296,8 @@ private:
void testPlayNote( Note * n );
void testPlayKey( int _key, int _vol, int _pan );
void pauseTestNotes(bool pause = true );
void playChordNotes(int key, int velocity=-1);
void pauseChordNotes(int key);
QList<int> getAllOctavesForKey( int keyToMirror ) const;

66
include/PluginIssue.h Normal file
View File

@@ -0,0 +1,66 @@
/*
* PluginIssue.h - PluginIssue class
*
* Copyright (c) 2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef PLUGINISSUE_H
#define PLUGINISSUE_H
#include <QDebug>
#include <string>
//! Types of issues that can cause LMMS to not load a plugin
//! LMMS Plugins should use this to indicate errors
enum PluginIssueType
{
unknownPortFlow,
unknownPortType,
tooManyInputChannels,
tooManyOutputChannels,
noOutputChannel,
portHasNoDef,
portHasNoMin,
portHasNoMax,
featureNotSupported, //!< plugin requires functionality LMMS can't offer
badPortType, //!< port type not supported
noIssue
};
//! Issue type bundled with informational string
class PluginIssue
{
static const char* msgFor(const PluginIssueType& it);
PluginIssueType m_issueType;
std::string m_info;
public:
PluginIssue(PluginIssueType it, std::string msg = std::string())
: m_issueType(it), m_info(msg)
{
}
friend QDebug operator<<(QDebug stream, const PluginIssue& iss);
};
QDebug operator<<(QDebug stream, const PluginIssue& iss);
#endif // PLUGINISSUE_H

View File

@@ -36,7 +36,10 @@ class TabWidget : public QWidget
{
Q_OBJECT
public:
TabWidget( const QString & _caption, QWidget * _parent, bool usePixmap = false );
//! @param resizable If true, the widget resizes to fit the size of all tabs
//! If false, all child widget will be cut down to the TabWidget's size
TabWidget( const QString & _caption, QWidget * _parent,
bool usePixmap = false, bool resizable = false );
virtual ~TabWidget() = default;
void addTab( QWidget * w, const QString & name, const char *pixmap = NULL, int idx = -1 );
@@ -74,7 +77,7 @@ protected:
virtual void paintEvent( QPaintEvent * _pe );
virtual void resizeEvent( QResizeEvent * _re );
virtual void wheelEvent( QWheelEvent * _we );
virtual QSize minimumSizeHint() const;
private:
struct widgetDesc
@@ -88,6 +91,7 @@ private:
widgetStack m_widgets;
bool m_resizable;
int m_activeTab;
QString m_caption; // Tab caption, used as the tooltip text on icon tabs
quint8 m_tabbarHeight; // The height of the tab bar

View File

@@ -33,6 +33,7 @@ class QAction;
class LMMS_EXPORT TempoSyncKnobModel : public FloatModel
{
Q_OBJECT
MODEL_IS_VISITABLE
public:
enum TempoSyncMode
{
@@ -51,10 +52,10 @@ public:
const float _max, const float _step,
const float _scale, Model * _parent,
const QString & _display_name = QString() );
virtual ~TempoSyncKnobModel();
virtual ~TempoSyncKnobModel() override;
void saveSettings( QDomDocument & _doc, QDomElement & _this, const QString& name );
void loadSettings( const QDomElement & _this, const QString& name );
void saveSettings( QDomDocument & _doc, QDomElement & _this, const QString& name ) override;
void loadSettings( const QDomElement & _this, const QString& name ) override;
TempoSyncMode syncMode() const
{

View File

@@ -55,10 +55,10 @@ EqControlsDialog::EqControlsDialog( EqControls *controls ) :
EqSpectrumView * inSpec = new EqSpectrumView( &controls->m_inFftBands, this );
inSpec->move( 26, 17 );
inSpec->setColor( QColor( 54, 45, 142, 150 ) );
inSpec->setColor( QColor( 77, 101, 242, 150 ) );
EqSpectrumView * outSpec = new EqSpectrumView( &controls->m_outFftBands, this );
outSpec->setColor( QColor( 9, 166, 156, 150 ) );
outSpec->setColor( QColor( 0, 255, 239, 150 ) );
outSpec->move( 26, 17 );
m_parameterWidget = new EqParameterWidget( this , controls );

View File

@@ -36,7 +36,8 @@ LadspaControls::LadspaControls( LadspaEffect * _eff ) :
{
connect( &m_stereoLinkModel, SIGNAL( dataChanged() ),
this, SLOT( updateLinkStatesFromGlobal() ) );
this, SLOT( updateLinkStatesFromGlobal() ),
Qt::DirectConnection );
multi_proc_t controls = m_effect->getPortControls();
m_controlCount = controls.count();
@@ -59,7 +60,8 @@ LadspaControls::LadspaControls( LadspaEffect * _eff ) :
if( linked_control )
{
connect( (*it)->control, SIGNAL( linkChanged( int, bool ) ),
this, SLOT( linkPort( int, bool ) ) );
this, SLOT( linkPort( int, bool ) ),
Qt::DirectConnection );
}
}
}

View File

@@ -145,9 +145,6 @@ void VstEffect::openPlugin( const QString & _plugin )
return;
}
VstPlugin::connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ), m_plugin.data(), SLOT( setTempo( bpm_t ) ) );
m_plugin->setTempo( Engine::getSong()->getTempo() );
delete tf;
m_key.attributes["file"] = _plugin;

View File

@@ -1962,7 +1962,8 @@ DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param )
{
if( m.id == IdStartProcessing
|| m.id == IdMidiEvent
|| m.id == IdVstSetParameter )
|| m.id == IdVstSetParameter
|| m.id == IdVstSetTempo )
{
_this->processMessage( m );
}

View File

@@ -162,7 +162,7 @@ VstPlugin::VstPlugin( const QString & _plugin ) :
setTempo( Engine::getSong()->getTempo() );
connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ),
this, SLOT( setTempo( bpm_t ) ) );
this, SLOT( setTempo( bpm_t ) ), Qt::DirectConnection );
connect( Engine::mixer(), SIGNAL( sampleRateChanged() ),
this, SLOT( updateSampleRate() ) );

View File

@@ -3,7 +3,7 @@ set(CMAKE_CXX_FLAGS "")
set(CMAKE_C_FLAGS_DEBUG "")
set(CMAKE_CXX_FLAGS_DEBUG "")
IF(LMMS_BUILD_LINUX)
IF(LMMS_BUILD_LINUX AND WANT_VST)
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(qt5-x11embed)
ENDIF()

View File

@@ -40,6 +40,7 @@ set(LMMS_SRCS
core/MixerWorkerThread.cpp
core/MixHelpers.cpp
core/Model.cpp
core/ModelVisitor.cpp
core/Note.cpp
core/NotePlayHandle.cpp
core/Oscillator.cpp
@@ -48,6 +49,7 @@ set(LMMS_SRCS
core/Piano.cpp
core/PlayHandle.cpp
core/Plugin.cpp
core/PluginIssue.cpp
core/PluginFactory.cpp
core/PresetPreviewPlayHandle.cpp
core/ProjectJournal.cpp

View File

@@ -205,7 +205,7 @@ void ControllerConnection::loadSettings( const QDomElement & _this )
else
{
m_controllerId = _this.attribute( "id", "-1" ).toInt();
if( m_controllerId < 0 )
if( m_controllerId < 0 || m_controllerId >= Engine::getSong()->controllers().size() )
{
qWarning( "controller index invalid\n" );
m_controllerId = -1;

View File

@@ -131,32 +131,32 @@ EnvelopeAndLfoParameters::EnvelopeAndLfoParameters(
instances()->add( this );
connect( &m_predelayModel, SIGNAL( dataChanged() ),
this, SLOT( updateSampleVars() ) );
this, SLOT( updateSampleVars() ), Qt::DirectConnection );
connect( &m_attackModel, SIGNAL( dataChanged() ),
this, SLOT( updateSampleVars() ) );
this, SLOT( updateSampleVars() ), Qt::DirectConnection );
connect( &m_holdModel, SIGNAL( dataChanged() ),
this, SLOT( updateSampleVars() ) );
this, SLOT( updateSampleVars() ), Qt::DirectConnection );
connect( &m_decayModel, SIGNAL( dataChanged() ),
this, SLOT( updateSampleVars() ) );
this, SLOT( updateSampleVars() ), Qt::DirectConnection );
connect( &m_sustainModel, SIGNAL( dataChanged() ),
this, SLOT( updateSampleVars() ) );
this, SLOT( updateSampleVars() ), Qt::DirectConnection );
connect( &m_releaseModel, SIGNAL( dataChanged() ),
this, SLOT( updateSampleVars() ) );
this, SLOT( updateSampleVars() ), Qt::DirectConnection );
connect( &m_amountModel, SIGNAL( dataChanged() ),
this, SLOT( updateSampleVars() ) );
this, SLOT( updateSampleVars() ), Qt::DirectConnection );
connect( &m_lfoPredelayModel, SIGNAL( dataChanged() ),
this, SLOT( updateSampleVars() ) );
this, SLOT( updateSampleVars() ), Qt::DirectConnection );
connect( &m_lfoAttackModel, SIGNAL( dataChanged() ),
this, SLOT( updateSampleVars() ) );
this, SLOT( updateSampleVars() ), Qt::DirectConnection );
connect( &m_lfoSpeedModel, SIGNAL( dataChanged() ),
this, SLOT( updateSampleVars() ) );
this, SLOT( updateSampleVars() ), Qt::DirectConnection );
connect( &m_lfoAmountModel, SIGNAL( dataChanged() ),
this, SLOT( updateSampleVars() ) );
this, SLOT( updateSampleVars() ), Qt::DirectConnection );
connect( &m_lfoWaveModel, SIGNAL( dataChanged() ),
this, SLOT( updateSampleVars() ) );
this, SLOT( updateSampleVars() ), Qt::DirectConnection );
connect( &m_x100Model, SIGNAL( dataChanged() ),
this, SLOT( updateSampleVars() ) );
this, SLOT( updateSampleVars() ), Qt::DirectConnection );
connect( Engine::mixer(), SIGNAL( sampleRateChanged() ),
this, SLOT( updateSampleVars() ) );

View File

@@ -42,7 +42,8 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port,
if( m_link )
{
connect( &m_linkEnabledModel, SIGNAL( dataChanged() ),
this, SLOT( linkStateChanged() ) );
this, SLOT( linkStateChanged() ),
Qt::DirectConnection );
}
switch( m_port->data_type )

View File

@@ -49,12 +49,12 @@ LfoController::LfoController( Model * _parent ) :
{
setSampleExact( true );
connect( &m_waveModel, SIGNAL( dataChanged() ),
this, SLOT( updateSampleFunction() ) );
this, SLOT( updateSampleFunction() ), Qt::DirectConnection );
connect( &m_speedModel, SIGNAL( dataChanged() ),
this, SLOT( updateDuration() ) );
this, SLOT( updateDuration() ), Qt::DirectConnection );
connect( &m_multiplierModel, SIGNAL( dataChanged() ),
this, SLOT( updateDuration() ) );
this, SLOT( updateDuration() ), Qt::DirectConnection );
connect( Engine::mixer(), SIGNAL( sampleRateChanged() ),
this, SLOT( updateDuration() ) );

View File

@@ -33,9 +33,9 @@ MeterModel::MeterModel( ::Model * _parent ) :
m_denominatorModel( 4, 1, 32, this, tr( "Denominator" ) )
{
connect( &m_numeratorModel, SIGNAL( dataChanged() ),
this, SIGNAL( dataChanged() ) );
this, SIGNAL( dataChanged() ), Qt::DirectConnection );
connect( &m_denominatorModel, SIGNAL( dataChanged() ),
this, SIGNAL( dataChanged() ) );
this, SIGNAL( dataChanged() ), Qt::DirectConnection );
}

44
src/core/ModelVisitor.cpp Normal file
View File

@@ -0,0 +1,44 @@
/*
* ModelVisitor.cpp - visitors for automatable models
*
* Copyright (c) 2019-2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#include "ModelVisitor.h"
#include "AutomatableModel.h"
#include "ComboBoxModel.h"
#include "TempoSyncKnobModel.h"
void ModelVisitor::visit(BoolModel &m) { up(m); }
void ModelVisitor::visit(IntModel &m) { up(m); }
void ModelVisitor::visit(FloatModel &m) { up(m); }
void ModelVisitor::visit(ComboBoxModel &m) { up<IntModel>(m); }
void ModelVisitor::visit(TempoSyncKnobModel &m) { up<FloatModel>(m); }
void ConstModelVisitor::visit(const BoolModel &m) { up(m); }
void ConstModelVisitor::visit(const IntModel &m) { up(m); }
void ConstModelVisitor::visit(const FloatModel &m) { up(m); }
void ConstModelVisitor::visit(const ComboBoxModel &m) { up<IntModel>(m); }
void ConstModelVisitor::visit(const TempoSyncKnobModel &m) { up<FloatModel>(m); }
ModelVisitor::~ModelVisitor() {}
ConstModelVisitor::~ConstModelVisitor() {}

View File

@@ -62,6 +62,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
m_subNotes(),
m_released( false ),
m_releaseStarted( false ),
m_hasMidiNote( false ),
m_hasParent( parent != NULL ),
m_parent( parent ),
m_hadChildren( false ),
@@ -105,17 +106,6 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
m_instrumentTrack->midiNoteOn( *this );
}
if( hasParent() || ! m_instrumentTrack->isArpeggioEnabled() )
{
const int baseVelocity = m_instrumentTrack->midiPort()->baseVelocity();
// send MidiNoteOn event
m_instrumentTrack->processOutEvent(
MidiEvent( MidiNoteOn, midiChannel(), midiKey(), midiVelocity( baseVelocity ) ),
MidiTime::fromFrames( offset(), Engine::framesPerTick() ),
offset() );
}
if( m_instrumentTrack->instrument()->flags() & Instrument::IsSingleStreamed )
{
setUsesBuffer( false );
@@ -205,6 +195,21 @@ void NotePlayHandle::play( sampleFrame * _working_buffer )
}
lock();
if( m_totalFramesPlayed == 0 && !m_hasMidiNote
&& ( hasParent() || ! m_instrumentTrack->isArpeggioEnabled() ) )
{
m_hasMidiNote = true;
const int baseVelocity = m_instrumentTrack->midiPort()->baseVelocity();
// send MidiNoteOn event
m_instrumentTrack->processOutEvent(
MidiEvent( MidiNoteOn, midiChannel(), midiKey(), midiVelocity( baseVelocity ) ),
MidiTime::fromFrames( offset(), Engine::framesPerTick() ),
offset() );
}
if( m_frequencyNeedsUpdate )
{
updateFrequency();
@@ -357,8 +362,10 @@ void NotePlayHandle::noteOff( const f_cnt_t _s )
m_framesBeforeRelease = _s;
m_releaseFramesToDo = qMax<f_cnt_t>( 0, actualReleaseFramesToDo() );
if( hasParent() || ! m_instrumentTrack->isArpeggioEnabled() )
if( m_hasMidiNote )
{
m_hasMidiNote = false;
// send MidiNoteOff event
m_instrumentTrack->processOutEvent(
MidiEvent( MidiNoteOff, midiChannel(), midiKey(), 0 ),

View File

@@ -53,8 +53,10 @@ PeakController::PeakController( Model * _parent,
this, SLOT( handleDestroyedEffect( ) ) );
}
connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateCoeffs() ) );
connect( m_peakEffect->attackModel(), SIGNAL( dataChanged() ), this, SLOT( updateCoeffs() ) );
connect( m_peakEffect->decayModel(), SIGNAL( dataChanged() ), this, SLOT( updateCoeffs() ) );
connect( m_peakEffect->attackModel(), SIGNAL( dataChanged() ),
this, SLOT( updateCoeffs() ), Qt::DirectConnection );
connect( m_peakEffect->decayModel(), SIGNAL( dataChanged() ),
this, SLOT( updateCoeffs() ), Qt::DirectConnection );
m_coeffNeedsUpdate = true;
}

72
src/core/PluginIssue.cpp Normal file
View File

@@ -0,0 +1,72 @@
/*
* PluginIssue.h - PluginIssue class
*
* Copyright (c) 2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#include <QDebug>
#include "PluginIssue.h"
const char *PluginIssue::msgFor(const PluginIssueType &it)
{
switch (it)
{
case unknownPortFlow:
return "unknown port flow for mandatory port";
case unknownPortType:
return "unknown port type for mandatory port";
case tooManyInputChannels:
return "too many audio input channels";
case tooManyOutputChannels:
return "too many audio output channels";
case noOutputChannel:
return "no audio output channel";
case portHasNoDef:
return "port is missing default value";
case portHasNoMin:
return "port is missing min value";
case portHasNoMax:
return "port is missing max value";
case featureNotSupported:
return "required feature not supported";
case badPortType:
return "unsupported port type";
case noIssue:
return nullptr;
}
return nullptr;
}
QDebug operator<<(QDebug stream, const PluginIssue &iss)
{
stream << PluginIssue::msgFor(iss.m_issueType);
if (iss.m_info.length())
{
stream.nospace() << ": " << iss.m_info.c_str();
}
return stream;
}

View File

@@ -92,18 +92,18 @@ Song::Song() :
{
for(int i = 0; i < Mode_Count; ++i) m_elapsedMilliSeconds[i] = 0;
connect( &m_tempoModel, SIGNAL( dataChanged() ),
this, SLOT( setTempo() ) );
this, SLOT( setTempo() ), Qt::DirectConnection );
connect( &m_tempoModel, SIGNAL( dataUnchanged() ),
this, SLOT( setTempo() ) );
this, SLOT( setTempo() ), Qt::DirectConnection );
connect( &m_timeSigModel, SIGNAL( dataChanged() ),
this, SLOT( setTimeSignature() ) );
this, SLOT( setTimeSignature() ), Qt::DirectConnection );
connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this,
SLOT( updateFramesPerTick() ) );
connect( &m_masterVolumeModel, SIGNAL( dataChanged() ),
this, SLOT( masterVolumeChanged() ) );
this, SLOT( masterVolumeChanged() ), Qt::DirectConnection );
/* connect( &m_masterPitchModel, SIGNAL( dataChanged() ),
this, SLOT( masterPitchChanged() ) );*/

View File

@@ -42,7 +42,8 @@ TempoSyncKnobModel::TempoSyncKnobModel( const float _val, const float _min,
m_custom( _parent )
{
connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ),
this, SLOT( calculateTempoSyncTime( bpm_t ) ) );
this, SLOT( calculateTempoSyncTime( bpm_t ) ),
Qt::DirectConnection );
}
@@ -154,7 +155,8 @@ void TempoSyncKnobModel::setSyncMode( TempoSyncMode _new_mode )
if( _new_mode == SyncCustom )
{
connect( &m_custom, SIGNAL( dataChanged() ),
this, SLOT( updateCustom() ) );
this, SLOT( updateCustom() ),
Qt::DirectConnection );
}
}
calculateTempoSyncTime( Engine::getSong()->getTempo() );

View File

@@ -328,8 +328,8 @@ TrackContentObjectView::~TrackContentObjectView()
/*! \brief Update a TrackContentObjectView
*
* TCO's get drawn only when needed,
* and when a TCO is updated,
* TCO's get drawn only when needed,
* and when a TCO is updated,
* it needs to be redrawn.
*
*/
@@ -598,9 +598,9 @@ void TrackContentObjectView::dropEvent( QDropEvent * de )
*/
void TrackContentObjectView::leaveEvent( QEvent * e )
{
while( QApplication::overrideCursor() != NULL )
if( cursor().shape() != Qt::BitmapCursor )
{
QApplication::restoreOverrideCursor();
setCursor( QCursor( embed::getIconPixmap( "hand" ), 3, 3 ) );
}
if( e != NULL )
{
@@ -746,20 +746,17 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me )
&& !m_tco->getAutoResize() )
{
m_action = ResizeLeft;
QCursor c( Qt::SizeHorCursor );
QApplication::setOverrideCursor( c );
setCursor( Qt::SizeHorCursor );
}
else if( me->x() < width() - RESIZE_GRIP_WIDTH )
{
m_action = Move;
QCursor c( Qt::SizeAllCursor );
QApplication::setOverrideCursor( c );
setCursor( Qt::SizeAllCursor );
}
else if( !m_tco->getAutoResize() )
{
m_action = Resize;
QCursor c( Qt::SizeHorCursor );
QApplication::setOverrideCursor( c );
setCursor( Qt::SizeHorCursor );
}
if( m_action == Move )
@@ -1007,17 +1004,7 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me )
if( ( me->x() > width() - RESIZE_GRIP_WIDTH && !me->buttons() && !m_tco->getAutoResize() )
|| ( me->x() < RESIZE_GRIP_WIDTH && !me->buttons() && sTco && !m_tco->getAutoResize() ) )
{
if( QApplication::overrideCursor() != NULL &&
QApplication::overrideCursor()->shape() !=
Qt::SizeHorCursor )
{
while( QApplication::overrideCursor() != NULL )
{
QApplication::restoreOverrideCursor();
}
}
QCursor c( Qt::SizeHorCursor );
QApplication::setOverrideCursor( c );
setCursor( Qt::SizeHorCursor );
}
else
{
@@ -1194,7 +1181,7 @@ void TrackContentWidget::updateBackground()
// draw lines
// vertical lines
pmp.setPen( QPen( gridColor(), 1 ) );
pmp.setPen( QPen( gridColor(), 1 ) );
for( float x = 0; x < w * 2; x += ppt )
{
pmp.drawLine( QLineF( x, 0.0, x, h ) );
@@ -1205,9 +1192,9 @@ void TrackContentWidget::updateBackground()
{
pmp.drawLine( QLineF( x, 0.0, x, h ) );
}
// horizontal line
pmp.setPen( QPen( gridColor(), 1 ) );
pmp.setPen( QPen( gridColor(), 1 ) );
pmp.drawLine( 0, h-1, w*2, h-1 );
pmp.end();
@@ -1390,7 +1377,7 @@ MidiTime TrackContentWidget::getPosition( int mouseX )
*/
void TrackContentWidget::dragEnterEvent( QDragEnterEvent * dee )
{
MidiTime tcoPos = MidiTime( getPosition( dee->pos().x() ).getTact(), 0 );
MidiTime tcoPos = getPosition( dee->pos().x() );
if( canPasteSelection( tcoPos, dee ) == false )
{
dee->ignore();
@@ -1928,7 +1915,7 @@ void TrackOperationsWidget::updateMenu()
toMenu->addAction( embed::getIconPixmap( "cancel", 16, 16 ),
tr( "Remove this track" ),
this, SLOT( removeTrack() ) );
if( ! m_trackView->trackContainerView()->fixedTCOs() )
{
toMenu->addAction( tr( "Clear this track" ), this, SLOT( clearTrack() ) );
@@ -2624,7 +2611,7 @@ TrackView::TrackView( Track * track, TrackContainerView * tcv ) :
&m_trackContentWidget, SLOT( update() ) );
connect( &m_track->m_soloModel, SIGNAL( dataChanged() ),
m_track, SLOT( toggleSolo() ) );
m_track, SLOT( toggleSolo() ), Qt::DirectConnection );
// create views for already existing TCOs
for( Track::tcoVector::iterator it =
m_track->m_trackContentObjects.begin();
@@ -2871,12 +2858,12 @@ void TrackView::mouseMoveEvent( QMouseEvent * me )
else if( m_action == MoveTrack )
{
// look which track-widget the mouse-cursor is over
const int yPos =
const int yPos =
m_trackContainerView->contentWidget()->mapFromGlobal( me->globalPos() ).y();
const TrackView * trackAtY = m_trackContainerView->trackViewAt( yPos );
// debug code
// qDebug( "y position %d", yPos );
// debug code
// qDebug( "y position %d", yPos );
// a track-widget not equal to ourself?
if( trackAtY != NULL && trackAtY != this )

View File

@@ -100,7 +100,7 @@ MidiAlsaSeq::MidiAlsaSeq() :
snd_seq_start_queue( m_seqHandle, m_queueID, NULL );
changeQueueTempo( Engine::getSong()->getTempo() );
connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ),
this, SLOT( changeQueueTempo( bpm_t ) ) );
this, SLOT( changeQueueTempo( bpm_t ) ), Qt::DirectConnection );
// initial list-update
updatePortList();

View File

@@ -63,9 +63,12 @@ MidiPort::MidiPort( const QString& name,
m_readableModel.setValue( m_mode == Input || m_mode == Duplex );
m_writableModel.setValue( m_mode == Output || m_mode == Duplex );
connect( &m_readableModel, SIGNAL( dataChanged() ), this, SLOT( updateMidiPortMode() ) );
connect( &m_writableModel, SIGNAL( dataChanged() ), this, SLOT( updateMidiPortMode() ) );
connect( &m_outputProgramModel, SIGNAL( dataChanged() ), this, SLOT( updateOutputProgram() ) );
connect( &m_readableModel, SIGNAL( dataChanged() ),
this, SLOT( updateMidiPortMode() ), Qt::DirectConnection );
connect( &m_writableModel, SIGNAL( dataChanged() ),
this, SLOT( updateMidiPortMode() ), Qt::DirectConnection );
connect( &m_outputProgramModel, SIGNAL( dataChanged() ),
this, SLOT( updateOutputProgram() ), Qt::DirectConnection );
// when using with non-raw-clients we can provide buttons showing

View File

@@ -163,6 +163,7 @@ bool FileBrowser::filterItems( const QString & filter, QTreeWidgetItem * item )
void FileBrowser::reloadTree( void )
{
QList<QString> expandedDirs = m_fileBrowserTreeWidget->expandedDirs();
const QString text = m_filterEdit->text();
m_filterEdit->clear();
m_fileBrowserTreeWidget->clear();
@@ -171,17 +172,17 @@ void FileBrowser::reloadTree( void )
{
addItems( *it );
}
expandItems();
expandItems(NULL, expandedDirs);
m_filterEdit->setText( text );
filterItems( text );
}
void FileBrowser::expandItems( QTreeWidgetItem * item )
void FileBrowser::expandItems( QTreeWidgetItem * item, QList<QString> expandedDirs )
{
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);
if ( m_recurse )
@@ -189,14 +190,15 @@ void FileBrowser::expandItems( QTreeWidgetItem * item )
it->setExpanded( true );
}
Directory *d = dynamic_cast<Directory *> ( it );
if( d )
if (d)
{
d->update();
d->setExpanded( false );
bool expand = expandedDirs.contains( d->fullName() );
d->setExpanded( expand );
}
if( m_recurse && it->childCount() )
if (m_recurse && it->childCount())
{
expandItems(it);
expandItems(it, expandedDirs);
}
}
}
@@ -326,6 +328,30 @@ FileBrowserTreeWidget::FileBrowserTreeWidget(QWidget * parent ) :
}
QList<QString> FileBrowserTreeWidget::expandedDirs( QTreeWidgetItem * item ) const
{
int numChildren = item ? item->childCount() : topLevelItemCount();
QList<QString> dirs;
for (int i = 0; i < numChildren; ++i)
{
QTreeWidgetItem * it = item ? item->child(i) : topLevelItem(i);
// Add expanded top level directories.
if (it->isExpanded() && (it->type() == TypeDirectoryItem))
{
Directory *d = static_cast<Directory *> ( it );
dirs.append( d->fullName() );
}
// Add expanded child directories (recurse).
if (it->childCount())
{
dirs.append( expandedDirs( it ) );
}
}
return dirs;
}
void FileBrowserTreeWidget::contextMenuEvent(QContextMenuEvent * e )
{
FileItem * f = dynamic_cast<FileItem *>( itemAt( e->pos() ) );

View File

@@ -313,7 +313,7 @@ FxMixerView::FxChannelView::FxChannelView(QWidget * _parent, FxMixerView * _mv,
m_soloBtn->setCheckable( true );
m_soloBtn->move( 9, m_fader->y()-21);
connect(&fxChannel->m_soloModel, SIGNAL( dataChanged() ),
_mv, SLOT ( toggledSolo() ) );
_mv, SLOT ( toggledSolo() ), Qt::DirectConnection );
ToolTip::add( m_soloBtn, tr( "Solo FX channel" ) );
// Create EffectRack for the channel

View File

@@ -34,7 +34,6 @@ InstrumentView::InstrumentView( Instrument * _Instrument, QWidget * _parent ) :
PluginView( _Instrument, _parent )
{
setModel( _Instrument );
setFixedSize( 250, 250 );
setAttribute( Qt::WA_DeleteOnClose, true );
}

View File

@@ -1007,6 +1007,9 @@ void PianoRoll::drawDetuningInfo( QPainter & _p, const Note * _n, int _x,
{
int middle_y = _y + KEY_LINE_HEIGHT / 2;
_p.setPen( noteColor() );
_p.setClipRect(WHITE_KEY_WIDTH, PR_TOP_MARGIN,
width() - WHITE_KEY_WIDTH,
keyAreaBottom() - PR_TOP_MARGIN);
int old_x = 0;
int old_y = 0;
@@ -1188,9 +1191,11 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke)
{
const int key_num = PianoView::getKeyFromKeyEvent( ke ) + ( DefaultOctave - 1 ) * KeysPerOctave;
if(! ke->isAutoRepeat() && key_num > -1)
if (!ke->isAutoRepeat() && key_num > -1)
{
m_pattern->instrumentTrack()->pianoModel()->handleKeyPress( key_num );
m_pattern->instrumentTrack()->pianoModel()->handleKeyPress(key_num);
// if a chord is set, play all chord notes (simulate click on all):
playChordNotes(key_num);
ke->accept();
}
}
@@ -1388,10 +1393,11 @@ void PianoRoll::keyReleaseEvent(QKeyEvent* ke )
if( hasValidPattern() && ke->modifiers() == Qt::NoModifier )
{
const int key_num = PianoView::getKeyFromKeyEvent( ke ) + ( DefaultOctave - 1 ) * KeysPerOctave;
if( ! ke->isAutoRepeat() && key_num > -1 )
if (!ke->isAutoRepeat() && key_num > -1)
{
m_pattern->instrumentTrack()->pianoModel()->handleKeyRelease( key_num );
m_pattern->instrumentTrack()->pianoModel()->handleKeyRelease(key_num);
// if a chord is set, simulate click release on all chord notes
pauseChordNotes(key_num);
ke->accept();
}
}
@@ -1836,7 +1842,9 @@ void PianoRoll::mousePressEvent(QMouseEvent * me )
{
// left click - play the note
int v = ( (float) x ) / ( (float) WHITE_KEY_WIDTH ) * MidiDefaultVelocity;
m_pattern->instrumentTrack()->pianoModel()->handleKeyPress( key_num, v );
m_pattern->instrumentTrack()->pianoModel()->handleKeyPress(key_num, v);
// if a chord is set, play the chords notes as well:
playChordNotes(key_num, v);
}
}
else
@@ -1939,7 +1947,10 @@ void PianoRoll::testPlayNote( Note * n )
const int baseVelocity = m_pattern->instrumentTrack()->midiPort()->baseVelocity();
m_pattern->instrumentTrack()->pianoModel()->handleKeyPress( n->key(), n->midiVelocity( baseVelocity ) );
m_pattern->instrumentTrack()->pianoModel()->handleKeyPress(n->key(), n->midiVelocity(baseVelocity));
// if a chord is set, play the chords notes as well:
playChordNotes(n->key(), n->midiVelocity(baseVelocity));
MidiEvent event( MidiMetaEvent, -1, n->key(), panningToMidi( n->getPanning() ) );
@@ -1962,6 +1973,9 @@ void PianoRoll::pauseTestNotes( bool pause )
{
// stop note
m_pattern->instrumentTrack()->pianoModel()->handleKeyRelease( note->key() );
// if a chord was set, stop the chords notes as well:
pauseChordNotes(note->key());
}
else
{
@@ -1973,19 +1987,56 @@ void PianoRoll::pauseTestNotes( bool pause )
}
}
void PianoRoll::playChordNotes(int key, int velocity)
{
// if a chord is set, play the chords notes beside the base note.
Piano *pianoModel = m_pattern->instrumentTrack()->pianoModel();
const InstrumentFunctionNoteStacking::Chord & chord =
InstrumentFunctionNoteStacking::ChordTable::getInstance().getChordByName(
m_chordModel.currentText());
if (!chord.isEmpty())
{
for (int i = 1; i < chord.size(); ++i)
{
pianoModel->handleKeyPress(key + chord[i], velocity);
}
}
}
void PianoRoll::pauseChordNotes(int key)
{
// if a chord was set, stop the chords notes beside the base note.
Piano *pianoModel = m_pattern->instrumentTrack()->pianoModel();
const InstrumentFunctionNoteStacking::Chord & chord =
InstrumentFunctionNoteStacking::ChordTable::getInstance().getChordByName(
m_chordModel.currentText());
if (!chord.isEmpty())
{
for (int i = 1; i < chord.size(); ++i)
{
pianoModel->handleKeyRelease(key + chord[i]);
}
}
}
void PianoRoll::testPlayKey( int key, int velocity, int pan )
{
Piano *pianoModel = m_pattern->instrumentTrack()->pianoModel();
// turn off old key
m_pattern->instrumentTrack()->pianoModel()->handleKeyRelease( m_lastKey );
pianoModel->handleKeyRelease( m_lastKey );
// if a chord was set, stop the chords notes as well
pauseChordNotes(m_lastKey);
// remember which one we're playing
m_lastKey = key;
// play new key
m_pattern->instrumentTrack()->pianoModel()->handleKeyPress( key, velocity );
pianoModel->handleKeyPress( key, velocity );
// and if a chord is set, play chord notes:
playChordNotes(key, velocity);
}
@@ -2115,6 +2166,7 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me )
{
m_pattern->instrumentTrack()->pianoModel()->
handleKeyRelease( note->key() );
pauseChordNotes(note->key());
note->setIsPlaying( false );
}
}
@@ -2122,6 +2174,7 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me )
// stop playing keys that we let go of
m_pattern->instrumentTrack()->pianoModel()->
handleKeyRelease( m_lastKey );
pauseChordNotes(m_lastKey);
}
m_currentNote = NULL;
@@ -2309,6 +2362,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me )
{
// mouse not over this note, stop playing it.
m_pattern->instrumentTrack()->pianoModel()->handleKeyRelease( n->key() );
pauseChordNotes(n->key());
n->setIsPlaying( false );
}
@@ -3255,6 +3309,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe )
drawDetuningInfo( p, note,
x + WHITE_KEY_WIDTH,
y_base - key * KEY_LINE_HEIGHT );
p.setClipRect(WHITE_KEY_WIDTH, PR_TOP_MARGIN,
width() - WHITE_KEY_WIDTH,
height() - PR_TOP_MARGIN);
}
}

View File

@@ -635,13 +635,14 @@ void graphModel::smoothNonCyclic()
emit samplesChanged(0, length()-1);
}
//makes a cyclic convolution.
void graphModel::convolve(const float *convolution, const int convolutionLength, const int centerOffset)
void graphModel::convolve(const float *convolution,
const int convolutionLength, const int centerOffset)
{
// store values in temporary array
QVector<float> temp = m_samples;
const int graphLength = length();
float sum;
// make a cyclic convolution
for ( int i = 0; i < graphLength; i++ )
{
sum = 0;

View File

@@ -34,8 +34,10 @@
#include "gui_templates.h"
#include "embed.h"
TabWidget::TabWidget( const QString & caption, QWidget * parent, bool usePixmap ) :
TabWidget::TabWidget(const QString & caption, QWidget * parent, bool usePixmap,
bool resizable) :
QWidget( parent ),
m_resizable( resizable ),
m_activeTab( 0 ),
m_caption( caption ),
m_usePixmap( usePixmap ),
@@ -81,7 +83,10 @@ void TabWidget::addTab( QWidget * w, const QString & name, const char *pixmap, i
m_widgets[idx] = d;
// Position tab's window
w->setFixedSize( width() - 4, height() - m_tabbarHeight );
if (!m_resizable)
{
w->setFixedSize( width() - 4, height() - m_tabbarHeight );
}
w->move( 2, m_tabbarHeight - 1 );
w->hide();
@@ -189,17 +194,19 @@ void TabWidget::mousePressEvent( QMouseEvent * me )
void TabWidget::resizeEvent( QResizeEvent * )
{
for( widgetStack::iterator it = m_widgets.begin();
it != m_widgets.end(); ++it )
if (!m_resizable)
{
( *it ).w->setFixedSize( width() - 4, height() - m_tabbarHeight );
for ( widgetStack::iterator it = m_widgets.begin();
it != m_widgets.end(); ++it )
{
( *it ).w->setFixedSize( width() - 4, height() - m_tabbarHeight );
}
}
}
void TabWidget::paintEvent( QPaintEvent * pe )
{
QPainter p( this );
@@ -284,7 +291,7 @@ void TabWidget::wheelEvent( QWheelEvent * we )
if( we->y() > m_tabheight )
{
return;
}
}
we->accept();
int dir = ( we->delta() < 0 ) ? 1 : -1;
@@ -300,6 +307,32 @@ void TabWidget::wheelEvent( QWheelEvent * we )
setActiveTab( tab );
}
// Let parent widgets know how much space this tab widget needs
QSize TabWidget::minimumSizeHint() const
{
if (m_resizable)
{
int maxWidth = 0, maxHeight = 0;
for ( widgetStack::const_iterator it = m_widgets.begin();
it != m_widgets.end(); ++it )
{
maxWidth = std::max(maxWidth, it->w->width());
maxHeight = std::max(maxHeight, it->w->height());
}
// "-1" :
// in "addTab", under "Position tab's window", the widget is
// moved up by 1 pixel
return QSize(maxWidth + 4, maxHeight + m_tabbarHeight - 1);
}
else { return QWidget::minimumSizeHint(); }
}
// Return the color to be used to draw a TabWidget's title text (if any)
QColor TabWidget::tabTitleText() const
{

View File

@@ -635,5 +635,6 @@ bool BBTrackView::close()
void BBTrackView::clickedTrackLabel()
{
Engine::getBBTrackContainer()->setCurrentBB( m_bbTrack->index() );
gui->getBBEditor()->show();
gui->getBBEditor()->parentWidget()->show();
gui->getBBEditor()->setFocus( Qt::ActiveWindowFocusReason );
}

View File

@@ -1298,7 +1298,7 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) :
this, SLOT( textChanged( const QString & ) ) );
m_nameLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred));
nameAndChangeTrackLayout->addWidget(m_nameLineEdit);
nameAndChangeTrackLayout->addWidget(m_nameLineEdit, 1);
// set up left/right arrows for changing instrument
@@ -1410,8 +1410,11 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) :
generalSettingsLayout->addLayout( basicControlsLayout );
m_tabWidget = new TabWidget( "", this, true );
m_tabWidget->setFixedHeight( INSTRUMENT_HEIGHT + GRAPHIC_TAB_HEIGHT - 4 );
m_tabWidget = new TabWidget( "", this, true, true );
// "-1" :
// in "TabWidget::addTab", under "Position tab's window", the widget is
// moved up by 1 pixel
m_tabWidget->setMinimumHeight( INSTRUMENT_HEIGHT + GRAPHIC_TAB_HEIGHT - 4 - 1 );
// create tab-widgets
@@ -1443,24 +1446,27 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) :
m_tabWidget->addTab( m_effectView, tr( "Effects" ), "fx_tab", 3 );
m_tabWidget->addTab( m_midiView, tr( "MIDI" ), "midi_tab", 4 );
m_tabWidget->addTab( m_miscView, tr( "Miscellaneous" ), "misc_tab", 5 );
adjustTabSize(m_ssView);
adjustTabSize(instrumentFunctions);
adjustTabSize(m_effectView);
adjustTabSize(m_midiView);
adjustTabSize(m_miscView);
// setup piano-widget
m_pianoView = new PianoView( this );
m_pianoView->setFixedSize( INSTRUMENT_WIDTH, PIANO_HEIGHT );
m_pianoView->setMinimumHeight( PIANO_HEIGHT );
m_pianoView->setMaximumHeight( PIANO_HEIGHT );
vlayout->addWidget( generalSettingsWidget );
vlayout->addWidget( m_tabWidget );
vlayout->addWidget( m_tabWidget, 1 );
vlayout->addWidget( m_pianoView );
setModel( _itv->model() );
updateInstrumentView();
setFixedWidth( INSTRUMENT_WIDTH );
resize( sizeHint() );
QMdiSubWindow * subWin = gui->mainWindow()->addWindowedWidget( this );
QMdiSubWindow* subWin = gui->mainWindow()->addWindowedWidget( this );
Qt::WindowFlags flags = subWin->windowFlags();
flags |= Qt::MSWindowsFixedSizeDialogHint;
flags &= ~Qt::WindowMaximizeButtonHint;
@@ -1473,7 +1479,7 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) :
systemMenu->actions().at( 4 )->setVisible( false ); // Maximize
subWin->setWindowIcon( embed::getIconPixmap( "instrument_track" ) );
subWin->setFixedSize( subWin->size() );
subWin->setMinimumSize( subWin->size() );
subWin->hide();
}
@@ -1624,6 +1630,7 @@ void InstrumentTrackWindow::updateInstrumentView()
modelChanged(); // Get the instrument window to refresh
m_track->dataChanged(); // Get the text on the trackButton to change
adjustTabSize(m_instrumentView);
m_pianoView->setVisible(m_track->m_instrument->hasNoteInput());
}
}
@@ -1819,6 +1826,7 @@ void InstrumentTrackWindow::viewInstrumentInDirection(int d)
// get the instrument window to refresh
modelChanged();
}
Q_ASSERT(bringToFront);
bringToFront->getInstrumentTrackWindow()->setFocus();
}
@@ -1831,4 +1839,12 @@ void InstrumentTrackWindow::viewPrevInstrument()
viewInstrumentInDirection(-1);
}
void InstrumentTrackWindow::adjustTabSize(QWidget *w)
{
// "-1" :
// in "TabWidget::addTab", under "Position tab's window", the widget is
// moved up by 1 pixel
w->setMinimumSize(INSTRUMENT_WIDTH - 4, INSTRUMENT_HEIGHT - 4 - 1);
}
#include "InstrumentTrack.moc"

View File

@@ -65,7 +65,7 @@ SampleTCO::SampleTCO( Track * _track ) :
// we need to receive bpm-change-events, because then we have to
// change length of this TCO
connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ),
this, SLOT( updateLength() ) );
this, SLOT( updateLength() ), Qt::DirectConnection );
connect( Engine::getSong(), SIGNAL( timeSignatureChanged( int,int ) ),
this, SLOT( updateLength() ) );
@@ -110,7 +110,7 @@ SampleTCO::SampleTCO( Track * _track ) :
SampleTCO::~SampleTCO()
{
SampleTrack * sampletrack = dynamic_cast<SampleTrack*>( getTrack() );
if( sampletrack)
if ( sampletrack )
{
sampletrack->updateTcos();
}
@@ -122,10 +122,7 @@ SampleTCO::~SampleTCO()
void SampleTCO::changeLength( const MidiTime & _length )
{
float nom = Engine::getSong()->getTimeSigModel().getNumerator();
float den = Engine::getSong()->getTimeSigModel().getDenominator();
int ticksPerTact = DefaultTicksPerTact * ( nom / den );
TrackContentObject::changeLength( qMax( static_cast<int>( _length ), ticksPerTact ) );
TrackContentObject::changeLength( qMax( static_cast<int>( _length ), 1 ) );
}
@@ -151,9 +148,21 @@ void SampleTCO::setSampleBuffer( SampleBuffer* sb )
void SampleTCO::setSampleFile( const QString & _sf )
{
m_sampleBuffer->setAudioFile( _sf );
int length;
if ( _sf.isEmpty() )
{ //When creating an empty sample pattern make it a bar long
float nom = Engine::getSong()->getTimeSigModel().getNumerator();
float den = Engine::getSong()->getTimeSigModel().getDenominator();
length = DefaultTicksPerTact * ( nom / den );
}
else
{ //Otherwise set it to the sample's length
m_sampleBuffer->setAudioFile( _sf );
length = sampleLength();
}
changeLength(length);
setStartTimeOffset( 0 );
changeLength( (int) ( m_sampleBuffer->frames() / Engine::framesPerTick() ) );
emit sampleChanged();
emit playbackPositionChanged();
@@ -440,8 +449,15 @@ void SampleTCOView::mouseReleaseEvent(QMouseEvent *_me)
void SampleTCOView::mouseDoubleClickEvent( QMouseEvent * )
{
QString af = m_tco->m_sampleBuffer->openAudioFile();
if( af != "" && af != m_tco->m_sampleBuffer->audioFile() )
{
if ( af.isEmpty() ) {} //Don't do anything if no file is loaded
else if ( af == m_tco->m_sampleBuffer->audioFile() )
{ //Instead of reloading the existing file, just reset the size
int length = (int) ( m_tco->m_sampleBuffer->frames() / Engine::framesPerTick() );
m_tco->changeLength(length);
}
else
{ //Otherwise load the new file as ususal
m_tco->setSampleFile( af );
Engine::getSong()->setModified();
}

View File

@@ -14,6 +14,7 @@ ADD_EXECUTABLE(tests
QTestSuite
$<TARGET_OBJECTS:lmmsobjs>
src/core/AutomatableModelTest.cpp
src/core/ProjectVersionTest.cpp
src/core/RelativePathsTest.cpp

View File

@@ -0,0 +1,55 @@
/*
* AutomatableModelTest.cpp
*
* Copyright (c) 2019-2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
*
* This file is part of LMMS - https://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#include "QTestSuite.h"
#include "AutomatableModel.h"
#include "ComboBoxModel.h"
class AutomatableModelTest : QTestSuite
{
Q_OBJECT
private slots:
//! Test that upcast and exact casts work,
//! but no downcast or any other casts
void CastTests()
{
ComboBoxModel comboModel;
AutomatableModel* amPtr = &comboModel;
QVERIFY(nullptr == amPtr->dynamicCast<FloatModel>()); // not a parent class
QCOMPARE(&comboModel, amPtr->dynamicCast<AutomatableModel>()); // parent class
QCOMPARE(&comboModel, amPtr->dynamicCast<IntModel>()); // parent class
QCOMPARE(&comboModel, amPtr->dynamicCast<ComboBoxModel>()); // same class
IntModel intModel;
IntModel* imPtr = &intModel;
QVERIFY(nullptr == imPtr->dynamicCast<FloatModel>()); // not a parent class
QCOMPARE(&intModel, imPtr->dynamicCast<AutomatableModel>()); // parent class
QCOMPARE(&intModel, imPtr->dynamicCast<IntModel>()); // same class
QVERIFY(nullptr == imPtr->dynamicCast<ComboBoxModel>()); // child class
}
} AutomatableModelTests;
#include "AutomatableModelTest.moc"