Merge branch 'stable-1.2'

# Conflicts:
#	.travis.yml
#	.travis/linux..script.sh
#	.travis/linux.win32.script.sh
#	.travis/linux.win64.script.sh
#	.travis/osx..install.sh
#	.travis/osx..script.sh
#	data/locale/en.ts
#	data/locale/id.ts
#	include/Graph.h
#	include/VstSyncController.h
#	include/lmms_math.h
#	plugins/vst_base/RemoteVstPlugin.cpp
#	src/core/RemotePlugin.cpp
#	src/core/Song.cpp
#	src/core/Track.cpp
#	src/gui/SubWindow.cpp
#	src/gui/widgets/Graph.cpp
This commit is contained in:
Hyunjin Song
2019-02-24 20:43:45 +09:00
92 changed files with 5767 additions and 3727 deletions

View File

@@ -466,7 +466,8 @@ void AutomatableModel::linkModel( AutomatableModel* model )
if( !model->hasLinkedModels() )
{
QObject::connect( this, SIGNAL( dataChanged() ), model, SIGNAL( dataChanged() ) );
QObject::connect( this, SIGNAL( dataChanged() ),
model, SIGNAL( dataChanged() ), Qt::DirectConnection );
}
}
}
@@ -522,7 +523,8 @@ void AutomatableModel::setControllerConnection( ControllerConnection* c )
m_controllerConnection = c;
if( c )
{
QObject::connect( m_controllerConnection, SIGNAL( valueChanged() ), this, SIGNAL( dataChanged() ) );
QObject::connect( m_controllerConnection, SIGNAL( valueChanged() ),
this, SIGNAL( dataChanged() ), Qt::DirectConnection );
QObject::connect( m_controllerConnection, SIGNAL( destroyed() ), this, SLOT( unlinkControllerConnection() ) );
m_valueChanged = true;
emit dataChanged();

View File

@@ -772,6 +772,16 @@ void AutomationPattern::resolveAllIDs()
{
a->addObject( dynamic_cast<AutomatableModel *>( o ), false );
}
else
{
// FIXME: Remove this block once the automation system gets fixed
// This is a temporary fix for https://github.com/LMMS/lmms/issues/3781
o = Engine::projectJournal()->journallingObject(ProjectJournal::idFromSave(*k));
if( o && dynamic_cast<AutomatableModel *>( o ) )
{
a->addObject( dynamic_cast<AutomatableModel *>( o ), false );
}
}
}
a->m_idsToResolve.clear();
a->dataChanged();

View File

@@ -117,7 +117,7 @@ void ControllerConnection::setController( Controller * _controller )
{
_controller->addConnection( this );
QObject::connect( _controller, SIGNAL( valueChanged() ),
this, SIGNAL( valueChanged() ) );
this, SIGNAL( valueChanged() ), Qt::DirectConnection );
}
m_ownsController =

View File

@@ -27,6 +27,9 @@
#include "ValueBuffer.h"
static bool s_NaNHandler;
namespace MixHelpers
{
@@ -68,10 +71,24 @@ bool isSilent( const sampleFrame* src, int frames )
return true;
}
bool useNaNHandler()
{
return s_NaNHandler;
}
void setNaNHandler( bool use )
{
s_NaNHandler = use;
}
/*! \brief Function for sanitizing a buffer of infs/nans - returns true if those are found */
bool sanitize( sampleFrame * src, int frames )
{
if( !useNaNHandler() )
{
return false;
}
bool found = false;
for( int f = 0; f < frames; ++f )
{
@@ -79,12 +96,23 @@ bool sanitize( sampleFrame * src, int frames )
{
if( isinf( src[f][c] ) || isnan( src[f][c] ) )
{
src[f][c] = 0.0f;
#ifdef LMMS_DEBUG
printf("Bad data, clearing buffer. frame: ");
printf("%d: value %f\n", f, src[f][c]);
#endif
for( int f = 0; f < frames; ++f )
{
for( int c = 0; c < 2; ++c )
{
src[f][c] = 0.0f;
}
}
found = true;
return found;
}
else
{
src[f][c] = qBound( -4.0f, src[f][c], 4.0f );
src[f][c] = qBound( -1000.0f, src[f][c], 1000.0f );
}
}
}
@@ -168,6 +196,13 @@ void addMultipliedByBuffers( sampleFrame* dst, const sampleFrame* src, ValueBuff
void addSanitizedMultipliedByBuffer( sampleFrame* dst, const sampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames )
{
if ( !useNaNHandler() )
{
addMultipliedByBuffer( dst, src, coeffSrc, coeffSrcBuf,
frames );
return;
}
for( int f = 0; f < frames; ++f )
{
dst[f][0] += ( isinf( src[f][0] ) || isnan( src[f][0] ) ) ? 0.0f : src[f][0] * coeffSrc * coeffSrcBuf->values()[f];
@@ -177,6 +212,13 @@ void addSanitizedMultipliedByBuffer( sampleFrame* dst, const sampleFrame* src, f
void addSanitizedMultipliedByBuffers( sampleFrame* dst, const sampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames )
{
if ( !useNaNHandler() )
{
addMultipliedByBuffers( dst, src, coeffSrcBuf1, coeffSrcBuf2,
frames );
return;
}
for( int f = 0; f < frames; ++f )
{
dst[f][0] += ( isinf( src[f][0] ) || isnan( src[f][0] ) )
@@ -205,6 +247,12 @@ struct AddSanitizedMultipliedOp
void addSanitizedMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames )
{
if ( !useNaNHandler() )
{
addMultiplied( dst, src, coeffSrc, frames );
return;
}
run<>( dst, src, frames, AddSanitizedMultipliedOp(coeffSrc) );
}

View File

@@ -177,11 +177,6 @@ void NotePlayHandle::setVolume( volume_t _volume )
void NotePlayHandle::setPanning( panning_t panning )
{
Note::setPanning( panning );
MidiEvent event( MidiMetaEvent, midiChannel(), midiKey(), panningToMidi( panning ) );
event.setMetaEvent( MidiNotePanning );
m_instrumentTrack->processOutEvent( event );
}

View File

@@ -164,6 +164,11 @@ jo_id_t ProjectJournal::idToSave( jo_id_t id )
return id & ~EO_ID_MSB;
}
jo_id_t ProjectJournal::idFromSave( jo_id_t id )
{
return id | EO_ID_MSB;
}

View File

@@ -55,7 +55,10 @@ ProcessWatcher::ProcessWatcher( RemotePlugin * _p ) :
void ProcessWatcher::run()
{
while( !m_quit && (m_plugin->isRunning() || m_plugin->messagesLeft()) )
m_plugin->m_process.start( m_plugin->m_exec, m_plugin->m_args );
exec();
m_plugin->m_process.moveToThread( m_plugin->thread() );
while( !m_quit && m_plugin->messagesLeft() )
{
msleep( 200 );
}
@@ -123,9 +126,13 @@ RemotePlugin::RemotePlugin() :
#endif
connect( &m_process, SIGNAL( finished( int, QProcess::ExitStatus ) ),
this, SLOT( processFinished( int, QProcess::ExitStatus ) ) );
this, SLOT( processFinished( int, QProcess::ExitStatus ) ),
Qt::DirectConnection );
connect( &m_process, SIGNAL( errorOccurred( QProcess::ProcessError ) ),
this, SLOT( processErrored( QProcess::ProcessError ) ) );
this, SLOT( processErrored( QProcess::ProcessError ) ),
Qt::DirectConnection );
connect( &m_process, SIGNAL( finished( int, QProcess::ExitStatus ) ),
&m_watcher, SLOT( quit() ), Qt::DirectConnection );
}
@@ -133,7 +140,7 @@ RemotePlugin::RemotePlugin() :
RemotePlugin::~RemotePlugin()
{
m_watcher.quit();
m_watcher.stop();
m_watcher.wait();
if( m_failed == false )
@@ -207,6 +214,11 @@ bool RemotePlugin::init(const QString &pluginExecutable,
return failed();
}
// ensure the watcher is ready in case we're running again
// (e.g. 32-bit VST plugins on Windows)
m_watcher.wait();
m_watcher.reset();
QStringList args;
#ifdef SYNC_WITH_SHM_FIFO
// swap in and out for bidirectional communication
@@ -219,7 +231,10 @@ bool RemotePlugin::init(const QString &pluginExecutable,
#ifndef DEBUG_REMOTE_PLUGIN
m_process.setProcessChannelMode( QProcess::ForwardedChannels );
m_process.setWorkingDirectory( QCoreApplication::applicationDirPath() );
m_process.start( exec, args );
m_exec = exec;
m_args = args;
// we start the process on the watcher thread to work around QTBUG-8819
m_process.moveToThread( &m_watcher );
m_watcher.start( QThread::LowestPriority );
#else
qDebug() << exec << args;

View File

@@ -103,7 +103,7 @@ void RenderManager::renderTracks()
Track* tk = (*it);
Track::TrackTypes type = tk->type();
// Don't mute automation tracks
// Don't render automation tracks
if ( tk->isMuted() == false &&
( type == Track::InstrumentTrack || type == Track::SampleTrack ) )
{
@@ -115,7 +115,11 @@ void RenderManager::renderTracks()
for( auto it = t2.begin(); it != t2.end(); ++it )
{
Track* tk = (*it);
if ( tk->isMuted() == false )
Track::TrackTypes type = tk->type();
// Don't render automation tracks
if ( tk->isMuted() == false &&
( type == Track::InstrumentTrack || type == Track::SampleTrack ) )
{
m_unmuted.push_back(tk);
}

View File

@@ -260,8 +260,6 @@ void Song::processNextBuffer()
m_playPos[m_playMode].setTicks(
tl->loopBegin().getTicks() );
m_vstSyncController.setAbsolutePosition(
tl->loopBegin().getTicks() );
m_vstSyncController.setPlaybackJumped( true );
emit updateSampleTracks();
@@ -288,8 +286,6 @@ void Song::processNextBuffer()
int ticks = m_playPos[m_playMode].getTicks() +
( int )( currentFrame / framesPerTick );
m_vstSyncController.setAbsolutePosition( ticks );
// did we play a whole tact?
if( ticks >= MidiTime::ticksPerTact() )
{
@@ -326,7 +322,6 @@ void Song::processNextBuffer()
// wrap milli second counter
setToTimeByTicks(ticks);
m_vstSyncController.setAbsolutePosition( ticks );
m_vstSyncController.setPlaybackJumped( true );
}
}
@@ -348,7 +343,6 @@ void Song::processNextBuffer()
m_playPos[m_playMode].setTicks( ticks );
setToTime(tl->loopBegin());
m_vstSyncController.setAbsolutePosition( ticks );
m_vstSyncController.setPlaybackJumped( true );
emit updateSampleTracks();
@@ -363,7 +357,17 @@ void Song::processNextBuffer()
m_playPos[m_playMode].setCurrentFrame( currentFrame );
}
f_cnt_t framesToPlay =
if( framesPlayed == 0 )
{
// update VST sync position after we've corrected frame/
// tick count but before actually playing any frames
m_vstSyncController.setAbsolutePosition(
m_playPos[m_playMode].getTicks()
+ m_playPos[m_playMode].currentFrame()
/ (double) framesPerTick );
}
f_cnt_t framesToPlay =
Engine::mixer()->framesPerPeriod() - framesPlayed;
f_cnt_t framesLeft = ( f_cnt_t )framesPerTick -
@@ -718,7 +722,10 @@ void Song::stop()
m_playPos[m_playMode].setCurrentFrame( 0 );
m_vstSyncController.setPlaybackState( m_exporting );
m_vstSyncController.setAbsolutePosition( m_playPos[m_playMode].getTicks() );
m_vstSyncController.setAbsolutePosition(
m_playPos[m_playMode].getTicks()
+ m_playPos[m_playMode].currentFrame()
/ (double) Engine::framesPerTick() );
// remove all note-play-handles that are active
Engine::mixer()->clear();

View File

@@ -909,10 +909,18 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me )
else if( m_action == MoveSelection )
{
const int dx = me->x() - m_initialMousePos.x();
const bool snap = !(me->modifiers() & Qt::ControlModifier) &&
me->button() == Qt::NoButton;
QVector<selectableObject *> so =
m_trackView->trackContainerView()->selectedObjects();
QVector<TrackContentObject *> tcos;
MidiTime smallest_pos, t;
int smallestPos = 0;
MidiTime dtick = MidiTime( static_cast<int>( dx *
MidiTime::ticksPerTact() / ppt ) );
if( snap )
{
dtick = dtick.toNearestTact();
}
// find out smallest position of all selected objects for not
// moving an object before zero
for( QVector<selectableObject *>::iterator it = so.begin();
@@ -926,23 +934,18 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me )
}
TrackContentObject * tco = tcov->m_tco;
tcos.push_back( tco );
smallest_pos = qMin<int>( smallest_pos,
(int)tco->startPosition() +
static_cast<int>( dx *
MidiTime::ticksPerTact() / ppt ) );
smallestPos = qMin<int>( smallestPos,
(int)tco->startPosition() + dtick );
}
dtick -= smallestPos;
if( snap )
{
dtick = dtick.toAbsoluteTact(); // round toward 0
}
for( QVector<TrackContentObject *>::iterator it = tcos.begin();
it != tcos.end(); ++it )
{
t = ( *it )->startPosition() +
static_cast<int>( dx *MidiTime::ticksPerTact() /
ppt )-smallest_pos;
if( ! ( me->modifiers() & Qt::ControlModifier )
&& me->button() == Qt::NoButton )
{
t = t.toNearestTact();
}
( *it )->movePosition( t );
( *it )->movePosition( ( *it )->startPosition() + dtick );
}
}
else if( m_action == Resize || m_action == ResizeLeft )

View File

@@ -136,12 +136,12 @@ VstSyncController::~VstSyncController()
void VstSyncController::setAbsolutePosition( int ticks )
void VstSyncController::setAbsolutePosition( double ticks )
{
#ifdef VST_SNC_LATENCY
m_syncData->ppqPos = ( ( ticks + 0 ) / (float)48 ) - m_syncData->m_latency;
m_syncData->ppqPos = ( ( ticks + 0 ) / 48.0 ) - m_syncData->m_latency;
#else
m_syncData->ppqPos = ( ( ticks + 0 ) / (float)48 );
m_syncData->ppqPos = ( ( ticks + 0 ) / 48.0 );
#endif
}

View File

@@ -118,7 +118,9 @@ void AudioPort::doProcessing()
{
if( ph->buffer() )
{
if( ph->usesBuffer() )
if( ph->usesBuffer()
&& ( ph->type() == PlayHandle::TypeNotePlayHandle
|| !MixHelpers::isSilent( ph->buffer(), fpp ) ) )
{
m_bufferUsage = true;
MixHelpers::add( m_portBuffer, ph->buffer(), fpp );

View File

@@ -27,6 +27,10 @@
#include "AudioPortAudio.h"
#ifndef LMMS_HAVE_PORTAUDIO
void AudioPortAudioSetupUtil::updateBackends()
{
}
void AudioPortAudioSetupUtil::updateDevices()
{
}
@@ -328,6 +332,28 @@ int AudioPortAudio::_process_callback(
void AudioPortAudioSetupUtil::updateBackends()
{
PaError err = Pa_Initialize();
if( err != paNoError ) {
printf( "Couldn't initialize PortAudio: %s\n", Pa_GetErrorText( err ) );
return;
}
const PaHostApiInfo * hi;
for( int i = 0; i < Pa_GetHostApiCount(); ++i )
{
hi = Pa_GetHostApiInfo( i );
m_backendModel.addItem( hi->name );
}
Pa_Terminate();
}
void AudioPortAudioSetupUtil::updateDevices()
{
PaError err = Pa_Initialize();
@@ -409,37 +435,6 @@ AudioPortAudio::setupWidget::setupWidget( QWidget * _parent ) :
m_channels->setLabel( tr( "CHANNELS" ) );
m_channels->move( 308, 20 );*/
// Setup models
PaError err = Pa_Initialize();
if( err != paNoError ) {
printf( "Couldn't initialize PortAudio: %s\n", Pa_GetErrorText( err ) );
return;
}
// todo: setup backend model
const PaHostApiInfo * hi;
for( int i = 0; i < Pa_GetHostApiCount(); ++i )
{
hi = Pa_GetHostApiInfo( i );
m_setupUtil.m_backendModel.addItem( hi->name );
}
Pa_Terminate();
const QString& backend = ConfigManager::inst()->value( "audioportaudio",
"backend" );
const QString& device = ConfigManager::inst()->value( "audioportaudio",
"device" );
int i = qMax( 0, m_setupUtil.m_backendModel.findText( backend ) );
m_setupUtil.m_backendModel.setValue( i );
m_setupUtil.updateDevices();
i = qMax( 0, m_setupUtil.m_deviceModel.findText( device ) );
m_setupUtil.m_deviceModel.setValue( i );
connect( &m_setupUtil.m_backendModel, SIGNAL( dataChanged() ),
&m_setupUtil, SLOT( updateDevices() ) );
@@ -478,6 +473,33 @@ void AudioPortAudio::setupWidget::saveSettings()
}
void AudioPortAudio::setupWidget::show()
{
if( m_setupUtil.m_backendModel.size() == 0 )
{
// populate the backend model the first time we are shown
m_setupUtil.updateBackends();
const QString& backend = ConfigManager::inst()->value(
"audioportaudio", "backend" );
const QString& device = ConfigManager::inst()->value(
"audioportaudio", "device" );
int i = qMax( 0, m_setupUtil.m_backendModel.findText( backend ) );
m_setupUtil.m_backendModel.setValue( i );
m_setupUtil.updateDevices();
i = qMax( 0, m_setupUtil.m_deviceModel.findText( device ) );
m_setupUtil.m_deviceModel.setValue( i );
}
AudioDeviceSetupWidget::show();
}
#endif

View File

@@ -65,6 +65,7 @@
#include "GuiApplication.h"
#include "ImportFilter.h"
#include "MainWindow.h"
#include "MixHelpers.h"
#include "OutputSettings.h"
#include "ProjectRenderer.h"
#include "RenderManager.h"
@@ -343,7 +344,13 @@ int main( int argc, char * * argv )
return EXIT_FAILURE;
}
#endif
#ifdef LMMS_BUILD_LINUX
// don't let OS steal the menu bar. FIXME: only effective on Qt4
QCoreApplication::setAttribute( Qt::AA_DontUseNativeMenuBar );
#endif
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QCoreApplication * app = coreOnly ?
new QCoreApplication( argc, argv ) :
new MainApplication( argc, argv );
@@ -688,6 +695,10 @@ int main( int argc, char * * argv )
ConfigManager::inst()->loadConfigFile(configFile);
// Hidden settings
MixHelpers::setNaNHandler( ConfigManager::inst()->value( "app",
"nanhandler", "1" ).toInt() );
// set language
QString pos = ConfigManager::inst()->value( "app", "language" );
if( pos.isEmpty() )

View File

@@ -73,7 +73,7 @@ FxMixerView::FxMixerView() :
// Set margins
ml->setContentsMargins( 0, 4, 0, 0 );
// Channel area
m_channelAreaWidget = new QWidget;
chLayout = new QHBoxLayout( m_channelAreaWidget );
@@ -138,9 +138,9 @@ FxMixerView::FxMixerView() :
ml->addWidget( newChannelBtn, 0, Qt::AlignTop );
// add the stacked layout for the effect racks of fx channels
// add the stacked layout for the effect racks of fx channels
ml->addWidget( m_racksWidget, 0, Qt::AlignTop | Qt::AlignRight );
setCurrentFxLine( m_fxChannelViews[0]->m_fxLine );
setLayout( ml );
@@ -219,10 +219,10 @@ void FxMixerView::refreshDisplay()
chLayout->addWidget(m_fxChannelViews[i]->m_fxLine);
m_racksLayout->addWidget( m_fxChannelViews[i]->m_rackView );
}
// set selected fx line to 0
setCurrentFxLine( 0 );
// update all fx lines
for( int i = 0; i < m_fxChannelViews.size(); ++i )
{
@@ -308,7 +308,7 @@ FxMixerView::FxChannelView::FxChannelView(QWidget * _parent, FxMixerView * _mv,
connect(&fxChannel->m_soloModel, SIGNAL( dataChanged() ),
_mv, SLOT ( toggledSolo() ) );
ToolTip::add( m_soloBtn, tr( "Solo FX channel" ) );
// Create EffectRack for the channel
m_rackView = new EffectRackView( &fxChannel->m_fxChain, _mv->m_racksWidget );
m_rackView->setFixedSize( 245, FxLine::FxLineHeight );
@@ -354,6 +354,8 @@ void FxMixerView::updateFxLine(int index)
// does current channel send to this channel?
int selIndex = m_currentFxLine->channelIndex();
FxLine * thisLine = m_fxChannelViews[index]->m_fxLine;
thisLine->setToolTip( Engine::fxMixer()->effectChannel( index )->m_name );
FloatModel * sendModel = mix->channelSendModel(selIndex, index);
if( sendModel == NULL )
{

View File

@@ -55,11 +55,6 @@ GuiApplication* GuiApplication::instance()
GuiApplication::GuiApplication()
{
// enable HiDPI scaling before showing anything (Qt 5.6+ only)
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);
#endif
// prompt the user to create the LMMS working directory (e.g. ~/Documents/lmms) if it doesn't exist
if ( !ConfigManager::inst()->hasWorkingDir() &&
QMessageBox::question( NULL,

View File

@@ -100,6 +100,8 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) :
"disablebackup" ).toInt() ),
m_openLastProject( ConfigManager::inst()->value( "app",
"openlastproject" ).toInt() ),
m_NaNHandler( ConfigManager::inst()->value( "app",
"nanhandler", "1" ).toInt() ),
m_hqAudioDev( ConfigManager::inst()->value( "mixer",
"hqaudio" ).toInt() ),
m_lang( ConfigManager::inst()->value( "app",
@@ -128,7 +130,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) :
m_compactTrackButtons( ConfigManager::inst()->value( "ui",
"compacttrackbuttons" ).toInt() ),
m_syncVSTPlugins( ConfigManager::inst()->value( "ui",
"syncvstplugins" ).toInt() ),
"syncvstplugins", "1" ).toInt() ),
m_animateAFP(ConfigManager::inst()->value( "ui",
"animateafp", "1" ).toInt() ),
m_printNoteLabels(ConfigManager::inst()->value( "ui",
@@ -136,7 +138,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) :
m_displayWaveform(ConfigManager::inst()->value( "ui",
"displaywaveform").toInt() ),
m_disableAutoQuit(ConfigManager::inst()->value( "ui",
"disableautoquit").toInt() ),
"disableautoquit", "1" ).toInt() ),
m_vstEmbedMethod( ConfigManager::inst()->vstEmbedMethod() )
{
setWindowIcon( embed::getIconPixmap( "setup_general" ) );
@@ -247,6 +249,15 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) :
misc_tw->setFixedHeight( YDelta*labelNumber + HeaderSize );
// Advanced setting, hidden for now
if( false )
{
LedCheckBox * useNaNHandler = new LedCheckBox(
tr( "Use built-in NaN handler" ),
misc_tw );
useNaNHandler->setChecked( m_NaNHandler );
}
TabWidget* embed_tw = new TabWidget( tr( "PLUGIN EMBEDDING" ), general);
embed_tw->setFixedHeight( 48 );
m_vstEmbedComboBox = new QComboBox( embed_tw );
@@ -815,6 +826,8 @@ void SetupDialog::accept()
QString::number( !m_disableBackup ) );
ConfigManager::inst()->setValue( "app", "openlastproject",
QString::number( m_openLastProject ) );
ConfigManager::inst()->setValue( "app", "nanhandler",
QString::number( m_NaNHandler ) );
ConfigManager::inst()->setValue( "mixer", "hqaudio",
QString::number( m_hqAudioDev ) );
ConfigManager::inst()->setValue( "ui", "smoothscroll",

View File

@@ -174,32 +174,6 @@ void SubWindow::elideText( QLabel *label, QString text )
/**
* @brief SubWindow::isMaximized
*
* This function checks if the subwindow is maximized.
* QMdiSubWindow::isMaximized() doesn't work on MacOS.
* Therefore we need our own implementation for checking this
* @return true if the subwindow is maximized at the moment.
* false if it's not.
*/
bool SubWindow::isMaximized()
{
#ifdef LMMS_BUILD_APPLE
// check if subwindow size is identical to the MdiArea size, accounting for scrollbars
int hScrollBarHeight = mdiArea()->horizontalScrollBar()->isVisible() ? mdiArea()->horizontalScrollBar()->size().height() : 0;
int vScrollBarWidth = mdiArea()->verticalScrollBar()->isVisible() ? mdiArea()->verticalScrollBar()->size().width() : 0;
QSize areaSize( this->mdiArea()->size().width() - vScrollBarWidth, this->mdiArea()->size().height() - hScrollBarHeight );
return areaSize == this->size();
#else
return QMdiSubWindow::isMaximized();
#endif
}
/**
* @brief SubWindow::getTrueNormalGeometry
*
@@ -388,8 +362,11 @@ void SubWindow::focusChanged( QMdiSubWindow *subWindow )
*/
void SubWindow::resizeEvent( QResizeEvent * event )
{
adjustTitleBar();
// When the parent QMdiArea gets resized, maximized subwindows also gets resized, if any.
// In that case, we should call QMdiSubWindow::resizeEvent first
// to ensure we get the correct window state.
QMdiSubWindow::resizeEvent( event );
adjustTitleBar();
// if the window was resized and ISN'T minimized/maximized/fullscreen,
// then save the current size

View File

@@ -89,7 +89,7 @@ TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppt,
QTimer * updateTimer = new QTimer( this );
connect( updateTimer, SIGNAL( timeout() ),
this, SLOT( updatePosition() ) );
updateTimer->start( 50 );
updateTimer->start( 1000 / 60 ); // 60 fps
connect( Engine::getSong(), SIGNAL( timeSignatureChanged( int,int ) ),
this, SLOT( update() ) );
}

View File

@@ -1076,6 +1076,7 @@ void PianoRoll::shiftSemiTone( int amount ) // shift notes by amount semitones
{
if (!hasValidPattern()) {return;}
m_pattern->addJournalCheckPoint();
bool useAllNotes = ! isSelection();
for( Note *note : m_pattern->notes() )
{
@@ -1102,6 +1103,7 @@ void PianoRoll::shiftPos( int amount ) //shift notes pos by amount
{
if (!hasValidPattern()) {return;}
m_pattern->addJournalCheckPoint();
bool useAllNotes = ! isSelection();
bool first = true;

View File

@@ -746,6 +746,16 @@ void SongEditorWindow::resizeEvent(QResizeEvent *event)
}
void SongEditorWindow::changeEvent(QEvent *event)
{
QWidget::changeEvent(event);
if (event->type() == QEvent::WindowStateChange)
{
m_editor->realignTracks();
}
}
void SongEditorWindow::play()
{
emit playTriggered();

View File

@@ -70,14 +70,14 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) :
m_autoQuit = new TempoSyncKnob( knobBright_26, this );
m_autoQuit->setLabel( tr( "DECAY" ) );
m_autoQuit->move( 60, 5 );
m_autoQuit->setEnabled( isEnabled );
m_autoQuit->setEnabled( isEnabled && !effect()->m_autoQuitDisabled );
m_autoQuit->setHintText( tr( "Time:" ), "ms" );
m_gate = new Knob( knobBright_26, this );
m_gate->setLabel( tr( "GATE" ) );
m_gate->move( 93, 5 );
m_gate->setEnabled( isEnabled );
m_gate->setEnabled( isEnabled && !effect()->m_autoQuitDisabled );
m_gate->setHintText( tr( "Gate:" ), "" );

View File

@@ -419,9 +419,9 @@ void EnvelopeAndLfoView::paintEvent( QPaintEvent * )
p.fillRect( x5, y_base - 1, 2, 2, end_points_color );
int LFO_GRAPH_W = s_lfoGraph->width() - 6; // substract border
int LFO_GRAPH_W = s_lfoGraph->width() - 3; // substract border
int LFO_GRAPH_H = s_lfoGraph->height() - 6; // substract border
int graph_x_base = LFO_GRAPH_X + 3;
int graph_x_base = LFO_GRAPH_X + 2;
int graph_y_base = LFO_GRAPH_Y + 3 + LFO_GRAPH_H / 2;
const float frames_for_graph = SECS_PER_LFO_OSCILLATION *

View File

@@ -720,6 +720,15 @@ void graphModel::clear()
}
// Clear any part of the graph that isn't displayed
void graphModel::clearInvisible()
{
const int graph_length = length();
const int full_graph_length = m_samples.size();
for( int i = graph_length; i < full_graph_length; i++ )
m_samples[i] = 0;
emit samplesChanged( graph_length, full_graph_length - 1 );
}
void graphModel::drawSampleAt( int x, float val )
{

View File

@@ -126,10 +126,14 @@ InstrumentTrack::InstrumentTrack( TrackContainer* tc ) :
setName( tr( "Default preset" ) );
connect( &m_baseNoteModel, SIGNAL( dataChanged() ), this, SLOT( updateBaseNote() ) );
connect( &m_pitchModel, SIGNAL( dataChanged() ), this, SLOT( updatePitch() ) );
connect( &m_pitchRangeModel, SIGNAL( dataChanged() ), this, SLOT( updatePitchRange() ) );
connect( &m_effectChannelModel, SIGNAL( dataChanged() ), this, SLOT( updateEffectChannel() ) );
connect( &m_baseNoteModel, SIGNAL( dataChanged() ),
this, SLOT( updateBaseNote() ), Qt::DirectConnection );
connect( &m_pitchModel, SIGNAL( dataChanged() ),
this, SLOT( updatePitch() ), Qt::DirectConnection );
connect( &m_pitchRangeModel, SIGNAL( dataChanged() ),
this, SLOT( updatePitchRange() ), Qt::DirectConnection );
connect( &m_effectChannelModel, SIGNAL( dataChanged() ),
this, SLOT( updateEffectChannel() ), Qt::DirectConnection );
}