Merge branch 'stable-1.2'

# Conflicts:
#	.gitmodules
#	.travis.yml
#	.travis/linux..before_install.sh
#	.travis/linux..install.sh
#	CMakeLists.txt
#	cmake/linux/package_linux.sh.in
#	cmake/modules/BuildPlugin.cmake
#	include/AutomatableModel.h
#	plugins/MidiImport/MidiImport.cpp
#	plugins/carlapatchbay/CMakeLists.txt
#	plugins/carlarack/CMakeLists.txt
#	src/core/Song.cpp
#	src/core/Track.cpp
#	src/gui/editors/SongEditor.cpp
#	tests/src/core/AutomatableModelTest.cpp
This commit is contained in:
Hyunjin Song
2020-04-04 12:08:55 +09:00
40 changed files with 329 additions and 160 deletions

View File

@@ -499,8 +499,23 @@ void AutomatableModel::unlinkModel( AutomatableModel* model )
void AutomatableModel::linkModels( AutomatableModel* model1, AutomatableModel* model2 )
{
if (!model1->m_linkedModels.contains( model2 ) && model1 != model2)
{
// copy data
model1->m_value = model2->m_value;
if (model1->valueBuffer() && model2->valueBuffer())
{
std::copy_n(model2->valueBuffer()->data(),
model1->valueBuffer()->length(),
model1->valueBuffer()->data());
}
// send dataChanged() before linking (because linking will
// connect the two dataChanged() signals)
emit model1->dataChanged();
// finally: link the models
model1->linkModel( model2 );
model2->linkModel( model1 );
}
}

View File

@@ -537,6 +537,15 @@ void NotePlayHandle::processMidiTime( const MidiTime& time )
void NotePlayHandle::resize( const bpm_t _new_tempo )
{
if (origin() == OriginMidiInput ||
(origin() == OriginNoteStacking && m_parent->origin() == OriginMidiInput))
{
// Don't resize notes from MIDI input - they should continue to play
// until the key is released, and their large duration can cause
// overflows in this method.
return;
}
double completed = m_totalFramesPlayed / (double) m_frames;
double new_frames = m_origFrames * m_origTempo / (double) _new_tempo;
m_frames = (f_cnt_t)new_frames;

View File

@@ -102,14 +102,14 @@ RemotePlugin::RemotePlugin() :
m_socketFile = QDir::tempPath() + QDir::separator() +
QUuid::createUuid().toString();
const char * path = m_socketFile.toUtf8().constData();
size_t length = strlen( path );
auto path = m_socketFile.toUtf8();
size_t length = path.length();
if ( length >= sizeof sa.sun_path )
{
length = sizeof sa.sun_path - 1;
qWarning( "Socket path too long." );
}
memcpy( sa.sun_path, path, length );
memcpy(sa.sun_path, path.constData(), length );
sa.sun_path[length] = '\0';
m_server = socket( PF_LOCAL, SOCK_STREAM, 0 );
@@ -117,7 +117,7 @@ RemotePlugin::RemotePlugin() :
{
qWarning( "Unable to start the server." );
}
remove( path );
remove(path.constData());
int ret = bind( m_server, (struct sockaddr *) &sa, sizeof sa );
if ( ret == -1 || listen( m_server, 1 ) == -1 )
{

View File

@@ -182,7 +182,7 @@ QString RenderManager::pathForTrack(const Track *track, int num)
{
QString extension = ProjectRenderer::getFileExtensionFromFormat( m_format );
QString name = track->name();
name = name.remove(QRegExp("[^a-zA-Z]"));
name = name.remove(QRegExp(FILENAME_FILTER));
name = QString( "%1_%2%3" ).arg( num ).arg( name ).arg( extension );
return QDir(m_outputPath).filePath(name);
}

View File

@@ -225,6 +225,7 @@ void FileBrowser::addItems(const QString & path )
return;
}
// try to add all directories from file system alphabetically into the tree
QDir cdir( path );
QStringList files = cdir.entryList( QDir::Dirs, QDir::Name );
for( QStringList::const_iterator it = files.constBegin();
@@ -240,6 +241,7 @@ void FileBrowser::addItems(const QString & path )
m_fileBrowserTreeWidget->topLevelItem( i ) );
if( d == NULL || cur_file < d->text( 0 ) )
{
// insert before item, we're done
Directory *dd = new Directory( cur_file, path,
m_filter );
m_fileBrowserTreeWidget->insertTopLevelItem( i,dd );
@@ -249,6 +251,11 @@ void FileBrowser::addItems(const QString & path )
}
else if( cur_file == d->text( 0 ) )
{
// imagine we have subdirs named "TripleOscillator/xyz" in
// two directories from m_directories
// then only add one tree widget for both
// so we don't add a new Directory - we just
// add the path to the current directory
d->addDirectory( path );
d->update();
orphan = false;
@@ -257,6 +264,8 @@ void FileBrowser::addItems(const QString & path )
}
if( orphan )
{
// it has not yet been added yet, so it's (lexically)
// larger than all other dirs => append it at the bottom
Directory *d = new Directory( cur_file,
path, m_filter );
d->update();
@@ -768,21 +777,29 @@ void Directory::update( void )
if( !childCount() )
{
m_dirCount = 0;
// for all paths leading here, add their items
for( QStringList::iterator it = m_directories.begin();
it != m_directories.end(); ++it )
{
int top_index = childCount();
int filesBeforeAdd = childCount() - m_dirCount;
if( addItems( fullName( *it ) ) &&
( *it ).contains(
ConfigManager::inst()->dataDir() ) )
{
QTreeWidgetItem * sep = new QTreeWidgetItem;
sep->setText( 0,
FileBrowserTreeWidget::tr(
"--- Factory files ---" ) );
sep->setIcon( 0, embed::getIconPixmap(
"factory_files" ) );
insertChild( m_dirCount + top_index, sep );
// factory file directory is added
// note: those are always added last
int filesNow = childCount() - m_dirCount;
if(filesNow > filesBeforeAdd) // any file appended?
{
QTreeWidgetItem * sep = new QTreeWidgetItem;
sep->setText( 0,
FileBrowserTreeWidget::tr(
"--- Factory files ---" ) );
sep->setIcon( 0, embed::getIconPixmap(
"factory_files" ) );
// add delimeter after last file before appending our files
insertChild( filesBeforeAdd + m_dirCount, sep );
}
}
}
}
@@ -803,6 +820,7 @@ bool Directory::addItems(const QString & path )
bool added_something = false;
// try to add all directories from file system alphabetically into the tree
QStringList files = thisDir.entryList( QDir::Dirs, QDir::Name );
for( QStringList::const_iterator it = files.constBegin();
it != files.constEnd(); ++it )
@@ -817,6 +835,7 @@ bool Directory::addItems(const QString & path )
child( i ) );
if( d == NULL || cur_file < d->text( 0 ) )
{
// insert before item, we're done
insertChild( i, new Directory( cur_file,
path, m_filter ) );
orphan = false;
@@ -825,6 +844,12 @@ bool Directory::addItems(const QString & path )
}
else if( cur_file == d->text( 0 ) )
{
// imagine we have top-level subdirs named "TripleOscillator" in
// two directories from FileBrowser::m_directories
// and imagine both have a sub folder named "xyz"
// then only add one tree widget for both
// so we don't add a new Directory - we just
// add the path to the current directory
d->addDirectory( path );
orphan = false;
break;
@@ -832,6 +857,8 @@ bool Directory::addItems(const QString & path )
}
if( orphan )
{
// it has not yet been added yet, so it's (lexically)
// larger than all other dirs => append it at the bottom
addChild( new Directory( cur_file, path,
m_filter ) );
m_dirCount++;

View File

@@ -1305,6 +1305,7 @@ void MainWindow::sessionCleanup()
void MainWindow::focusOutEvent( QFocusEvent * _fe )
{
// TODO Remove this function, since it is apparently never actually called!
// when loosing focus we do not receive key-(release!)-events anymore,
// so we might miss release-events of one the modifiers we're watching!
clearKeyModifiers();

View File

@@ -2382,7 +2382,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me )
m_pattern->instrumentTrack()->processInEvent( evt );
}
}
else if( n->isPlaying() )
else if( n->isPlaying() && !isSelection() )
{
// mouse not over this note, stop playing it.
m_pattern->instrumentTrack()->pianoModel()->handleKeyRelease( n->key() );

View File

@@ -473,12 +473,13 @@ void SongEditor::toggleProportionalSnap()
void SongEditor::keyPressEvent( QKeyEvent * ke )
{
if( ke->modifiers() & Qt::ShiftModifier &&
bool isShiftPressed = ke->modifiers() & Qt::ShiftModifier;
if( isShiftPressed &&
( ke->key() == Qt::Key_Insert || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return ) )
{
m_song->insertBar();
}
else if( ke->modifiers() & Qt::ShiftModifier &&
else if( isShiftPressed &&
( ke->key() == Qt::Key_Delete || ke->key() == Qt::Key_Backspace ) )
{
m_song->removeBar();
@@ -517,7 +518,7 @@ void SongEditor::keyPressEvent( QKeyEvent * ke )
}
else if( ke->key() == Qt::Key_A && ke->modifiers() & Qt::ControlModifier )
{
selectAllTcos( !(ke->modifiers() & Qt::ShiftModifier) );
selectAllTcos( !isShiftPressed );
}
else if( ke->key() == Qt::Key_Escape )
{

View File

@@ -52,35 +52,28 @@ TextFloat * Knob::s_textFloat = NULL;
//! @todo: in C++11, we can use delegating ctors
#define DEFAULT_KNOB_INITIALIZER_LIST \
QWidget( _parent ), \
FloatModelView( new FloatModel( 0, 0, 0, 1, NULL, _name, true ), this ), \
m_label( "" ), \
m_knobPixmap( NULL ), \
m_volumeKnob( false ), \
m_volumeRatio( 100.0, 0.0, 1000000.0 ), \
m_buttonPressed( false ), \
m_angle( -10 ), \
m_lineWidth( 0 ), \
m_textColor( 255, 255, 255 )
Knob::Knob( knobTypes _knob_num, QWidget * _parent, const QString & _name ) :
DEFAULT_KNOB_INITIALIZER_LIST,
QWidget( _parent ),
FloatModelView( new FloatModel( 0, 0, 0, 1, NULL, _name, true ), this ),
m_label( "" ),
m_knobPixmap( NULL ),
m_volumeKnob( false ),
m_volumeRatio( 100.0, 0.0, 1000000.0 ),
m_buttonPressed( false ),
m_angle( -10 ),
m_lineWidth( 0 ),
m_textColor( 255, 255, 255 ),
m_knobNum( _knob_num )
{
initUi( _name );
}
Knob::Knob( QWidget * _parent, const QString & _name ) :
DEFAULT_KNOB_INITIALIZER_LIST,
m_knobNum( knobBright_26 )
Knob( knobBright_26, _parent, _name )
{
initUi( _name );
}
#undef DEFAULT_KNOB_INITIALIZER_LIST
@@ -606,7 +599,7 @@ void Knob::mousePressEvent( QMouseEvent * _me )
m_buttonPressed = true;
}
else if( _me->button() == Qt::LeftButton &&
gui->mainWindow()->isShiftPressed() == true )
(_me->modifiers() & Qt::ShiftModifier) )
{
new StringPairDrag( "float_value",
QString::number( model()->value() ),
@@ -808,9 +801,9 @@ void Knob::enterValue()
void Knob::friendlyUpdate()
{
if( model()->controllerConnection() == NULL ||
if (model() && (model()->controllerConnection() == NULL ||
model()->controllerConnection()->getController()->frequentUpdates() == false ||
Controller::runningFrames() % (256*4) == 0 )
Controller::runningFrames() % (256*4) == 0))
{
update();
}

View File

@@ -114,7 +114,7 @@ void LcdSpinBox::mouseMoveEvent( QMouseEvent* event )
if( m_mouseMoving )
{
int dy = event->globalY() - m_origMousePos.y();
if( gui->mainWindow()->isShiftPressed() )
if( event->modifiers() & Qt::ShiftModifier )
dy = qBound( -4, dy/4, 4 );
if( dy > 1 || dy < -1 )
{

View File

@@ -39,42 +39,32 @@
//! @todo: in C++11, we can use delegating ctors
#define DEFAULT_LCDWIDGET_INITIALIZER_LIST \
QWidget( parent ), \
m_label(), \
m_textColor( 255, 255, 255 ), \
m_textShadowColor( 64, 64, 64 )
LcdWidget::LcdWidget( QWidget* parent, const QString& name ) :
DEFAULT_LCDWIDGET_INITIALIZER_LIST,
m_numDigits( 1 )
LcdWidget( 1, parent, name )
{
initUi( name );
}
LcdWidget::LcdWidget( int numDigits, QWidget* parent, const QString& name ) :
DEFAULT_LCDWIDGET_INITIALIZER_LIST,
m_numDigits( numDigits )
LcdWidget( numDigits, QString("19green"), parent, name )
{
initUi( name );
}
LcdWidget::LcdWidget( int numDigits, const QString& style, QWidget* parent, const QString& name ) :
DEFAULT_LCDWIDGET_INITIALIZER_LIST,
QWidget( parent ),
m_label(),
m_textColor( 255, 255, 255 ),
m_textShadowColor( 64, 64, 64 ),
m_numDigits( numDigits )
{
initUi( name, style );
}
#undef DEFAULT_LCDWIDGET_INITIALIZER_LIST

View File

@@ -39,13 +39,9 @@ static const QString names[LedCheckBox::NumColors] =
//! @todo: in C++11, we can use delegating ctors
#define DEFAULT_LEDCHECKBOX_INITIALIZER_LIST \
AutomatableButton( _parent, _name )
LedCheckBox::LedCheckBox( const QString & _text, QWidget * _parent,
const QString & _name, LedColors _color ) :
DEFAULT_LEDCHECKBOX_INITIALIZER_LIST,
AutomatableButton( _parent, _name ),
m_text( _text )
{
initUi( _color );
@@ -56,13 +52,10 @@ LedCheckBox::LedCheckBox( const QString & _text, QWidget * _parent,
LedCheckBox::LedCheckBox( QWidget * _parent,
const QString & _name, LedColors _color ) :
DEFAULT_LEDCHECKBOX_INITIALIZER_LIST
LedCheckBox( QString(), _parent, _name, _color )
{
initUi( _color );
}
#undef DEFAULT_LEDCHECKBOX_INITIALIZER_LIST
LedCheckBox::~LedCheckBox()

View File

@@ -447,7 +447,7 @@ void InstrumentTrack::silenceAllNotes( bool removeIPH )
}
m_midiNotesMutex.unlock();
lock();
Engine::mixer()->requestChangeInModel();
// invalidate all NotePlayHandles and PresetPreviewHandles linked to this track
m_processHandles.clear();
@@ -457,7 +457,7 @@ void InstrumentTrack::silenceAllNotes( bool removeIPH )
flags |= PlayHandle::TypeInstrumentPlayHandle;
}
Engine::mixer()->removePlayHandlesOfTypes( this, flags );
unlock();
Engine::mixer()->doneChangeInModel();
}
@@ -546,11 +546,13 @@ void InstrumentTrack::setName( const QString & _new_name )
void InstrumentTrack::updateBaseNote()
{
Engine::mixer()->requestChangeInModel();
for( NotePlayHandleList::Iterator it = m_processHandles.begin();
it != m_processHandles.end(); ++it )
{
( *it )->setFrequencyUpdate();
}
Engine::mixer()->doneChangeInModel();
}
@@ -1599,7 +1601,7 @@ void InstrumentTrackWindow::saveSettingsBtnClicked()
sfd.setDirectory( presetRoot + m_track->instrumentName() );
sfd.setFileMode( FileDialog::AnyFile );
QString fname = m_track->name();
sfd.selectFile( fname.remove(QRegExp("[^a-zA-Z0-9_\\-\\d\\s]")) );
sfd.selectFile(fname.remove(QRegExp(FILENAME_FILTER)));
sfd.setDefaultSuffix( "xpf");
if( sfd.exec() == QDialog::Accepted &&