Merge branch 'master' into instr-sub-plugins
This commit is contained in:
2
src/3rdparty/rpmalloc/rpmalloc
vendored
2
src/3rdparty/rpmalloc/rpmalloc
vendored
Submodule src/3rdparty/rpmalloc/rpmalloc updated: 36b1942fbc...b5bdc18051
@@ -45,7 +45,7 @@ INCLUDE(GenQrc)
|
||||
ADD_GEN_QRC(LMMS_RCC_OUT lmms.qrc
|
||||
"${CMAKE_SOURCE_DIR}/doc/AUTHORS"
|
||||
"${CMAKE_SOURCE_DIR}/LICENSE.txt"
|
||||
"${CMAKE_BINARY_DIR}/CONTRIBUTORS"
|
||||
"${CONTRIBUTORS}"
|
||||
)
|
||||
|
||||
# Paths relative to lmms executable
|
||||
|
||||
@@ -89,16 +89,23 @@ bool AutomatableModel::isAutomated() const
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool AutomatableModel::mustQuoteName(const QString& name)
|
||||
{
|
||||
QRegExp reg("^[A-Za-z0-9._-]+$");
|
||||
return !reg.exactMatch(name);
|
||||
}
|
||||
|
||||
void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, const QString& name )
|
||||
{
|
||||
bool mustQuote = mustQuoteName(name);
|
||||
|
||||
if( isAutomated() || m_scaleType != Linear )
|
||||
{
|
||||
// automation needs tuple of data (name, id, value)
|
||||
// scale type also needs an extra value
|
||||
// => it must be appended as a node
|
||||
|
||||
QRegExp reg("^[A-Za-z0-9._-]+$");
|
||||
bool mustQuote = !reg.exactMatch(name);
|
||||
QDomElement me = doc.createElement( mustQuote ? QString("automatablemodel") : name );
|
||||
me.setAttribute( "id", ProjectJournal::idToSave( id() ) );
|
||||
me.setAttribute( "value", m_value );
|
||||
@@ -110,8 +117,18 @@ void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, co
|
||||
}
|
||||
else
|
||||
{
|
||||
// non automation, linear scale (default), can be saved as attribute
|
||||
element.setAttribute( name, m_value );
|
||||
if(mustQuote)
|
||||
{
|
||||
QDomElement me = doc.createElement( "automatablemodel" );
|
||||
me.setAttribute( "nodename", name );
|
||||
me.setAttribute( "value", m_value );
|
||||
element.appendChild( me );
|
||||
}
|
||||
else
|
||||
{
|
||||
// non automation, linear scale (default), can be saved as attribute
|
||||
element.setAttribute( name, m_value );
|
||||
}
|
||||
}
|
||||
|
||||
if( m_controllerConnection && m_controllerConnection->getController()->type()
|
||||
@@ -131,7 +148,13 @@ void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, co
|
||||
element.appendChild( controllerElement );
|
||||
}
|
||||
|
||||
QDomElement element = doc.createElement( name );
|
||||
bool mustQuote = mustQuoteName(name);
|
||||
QString elementName = mustQuote ? "controllerconnection"
|
||||
: name;
|
||||
|
||||
QDomElement element = doc.createElement( elementName );
|
||||
if(mustQuote)
|
||||
element.setAttribute( "nodename", name );
|
||||
m_controllerConnection->saveSettings( doc, element );
|
||||
|
||||
controllerElement.appendChild( element );
|
||||
@@ -170,6 +193,17 @@ void AutomatableModel::loadSettings( const QDomElement& element, const QString&
|
||||
if( connectionNode.isElement() )
|
||||
{
|
||||
QDomNode thisConnection = connectionNode.toElement().namedItem( name );
|
||||
if( !thisConnection.isElement() )
|
||||
{
|
||||
thisConnection = connectionNode.toElement().namedItem( "controllerconnection" );
|
||||
QDomElement tcElement = thisConnection.toElement();
|
||||
// sanity check
|
||||
if( tcElement.isNull() || tcElement.attribute( "nodename" ) != name )
|
||||
{
|
||||
// no, that wasn't it, act as if we never found one
|
||||
thisConnection.clear();
|
||||
}
|
||||
}
|
||||
if( thisConnection.isElement() )
|
||||
{
|
||||
setControllerConnection( new ControllerConnection( (Controller*)NULL ) );
|
||||
@@ -432,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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -488,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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -67,6 +67,7 @@ set(LMMS_SRCS
|
||||
core/TrackContainer.cpp
|
||||
core/ValueBuffer.cpp
|
||||
core/VstSyncController.cpp
|
||||
core/StepRecorder.cpp
|
||||
|
||||
core/audio/AudioAlsa.cpp
|
||||
core/audio/AudioDevice.cpp
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "Song.h"
|
||||
|
||||
#include "InstrumentTrack.h"
|
||||
#include "SampleTrack.h"
|
||||
#include "BBTrackContainer.h"
|
||||
|
||||
FxRoute::FxRoute( FxChannel * from, FxChannel * to, float amount ) :
|
||||
@@ -305,6 +306,22 @@ void FxMixer::deleteChannel( int index )
|
||||
inst->effectChannelModel()->setValue(val-1);
|
||||
}
|
||||
}
|
||||
else if( t->type() == Track::SampleTrack )
|
||||
{
|
||||
SampleTrack* strk = dynamic_cast<SampleTrack *>( t );
|
||||
int val = strk->effectChannelModel()->value(0);
|
||||
if( val == index )
|
||||
{
|
||||
// we are deleting this track's fx send
|
||||
// send to master
|
||||
strk->effectChannelModel()->setValue(0);
|
||||
}
|
||||
else if( val > index )
|
||||
{
|
||||
// subtract 1 to make up for the missing channel
|
||||
strk->effectChannelModel()->setValue(val-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FxChannel * ch = m_fxChannels[index];
|
||||
@@ -379,6 +396,19 @@ void FxMixer::moveChannelLeft( int index )
|
||||
inst->effectChannelModel()->setValue(a);
|
||||
}
|
||||
}
|
||||
else if( trackList[i]->type() == Track::SampleTrack )
|
||||
{
|
||||
SampleTrack * strk = (SampleTrack *) trackList[i];
|
||||
int val = strk->effectChannelModel()->value(0);
|
||||
if( val == a )
|
||||
{
|
||||
strk->effectChannelModel()->setValue(b);
|
||||
}
|
||||
else if( val == b )
|
||||
{
|
||||
strk->effectChannelModel()->setValue(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -780,4 +810,3 @@ void FxMixer::validateChannelName( int index, int oldIndex )
|
||||
m_fxChannels[index]->m_name = tr( "FX %1" ).arg( index );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,11 @@
|
||||
#include "lmms_math.h"
|
||||
#include "ValueBuffer.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
|
||||
static bool s_NaNHandler;
|
||||
|
||||
|
||||
namespace MixHelpers
|
||||
{
|
||||
@@ -68,10 +73,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 +98,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 +198,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 +214,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 +249,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) );
|
||||
}
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -218,6 +218,7 @@ Plugin * Plugin::instantiate(const QString& pluginName, Model * parent,
|
||||
{
|
||||
const PluginFactory::PluginInfo& pi = pluginFactory->pluginInfo(pluginName.toUtf8());
|
||||
|
||||
Plugin* inst;
|
||||
if( pi.isNull() )
|
||||
{
|
||||
if( gui )
|
||||
@@ -228,28 +229,31 @@ Plugin * Plugin::instantiate(const QString& pluginName, Model * parent,
|
||||
arg( pluginName ).arg( pluginFactory->errorString(pluginName) ),
|
||||
QMessageBox::Ok | QMessageBox::Default );
|
||||
}
|
||||
return new DummyPlugin();
|
||||
}
|
||||
|
||||
Plugin* inst;
|
||||
InstantiationHook instantiationHook;
|
||||
if ((instantiationHook = ( InstantiationHook ) pi.library->resolve( "lmms_plugin_main" )))
|
||||
{
|
||||
inst = instantiationHook(parent, data);
|
||||
inst = new DummyPlugin();
|
||||
}
|
||||
else
|
||||
{
|
||||
if( gui )
|
||||
InstantiationHook instantiationHook;
|
||||
if ((instantiationHook = ( InstantiationHook ) pi.library->resolve( "lmms_plugin_main" )))
|
||||
{
|
||||
QMessageBox::information( NULL,
|
||||
tr( "Error while loading plugin" ),
|
||||
tr( "Failed to load plugin \"%1\"!").arg( pluginName ),
|
||||
QMessageBox::Ok | QMessageBox::Default );
|
||||
inst = instantiationHook(parent, data);
|
||||
if(!inst) {
|
||||
inst = new DummyPlugin();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( gui )
|
||||
{
|
||||
QMessageBox::information( NULL,
|
||||
tr( "Error while loading plugin" ),
|
||||
tr( "Failed to load plugin \"%1\"!").arg( pluginName ),
|
||||
QMessageBox::Ok | QMessageBox::Default );
|
||||
}
|
||||
inst = new DummyPlugin();
|
||||
}
|
||||
return new DummyPlugin();
|
||||
}
|
||||
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -185,25 +185,17 @@ void ProjectRenderer::run()
|
||||
// Skip first empty buffer.
|
||||
Engine::mixer()->nextBuffer();
|
||||
|
||||
const Song::PlayPos & exportPos = Engine::getSong()->getPlayPos(
|
||||
Song::Mode_PlaySong );
|
||||
m_progress = 0;
|
||||
std::pair<MidiTime, MidiTime> exportEndpoints = Engine::getSong()->getExportEndpoints();
|
||||
tick_t startTick = exportEndpoints.first.getTicks();
|
||||
tick_t endTick = exportEndpoints.second.getTicks();
|
||||
tick_t lengthTicks = endTick - startTick;
|
||||
|
||||
// Now start processing
|
||||
Engine::mixer()->startProcessing(false);
|
||||
|
||||
// Continually track and emit progress percentage to listeners.
|
||||
while( exportPos.getTicks() < endTick &&
|
||||
Engine::getSong()->isExporting() == true
|
||||
&& !m_abort )
|
||||
while (!Engine::getSong()->isExportDone() && !m_abort)
|
||||
{
|
||||
m_fileDev->processNextBuffer();
|
||||
const int nprog = lengthTicks == 0 ? 100 : (exportPos.getTicks()-startTick) * 100 / lengthTicks;
|
||||
if( m_progress != nprog )
|
||||
const int nprog = Engine::getSong()->getExportProgress();
|
||||
if (m_progress != nprog)
|
||||
{
|
||||
m_progress = nprog;
|
||||
emit progressChanged( m_progress );
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -86,7 +86,9 @@ Song::Song() :
|
||||
m_patternToPlay( NULL ),
|
||||
m_loopPattern( false ),
|
||||
m_elapsedTicks( 0 ),
|
||||
m_elapsedTacts( 0 )
|
||||
m_elapsedTacts( 0 ),
|
||||
m_loopRenderCount(1),
|
||||
m_loopRenderRemaining(1)
|
||||
{
|
||||
for(int i = 0; i < Mode_Count; ++i) m_elapsedMilliSeconds[i] = 0;
|
||||
connect( &m_tempoModel, SIGNAL( dataChanged() ),
|
||||
@@ -258,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();
|
||||
@@ -286,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() )
|
||||
{
|
||||
@@ -324,13 +322,12 @@ void Song::processNextBuffer()
|
||||
// wrap milli second counter
|
||||
setToTimeByTicks(ticks);
|
||||
|
||||
m_vstSyncController.setAbsolutePosition( ticks );
|
||||
m_vstSyncController.setPlaybackJumped( true );
|
||||
}
|
||||
}
|
||||
m_playPos[m_playMode].setTicks( ticks );
|
||||
|
||||
if( checkLoop )
|
||||
if (checkLoop || m_loopRenderRemaining > 1)
|
||||
{
|
||||
m_vstSyncController.startCycle(
|
||||
tl->loopBegin().getTicks(), tl->loopEnd().getTicks() );
|
||||
@@ -340,11 +337,12 @@ void Song::processNextBuffer()
|
||||
// beginning of the range
|
||||
if( m_playPos[m_playMode] >= tl->loopEnd() )
|
||||
{
|
||||
if (m_loopRenderRemaining > 1)
|
||||
m_loopRenderRemaining--;
|
||||
ticks = tl->loopBegin().getTicks();
|
||||
m_playPos[m_playMode].setTicks( ticks );
|
||||
setToTime(tl->loopBegin());
|
||||
|
||||
m_vstSyncController.setAbsolutePosition( ticks );
|
||||
m_vstSyncController.setPlaybackJumped( true );
|
||||
|
||||
emit updateSampleTracks();
|
||||
@@ -359,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 -
|
||||
@@ -476,29 +484,41 @@ void Song::setModified(bool value)
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<MidiTime, MidiTime> Song::getExportEndpoints() const
|
||||
bool Song::isExportDone() const
|
||||
{
|
||||
if ( m_renderBetweenMarkers )
|
||||
return !isExporting() || m_playPos[m_playMode] >= m_exportSongEnd;
|
||||
}
|
||||
|
||||
int Song::getExportProgress() const
|
||||
{
|
||||
MidiTime pos = m_playPos[m_playMode];
|
||||
|
||||
if (pos >= m_exportSongEnd)
|
||||
{
|
||||
return std::pair<MidiTime, MidiTime>(
|
||||
m_playPos[Mode_PlaySong].m_timeLine->loopBegin(),
|
||||
m_playPos[Mode_PlaySong].m_timeLine->loopEnd()
|
||||
);
|
||||
return 100;
|
||||
}
|
||||
else if ( m_exportLoop )
|
||||
else if (pos <= m_exportSongBegin)
|
||||
{
|
||||
return std::pair<MidiTime, MidiTime>( MidiTime(0, 0), MidiTime(m_length, 0) );
|
||||
return 0;
|
||||
}
|
||||
else if (pos >= m_exportLoopEnd)
|
||||
{
|
||||
pos = (m_exportLoopBegin-m_exportSongBegin) + (m_exportLoopEnd - m_exportLoopBegin) *
|
||||
m_loopRenderCount + (pos - m_exportLoopEnd);
|
||||
}
|
||||
else if ( pos >= m_exportLoopBegin )
|
||||
{
|
||||
pos = (m_exportLoopBegin-m_exportSongBegin) + ((m_exportLoopEnd - m_exportLoopBegin) *
|
||||
(m_loopRenderCount - m_loopRenderRemaining)) + (pos - m_exportLoopBegin);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not exporting as a loop, we leave one bar of padding at the end of the song to accomodate reverb, etc.
|
||||
return std::pair<MidiTime, MidiTime>( MidiTime(0, 0), MidiTime(m_length+1, 0) );
|
||||
pos = (pos - m_exportSongBegin);
|
||||
}
|
||||
|
||||
return (float)pos/(float)m_exportEffectiveLength*100.0f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Song::playSong()
|
||||
{
|
||||
m_recording = false;
|
||||
@@ -702,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();
|
||||
@@ -719,15 +742,41 @@ void Song::stop()
|
||||
void Song::startExport()
|
||||
{
|
||||
stop();
|
||||
if(m_renderBetweenMarkers)
|
||||
if (m_renderBetweenMarkers)
|
||||
{
|
||||
m_exportSongBegin = m_exportLoopBegin = m_playPos[Mode_PlaySong].m_timeLine->loopBegin();
|
||||
m_exportSongEnd = m_exportLoopEnd = m_playPos[Mode_PlaySong].m_timeLine->loopEnd();
|
||||
|
||||
m_playPos[Mode_PlaySong].setTicks( m_playPos[Mode_PlaySong].m_timeLine->loopBegin().getTicks() );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_exportSongEnd = MidiTime(m_length, 0);
|
||||
|
||||
// Handle potentially ridiculous loop points gracefully.
|
||||
if (m_loopRenderCount > 1 && m_playPos[Mode_PlaySong].m_timeLine->loopEnd() > m_exportSongEnd)
|
||||
{
|
||||
m_exportSongEnd = m_playPos[Mode_PlaySong].m_timeLine->loopEnd();
|
||||
}
|
||||
|
||||
if (!m_exportLoop)
|
||||
m_exportSongEnd += MidiTime(1,0);
|
||||
|
||||
m_exportSongBegin = MidiTime(0,0);
|
||||
m_exportLoopBegin = m_playPos[Mode_PlaySong].m_timeLine->loopBegin() < m_exportSongEnd &&
|
||||
m_playPos[Mode_PlaySong].m_timeLine->loopEnd() <= m_exportSongEnd ?
|
||||
m_playPos[Mode_PlaySong].m_timeLine->loopBegin() : MidiTime(0,0);
|
||||
m_exportLoopEnd = m_playPos[Mode_PlaySong].m_timeLine->loopBegin() < m_exportSongEnd &&
|
||||
m_playPos[Mode_PlaySong].m_timeLine->loopEnd() <= m_exportSongEnd ?
|
||||
m_playPos[Mode_PlaySong].m_timeLine->loopEnd() : MidiTime(0,0);
|
||||
|
||||
m_playPos[Mode_PlaySong].setTicks( 0 );
|
||||
}
|
||||
|
||||
m_exportEffectiveLength = (m_exportLoopBegin - m_exportSongBegin) + (m_exportLoopEnd - m_exportLoopBegin)
|
||||
* m_loopRenderCount + (m_exportSongEnd - m_exportLoopEnd);
|
||||
m_loopRenderRemaining = m_loopRenderCount;
|
||||
|
||||
playSong();
|
||||
|
||||
m_exporting = true;
|
||||
|
||||
366
src/core/StepRecorder.cpp
Normal file
366
src/core/StepRecorder.cpp
Normal file
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* 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 "StepRecorder.h"
|
||||
#include "StepRecorderWidget.h"
|
||||
#include "PianoRoll.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
#include <climits>
|
||||
using std::min;
|
||||
using std::max;
|
||||
|
||||
const int REMOVE_RELEASED_NOTE_TIME_THRESHOLD_MS = 70;
|
||||
|
||||
StepRecorder::StepRecorder(PianoRoll& pianoRoll, StepRecorderWidget& stepRecorderWidget):
|
||||
m_pianoRoll(pianoRoll),
|
||||
m_stepRecorderWidget(stepRecorderWidget)
|
||||
{
|
||||
m_stepRecorderWidget.hide();
|
||||
}
|
||||
|
||||
void StepRecorder::initialize()
|
||||
{
|
||||
connect(&m_updateReleasedTimer, SIGNAL(timeout()), this, SLOT(removeNotesReleasedForTooLong()));
|
||||
}
|
||||
|
||||
void StepRecorder::start(const MidiTime& currentPosition, const MidiTime& stepLength)
|
||||
{
|
||||
m_isRecording = true;
|
||||
|
||||
setStepsLength(stepLength);
|
||||
|
||||
// quantize current position to get start recording position
|
||||
const int q = m_pianoRoll.quantization();
|
||||
const int curPosTicks = currentPosition.getTicks();
|
||||
const int QuantizedPosTicks = (curPosTicks / q) * q;
|
||||
const MidiTime& QuantizedPos = MidiTime(QuantizedPosTicks);
|
||||
|
||||
m_curStepStartPos = QuantizedPos;
|
||||
m_curStepLength = 0;
|
||||
|
||||
m_stepRecorderWidget.show();
|
||||
|
||||
m_stepRecorderWidget.showHint();
|
||||
|
||||
prepareNewStep();
|
||||
}
|
||||
|
||||
void StepRecorder::stop()
|
||||
{
|
||||
m_stepRecorderWidget.hide();
|
||||
m_isRecording = false;
|
||||
}
|
||||
|
||||
void StepRecorder::notePressed(const Note & n)
|
||||
{
|
||||
//if this is the first pressed note in step, advance position
|
||||
if(!m_isStepInProgress)
|
||||
{
|
||||
m_isStepInProgress = true;
|
||||
|
||||
//move curser one step forwards
|
||||
stepForwards();
|
||||
}
|
||||
|
||||
StepNote* stepNote = findCurStepNote(n.key());
|
||||
if(stepNote == nullptr)
|
||||
{
|
||||
m_curStepNotes.append(new StepNote(Note(m_curStepLength, m_curStepStartPos, n.key(), n.getVolume(), n.getPanning())));
|
||||
m_pianoRoll.update();
|
||||
}
|
||||
else if (stepNote->isReleased())
|
||||
{
|
||||
stepNote->setPressed();
|
||||
}
|
||||
}
|
||||
|
||||
void StepRecorder::noteReleased(const Note & n)
|
||||
{
|
||||
StepNote* stepNote = findCurStepNote(n.key());
|
||||
|
||||
if(stepNote != nullptr && stepNote->isPressed())
|
||||
{
|
||||
stepNote->setReleased();
|
||||
|
||||
//if m_updateReleasedTimer is not already active, activate it
|
||||
//(when activated, the timer will re-set itself as long as there are released notes)
|
||||
if(!m_updateReleasedTimer.isActive())
|
||||
{
|
||||
m_updateReleasedTimer.start(REMOVE_RELEASED_NOTE_TIME_THRESHOLD_MS);
|
||||
}
|
||||
|
||||
//check if all note are released, apply notes to pattern(or dimiss if length is zero) and prepare to record next step
|
||||
if(allCurStepNotesReleased())
|
||||
{
|
||||
if(m_curStepLength > 0)
|
||||
{
|
||||
applyStep();
|
||||
}
|
||||
else
|
||||
{
|
||||
dismissStep();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool StepRecorder::keyPressEvent(QKeyEvent* ke)
|
||||
{
|
||||
bool event_handled = false;
|
||||
|
||||
switch(ke->key())
|
||||
{
|
||||
case Qt::Key_Right:
|
||||
{
|
||||
if(!ke->isAutoRepeat())
|
||||
{
|
||||
stepForwards();
|
||||
}
|
||||
event_handled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case Qt::Key_Left:
|
||||
{
|
||||
if(!ke->isAutoRepeat())
|
||||
{
|
||||
stepBackwards();
|
||||
}
|
||||
event_handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return event_handled;
|
||||
}
|
||||
|
||||
void StepRecorder::setStepsLength(const MidiTime& newLength)
|
||||
{
|
||||
if(m_isStepInProgress)
|
||||
{
|
||||
//update current step length by the new amount : (number_of_steps * newLength)
|
||||
m_curStepLength = (m_curStepLength / m_stepsLength) * newLength;
|
||||
|
||||
updateCurStepNotes();
|
||||
}
|
||||
|
||||
m_stepsLength = newLength;
|
||||
|
||||
updateWidget();
|
||||
}
|
||||
|
||||
QVector<Note*> StepRecorder::getCurStepNotes()
|
||||
{
|
||||
QVector<Note*> notes;
|
||||
|
||||
if(m_isStepInProgress)
|
||||
{
|
||||
for(StepNote* stepNote: m_curStepNotes)
|
||||
{
|
||||
notes.append(&stepNote->m_note);
|
||||
}
|
||||
}
|
||||
|
||||
return notes;
|
||||
}
|
||||
|
||||
void StepRecorder::stepForwards()
|
||||
{
|
||||
if(m_isStepInProgress)
|
||||
{
|
||||
m_curStepLength += m_stepsLength;
|
||||
|
||||
updateCurStepNotes();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_curStepStartPos += m_stepsLength;
|
||||
}
|
||||
|
||||
updateWidget();
|
||||
}
|
||||
|
||||
void StepRecorder::stepBackwards()
|
||||
{
|
||||
if(m_isStepInProgress)
|
||||
{
|
||||
if(m_curStepLength > 0)
|
||||
{
|
||||
m_curStepLength = max(m_curStepLength - m_stepsLength, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
//if length is already zero - move starting position backwards
|
||||
m_curStepStartPos = max(m_curStepStartPos - m_stepsLength, 0);
|
||||
}
|
||||
|
||||
updateCurStepNotes();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_curStepStartPos = max(m_curStepStartPos - m_stepsLength, 0);
|
||||
}
|
||||
|
||||
updateWidget();
|
||||
}
|
||||
|
||||
void StepRecorder::applyStep()
|
||||
{
|
||||
m_pattern->addJournalCheckPoint();
|
||||
|
||||
for (const StepNote* stepNote : m_curStepNotes)
|
||||
{
|
||||
m_pattern->addNote(stepNote->m_note, false);
|
||||
}
|
||||
|
||||
m_pattern->rearrangeAllNotes();
|
||||
m_pattern->updateLength();
|
||||
m_pattern->dataChanged();
|
||||
Engine::getSong()->setModified();
|
||||
|
||||
prepareNewStep();
|
||||
}
|
||||
|
||||
void StepRecorder::dismissStep()
|
||||
{
|
||||
if(!m_isStepInProgress)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
prepareNewStep();
|
||||
}
|
||||
|
||||
void StepRecorder::prepareNewStep()
|
||||
{
|
||||
for(StepNote* stepNote : m_curStepNotes)
|
||||
{
|
||||
delete stepNote;
|
||||
}
|
||||
m_curStepNotes.clear();
|
||||
|
||||
m_isStepInProgress = false;
|
||||
|
||||
m_curStepStartPos = getCurStepEndPos();
|
||||
m_curStepLength = 0;
|
||||
|
||||
updateWidget();
|
||||
}
|
||||
|
||||
void StepRecorder::setCurrentPattern( Pattern* newPattern )
|
||||
{
|
||||
if(m_pattern != NULL && m_pattern != newPattern)
|
||||
{
|
||||
dismissStep();
|
||||
}
|
||||
|
||||
m_pattern = newPattern;
|
||||
}
|
||||
|
||||
void StepRecorder::removeNotesReleasedForTooLong()
|
||||
{
|
||||
int nextTimout = std::numeric_limits<int>::max();
|
||||
bool notesRemoved = false;
|
||||
|
||||
QMutableVectorIterator<StepNote*> itr(m_curStepNotes);
|
||||
while (itr.hasNext())
|
||||
{
|
||||
StepNote* stepNote = itr.next();
|
||||
|
||||
if(stepNote->isReleased())
|
||||
{
|
||||
const int timeSinceReleased = stepNote->timeSinceReleased(); // capture value to avoid wraparound when calculting nextTimout
|
||||
if (timeSinceReleased >= REMOVE_RELEASED_NOTE_TIME_THRESHOLD_MS)
|
||||
{
|
||||
delete stepNote;
|
||||
itr.remove();
|
||||
notesRemoved = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
nextTimout = min(nextTimout, REMOVE_RELEASED_NOTE_TIME_THRESHOLD_MS - timeSinceReleased);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(notesRemoved)
|
||||
{
|
||||
m_pianoRoll.update();
|
||||
}
|
||||
|
||||
if(nextTimout != std::numeric_limits<int>::max())
|
||||
{
|
||||
m_updateReleasedTimer.start(nextTimout);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no released note found for next timout, stop timer
|
||||
m_updateReleasedTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
MidiTime StepRecorder::getCurStepEndPos()
|
||||
{
|
||||
return m_curStepStartPos + m_curStepLength;
|
||||
}
|
||||
|
||||
void StepRecorder::updateCurStepNotes()
|
||||
{
|
||||
for (StepNote* stepNote : m_curStepNotes)
|
||||
{
|
||||
stepNote->m_note.setLength(m_curStepLength);
|
||||
stepNote->m_note.setPos(m_curStepStartPos);
|
||||
}
|
||||
}
|
||||
|
||||
void StepRecorder::updateWidget()
|
||||
{
|
||||
m_stepRecorderWidget.setStartPosition(m_curStepStartPos);
|
||||
m_stepRecorderWidget.setEndPosition(getCurStepEndPos());
|
||||
m_stepRecorderWidget.setStepsLength(m_stepsLength);
|
||||
}
|
||||
|
||||
bool StepRecorder::allCurStepNotesReleased()
|
||||
{
|
||||
for (const StepNote* stepNote : m_curStepNotes)
|
||||
{
|
||||
if(stepNote->isPressed())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
StepRecorder::StepNote* StepRecorder::findCurStepNote(const int key)
|
||||
{
|
||||
for (StepNote* stepNote : m_curStepNotes)
|
||||
{
|
||||
if(stepNote->m_note.key() == key)
|
||||
{
|
||||
return stepNote;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@@ -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 )
|
||||
@@ -1921,13 +1924,15 @@ void TrackOperationsWidget::updateMenu()
|
||||
{
|
||||
toMenu->addAction( tr( "Clear this track" ), this, SLOT( clearTrack() ) );
|
||||
}
|
||||
if( InstrumentTrackView * trackView = dynamic_cast<InstrumentTrackView *>( m_trackView ) )
|
||||
if (QMenu *fxMenu = m_trackView->createFxMenu(tr("FX %1: %2"), tr("Assign to new FX Channel")))
|
||||
{
|
||||
QMenu *fxMenu = trackView->createFxMenu( tr( "FX %1: %2" ), tr( "Assign to new FX Channel" ));
|
||||
toMenu->addMenu(fxMenu);
|
||||
}
|
||||
|
||||
if (InstrumentTrackView * trackView = dynamic_cast<InstrumentTrackView *>(m_trackView))
|
||||
{
|
||||
toMenu->addSeparator();
|
||||
toMenu->addMenu( trackView->midiMenu() );
|
||||
toMenu->addMenu(trackView->midiMenu());
|
||||
}
|
||||
if( dynamic_cast<AutomationTrackView *>( m_trackView ) )
|
||||
{
|
||||
@@ -2674,6 +2679,19 @@ void TrackView::update()
|
||||
|
||||
|
||||
|
||||
/*! \brief Create a menu for assigning/creating channels for this track.
|
||||
*
|
||||
*/
|
||||
QMenu * TrackView::createFxMenu(QString title, QString newFxLabel)
|
||||
{
|
||||
Q_UNUSED(title)
|
||||
Q_UNUSED(newFxLabel)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*! \brief Close this track View.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -105,6 +106,21 @@ static inline QString baseName( const QString & file )
|
||||
}
|
||||
|
||||
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
// Workaround for old MinGW
|
||||
#ifdef __MINGW32__
|
||||
extern "C" _CRTIMP errno_t __cdecl freopen_s(FILE** _File,
|
||||
const char *_Filename, const char *_Mode, FILE *_Stream);
|
||||
#endif
|
||||
|
||||
// For qInstallMessageHandler
|
||||
void consoleMessageHandler(QtMsgType type,
|
||||
const QMessageLogContext &context, const QString &msg)
|
||||
{
|
||||
QByteArray localMsg = msg.toLocal8Bit();
|
||||
fprintf(stderr, "%s\n", localMsg.constData());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
inline void loadTranslation( const QString & tname,
|
||||
@@ -243,6 +259,33 @@ int main( int argc, char * * argv )
|
||||
signal(SIGFPE, signalHandler);
|
||||
#endif
|
||||
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
// Don't touch redirected streams here
|
||||
// GetStdHandle should be called before AttachConsole
|
||||
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
HANDLE hStdErr = GetStdHandle(STD_ERROR_HANDLE);
|
||||
FILE *fIn, *fOut, *fErr;
|
||||
// Enable console output if available
|
||||
if (AttachConsole(ATTACH_PARENT_PROCESS))
|
||||
{
|
||||
if (!hStdIn)
|
||||
{
|
||||
freopen_s(&fIn, "CONIN$", "r", stdin);
|
||||
}
|
||||
if (!hStdOut)
|
||||
{
|
||||
freopen_s(&fOut, "CONOUT$", "w", stdout);
|
||||
}
|
||||
if (!hStdErr)
|
||||
{
|
||||
freopen_s(&fErr, "CONOUT$", "w", stderr);
|
||||
}
|
||||
}
|
||||
// Make Qt's debug message handlers work
|
||||
qInstallMessageHandler(consoleMessageHandler);
|
||||
#endif
|
||||
|
||||
// initialize memory managers
|
||||
NotePlayHandleManager::init();
|
||||
|
||||
@@ -301,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 );
|
||||
@@ -646,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() )
|
||||
@@ -930,5 +983,15 @@ int main( int argc, char * * argv )
|
||||
printf( "\n" );
|
||||
}
|
||||
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
// Cleanup console
|
||||
HWND hConsole = GetConsoleWindow();
|
||||
if (hConsole)
|
||||
{
|
||||
SendMessage(hConsole, WM_CHAR, (WPARAM)VK_RETURN, (LPARAM)0);
|
||||
FreeConsole();
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ SET(LMMS_SRCS
|
||||
gui/widgets/FadeButton.cpp
|
||||
gui/widgets/Fader.cpp
|
||||
gui/widgets/FxLine.cpp
|
||||
gui/widgets/FxLineLcdSpinBox.cpp
|
||||
gui/widgets/Graph.cpp
|
||||
gui/widgets/GroupBox.cpp
|
||||
gui/widgets/InstrumentFunctionViews.cpp
|
||||
@@ -87,6 +88,7 @@ SET(LMMS_SRCS
|
||||
gui/widgets/TrackLabelButton.cpp
|
||||
gui/widgets/TrackRenameLineEdit.cpp
|
||||
gui/widgets/VisualizationWidget.cpp
|
||||
gui/widgets/StepRecorderWidget.cpp
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "ui_EffectSelectDialog.h"
|
||||
|
||||
#include "gui_templates.h"
|
||||
#include "DummyEffect.h"
|
||||
#include "embed.h"
|
||||
#include "PluginFactory.h"
|
||||
|
||||
@@ -142,12 +143,17 @@ EffectSelectDialog::~EffectSelectDialog()
|
||||
|
||||
Effect * EffectSelectDialog::instantiateSelectedPlugin( EffectChain * _parent )
|
||||
{
|
||||
if( !m_currentSelection.name.isEmpty() && m_currentSelection.desc )
|
||||
Effect* result = nullptr;
|
||||
if(!m_currentSelection.name.isEmpty() && m_currentSelection.desc)
|
||||
{
|
||||
return Effect::instantiate( m_currentSelection.desc->name,
|
||||
_parent, &m_currentSelection );
|
||||
result = Effect::instantiate(m_currentSelection.desc->name,
|
||||
_parent, &m_currentSelection);
|
||||
}
|
||||
return NULL;
|
||||
if(!result)
|
||||
{
|
||||
result = new DummyEffect(_parent, QDomElement());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -128,6 +128,7 @@ void ExportProjectDialog::accept()
|
||||
|
||||
void ExportProjectDialog::closeEvent( QCloseEvent * _ce )
|
||||
{
|
||||
Engine::getSong()->setLoopRenderCount(1);
|
||||
if( m_renderManager ) {
|
||||
m_renderManager->abortProcessing();
|
||||
}
|
||||
@@ -187,6 +188,7 @@ void ExportProjectDialog::startExport()
|
||||
|
||||
Engine::getSong()->setExportLoop( exportLoopCB->isChecked() );
|
||||
Engine::getSong()->setRenderBetweenMarkers( renderMarkersCB->isChecked() );
|
||||
Engine::getSong()->setLoopRenderCount(loopCountSB->value());
|
||||
|
||||
connect( m_renderManager.get(), SIGNAL( progressChanged( int ) ),
|
||||
progressBar, SLOT( setValue( int ) ) );
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "Mixer.h"
|
||||
#include "gui_templates.h"
|
||||
#include "InstrumentTrack.h"
|
||||
#include "SampleTrack.h"
|
||||
#include "Song.h"
|
||||
#include "BBTrackContainer.h"
|
||||
|
||||
@@ -73,7 +74,7 @@ FxMixerView::FxMixerView() :
|
||||
|
||||
// Set margins
|
||||
ml->setContentsMargins( 0, 4, 0, 0 );
|
||||
|
||||
|
||||
// Channel area
|
||||
m_channelAreaWidget = new QWidget;
|
||||
chLayout = new QHBoxLayout( m_channelAreaWidget );
|
||||
@@ -138,9 +139,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 +220,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 )
|
||||
{
|
||||
@@ -251,6 +252,12 @@ void FxMixerView::updateMaxChannelSelector()
|
||||
inst->effectChannelModel()->setRange(0,
|
||||
m_fxChannelViews.size()-1,1);
|
||||
}
|
||||
else if( trackList[i]->type() == Track::SampleTrack )
|
||||
{
|
||||
SampleTrack * strk = (SampleTrack *) trackList[i];
|
||||
strk->effectChannelModel()->setRange(0,
|
||||
m_fxChannelViews.size()-1,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,7 +315,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 +361,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 )
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -66,6 +66,8 @@
|
||||
#include "MidiApple.h"
|
||||
#include "MidiDummy.h"
|
||||
|
||||
constexpr int BUFFERSIZE_RESOLUTION = 32;
|
||||
|
||||
inline void labelWidget( QWidget * _w, const QString & _txt )
|
||||
{
|
||||
QLabel * title = new QLabel( _txt, _w );
|
||||
@@ -98,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",
|
||||
@@ -126,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",
|
||||
@@ -134,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" ) );
|
||||
@@ -176,12 +180,12 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) :
|
||||
bufsize_tw->setFixedHeight( 80 );
|
||||
|
||||
m_bufSizeSlider = new QSlider( Qt::Horizontal, bufsize_tw );
|
||||
m_bufSizeSlider->setRange( 1, 256 );
|
||||
m_bufSizeSlider->setRange( 1, 128 );
|
||||
m_bufSizeSlider->setTickPosition( QSlider::TicksBelow );
|
||||
m_bufSizeSlider->setPageStep( 8 );
|
||||
m_bufSizeSlider->setTickInterval( 8 );
|
||||
m_bufSizeSlider->setGeometry( 10, 16, 340, 18 );
|
||||
m_bufSizeSlider->setValue( m_bufferSize / 64 );
|
||||
m_bufSizeSlider->setValue( m_bufferSize / BUFFERSIZE_RESOLUTION );
|
||||
|
||||
connect( m_bufSizeSlider, SIGNAL( valueChanged( int ) ), this,
|
||||
SLOT( setBufferSize( int ) ) );
|
||||
@@ -245,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 );
|
||||
@@ -813,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",
|
||||
@@ -877,7 +892,7 @@ void SetupDialog::accept()
|
||||
|
||||
void SetupDialog::setBufferSize( int _value )
|
||||
{
|
||||
const int step = DEFAULT_BUFFER_SIZE / 64;
|
||||
const int step = DEFAULT_BUFFER_SIZE / BUFFERSIZE_RESOLUTION;
|
||||
if( _value > step && _value % step )
|
||||
{
|
||||
int mod_value = _value % step;
|
||||
@@ -897,7 +912,7 @@ void SetupDialog::setBufferSize( int _value )
|
||||
m_bufSizeSlider->setValue( _value );
|
||||
}
|
||||
|
||||
m_bufferSize = _value * 64;
|
||||
m_bufferSize = _value * BUFFERSIZE_RESOLUTION;
|
||||
m_bufSizeLbl->setText( tr( "Frames: %1\nLatency: %2 ms" ).arg(
|
||||
m_bufferSize ).arg(
|
||||
1000.0f * m_bufferSize /
|
||||
@@ -910,7 +925,7 @@ void SetupDialog::setBufferSize( int _value )
|
||||
|
||||
void SetupDialog::resetBufSize()
|
||||
{
|
||||
setBufferSize( DEFAULT_BUFFER_SIZE / 64 );
|
||||
setBufferSize( DEFAULT_BUFFER_SIZE / BUFFERSIZE_RESOLUTION );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() ) );
|
||||
}
|
||||
|
||||
@@ -7,19 +7,19 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>379</width>
|
||||
<height>374</height>
|
||||
<height>400</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>379</width>
|
||||
<height>374</height>
|
||||
<height>400</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>379</width>
|
||||
<height>374</height>
|
||||
<height>400</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -40,6 +40,47 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="loopRepeatWidget" native="true">
|
||||
<layout class="QHBoxLayout" name="loopRepeatHL">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelLoopRepeat">
|
||||
<property name="text">
|
||||
<string>Render Looped Section:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="loopCountSB">
|
||||
<property name="suffix">
|
||||
<string> time(s)</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>99</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout">
|
||||
<item>
|
||||
|
||||
@@ -73,11 +73,12 @@ void Editor::togglePlayStop()
|
||||
play();
|
||||
}
|
||||
|
||||
Editor::Editor(bool record) :
|
||||
Editor::Editor(bool record, bool stepRecord) :
|
||||
m_toolBar(new DropToolBar(this)),
|
||||
m_playAction(nullptr),
|
||||
m_recordAction(nullptr),
|
||||
m_recordAccompanyAction(nullptr),
|
||||
m_toggleStepRecordingAction(nullptr),
|
||||
m_stopAction(nullptr)
|
||||
{
|
||||
m_toolBar = addDropToolBarToTop(tr("Transport controls"));
|
||||
@@ -93,11 +94,13 @@ Editor::Editor(bool record) :
|
||||
|
||||
m_recordAction = new QAction(embed::getIconPixmap("record"), tr("Record"), this);
|
||||
m_recordAccompanyAction = new QAction(embed::getIconPixmap("record_accompany"), tr("Record while playing"), this);
|
||||
m_toggleStepRecordingAction = new QAction(embed::getIconPixmap("record_step_off"), tr("Toggle Step Recording"), this);
|
||||
|
||||
// Set up connections
|
||||
connect(m_playAction, SIGNAL(triggered()), this, SLOT(play()));
|
||||
connect(m_recordAction, SIGNAL(triggered()), this, SLOT(record()));
|
||||
connect(m_recordAccompanyAction, SIGNAL(triggered()), this, SLOT(recordAccompany()));
|
||||
connect(m_toggleStepRecordingAction, SIGNAL(triggered()), this, SLOT(toggleStepRecording()));
|
||||
connect(m_stopAction, SIGNAL(triggered()), this, SLOT(stop()));
|
||||
new QShortcut(Qt::Key_Space, this, SLOT(togglePlayStop()));
|
||||
|
||||
@@ -108,6 +111,10 @@ Editor::Editor(bool record) :
|
||||
addButton(m_recordAction, "recordButton");
|
||||
addButton(m_recordAccompanyAction, "recordAccompanyButton");
|
||||
}
|
||||
if(stepRecord)
|
||||
{
|
||||
addButton(m_toggleStepRecordingAction, "stepRecordButton");
|
||||
}
|
||||
addButton(m_stopAction, "stopButton");
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
#include "stdshims.h"
|
||||
#include "TextFloat.h"
|
||||
#include "TimeLineWidget.h"
|
||||
#include "StepRecorderWidget.h"
|
||||
|
||||
|
||||
using std::move;
|
||||
@@ -177,11 +178,15 @@ PianoRoll::PianoRoll() :
|
||||
m_ctrlMode( ModeDraw ),
|
||||
m_mouseDownRight( false ),
|
||||
m_scrollBack( false ),
|
||||
m_stepRecorderWidget(this, DEFAULT_PR_PPT, PR_TOP_MARGIN, PR_BOTTOM_MARGIN + m_notesEditHeight, WHITE_KEY_WIDTH, 0),
|
||||
m_stepRecorder(*this, m_stepRecorderWidget),
|
||||
m_barLineColor( 0, 0, 0 ),
|
||||
m_beatLineColor( 0, 0, 0 ),
|
||||
m_lineColor( 0, 0, 0 ),
|
||||
m_noteModeColor( 0, 0, 0 ),
|
||||
m_noteColor( 0, 0, 0 ),
|
||||
m_ghostNoteColor( 0, 0, 0 ),
|
||||
m_ghostNoteTextColor( 0, 0, 0 ),
|
||||
m_barColor( 0, 0, 0 ),
|
||||
m_selectedNoteColor( 0, 0, 0 ),
|
||||
m_textColor( 0, 0, 0 ),
|
||||
@@ -189,7 +194,9 @@ PianoRoll::PianoRoll() :
|
||||
m_textShadow( 0, 0, 0 ),
|
||||
m_markedSemitoneColor( 0, 0, 0 ),
|
||||
m_noteOpacity( 255 ),
|
||||
m_ghostNoteOpacity( 255 ),
|
||||
m_noteBorders( true ),
|
||||
m_ghostNoteBorders( true ),
|
||||
m_backgroundShade( 0, 0, 0 )
|
||||
{
|
||||
// gui names of edit modes
|
||||
@@ -319,6 +326,10 @@ PianoRoll::PianoRoll() :
|
||||
connect( m_timeLine, SIGNAL( positionChanged( const MidiTime & ) ),
|
||||
this, SLOT( updatePosition( const MidiTime & ) ) );
|
||||
|
||||
//update timeline when in step-recording mode
|
||||
connect( &m_stepRecorderWidget, SIGNAL( positionChanged( const MidiTime & ) ),
|
||||
this, SLOT( updatePositionStepRecording( const MidiTime & ) ) );
|
||||
|
||||
// update timeline when in record-accompany mode
|
||||
connect( Engine::getSong()->getPlayPos( Song::Mode_PlaySong ).m_timeLine,
|
||||
SIGNAL( positionChanged( const MidiTime & ) ),
|
||||
@@ -391,7 +402,7 @@ PianoRoll::PianoRoll() :
|
||||
|
||||
// Note length change can cause a redraw if Q is set to lock
|
||||
connect( &m_noteLenModel, SIGNAL( dataChanged() ),
|
||||
this, SLOT( quantizeChanged() ) );
|
||||
this, SLOT( noteLengthChanged() ) );
|
||||
|
||||
// Set up scale model
|
||||
const InstrumentFunctionNoteStacking::ChordTable& chord_table =
|
||||
@@ -440,6 +451,8 @@ PianoRoll::PianoRoll() :
|
||||
//connection for selecion from timeline
|
||||
connect( m_timeLine, SIGNAL( regionSelectedFromPixels( int, int ) ),
|
||||
this, SLOT( selectRegionFromPixels( int, int ) ) );
|
||||
|
||||
m_stepRecorder.initialize();
|
||||
}
|
||||
|
||||
|
||||
@@ -448,6 +461,7 @@ void PianoRoll::reset()
|
||||
{
|
||||
m_lastNoteVolume = DefaultVolume;
|
||||
m_lastNotePanning = DefaultPanning;
|
||||
clearGhostPattern();
|
||||
}
|
||||
|
||||
void PianoRoll::showTextFloat(const QString &text, const QPoint &pos, int timeout)
|
||||
@@ -599,6 +613,48 @@ PianoRoll::~PianoRoll()
|
||||
}
|
||||
|
||||
|
||||
void PianoRoll::setGhostPattern( Pattern* newPattern )
|
||||
{
|
||||
// Expects a pointer to a pattern or nullptr.
|
||||
m_ghostNotes.clear();
|
||||
if( newPattern != nullptr )
|
||||
{
|
||||
for( Note *note : newPattern->notes() )
|
||||
{
|
||||
Note * new_note = new Note( note->length(), note->pos(), note->key() );
|
||||
m_ghostNotes.push_back( new_note );
|
||||
}
|
||||
emit ghostPatternSet( true );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PianoRoll::loadGhostNotes( const QDomElement & de )
|
||||
{
|
||||
// Load ghost notes from DOM element.
|
||||
if( de.isElement() )
|
||||
{
|
||||
QDomNode node = de.firstChild();
|
||||
while( !node.isNull() )
|
||||
{
|
||||
Note * n = new Note;
|
||||
n->restoreState( node.toElement() );
|
||||
m_ghostNotes.push_back( n );
|
||||
node = node.nextSibling();
|
||||
}
|
||||
emit ghostPatternSet( true );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PianoRoll::clearGhostPattern()
|
||||
{
|
||||
setGhostPattern( nullptr );
|
||||
emit ghostPatternSet( false );
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
void PianoRoll::setCurrentPattern( Pattern* newPattern )
|
||||
{
|
||||
if( hasValidPattern() )
|
||||
@@ -613,12 +669,19 @@ void PianoRoll::setCurrentPattern( Pattern* newPattern )
|
||||
Engine::getSong()->playPattern( NULL );
|
||||
}
|
||||
|
||||
if(m_stepRecorder.isRecording())
|
||||
{
|
||||
m_stepRecorder.stop();
|
||||
}
|
||||
|
||||
// set new data
|
||||
m_pattern = newPattern;
|
||||
m_currentPosition = 0;
|
||||
m_currentNote = NULL;
|
||||
m_startKey = INITIAL_START_KEY;
|
||||
|
||||
m_stepRecorder.setCurrentPattern(newPattern);
|
||||
|
||||
if( ! hasValidPattern() )
|
||||
{
|
||||
//resizeEvent( NULL );
|
||||
@@ -801,6 +864,30 @@ bool PianoRoll::noteBorders() const
|
||||
void PianoRoll::setNoteBorders( const bool b )
|
||||
{ m_noteBorders = b; }
|
||||
|
||||
QColor PianoRoll::ghostNoteColor() const
|
||||
{ return m_ghostNoteColor; }
|
||||
|
||||
void PianoRoll::setGhostNoteColor( const QColor & c )
|
||||
{ m_ghostNoteColor = c; }
|
||||
|
||||
QColor PianoRoll::ghostNoteTextColor() const
|
||||
{ return m_ghostNoteTextColor; }
|
||||
|
||||
void PianoRoll::setGhostNoteTextColor( const QColor & c )
|
||||
{ m_ghostNoteTextColor = c; }
|
||||
|
||||
int PianoRoll::ghostNoteOpacity() const
|
||||
{ return m_ghostNoteOpacity; }
|
||||
|
||||
void PianoRoll::setGhostNoteOpacity( const int i )
|
||||
{ m_ghostNoteOpacity = i; }
|
||||
|
||||
bool PianoRoll::ghostNoteBorders() const
|
||||
{ return m_ghostNoteBorders; }
|
||||
|
||||
void PianoRoll::setGhostNoteBorders( const bool b )
|
||||
{ m_ghostNoteBorders = b; }
|
||||
|
||||
QColor PianoRoll::backgroundShade() const
|
||||
{ return m_backgroundShade; }
|
||||
|
||||
@@ -810,7 +897,6 @@ void PianoRoll::setBackgroundShade( const QColor & c )
|
||||
|
||||
|
||||
|
||||
|
||||
void PianoRoll::drawNoteRect( QPainter & p, int x, int y,
|
||||
int width, const Note * n, const QColor & noteCol, const QColor & noteTextColor,
|
||||
const QColor & selCol, const int noteOpc, const bool borders, bool drawNoteName )
|
||||
@@ -990,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() )
|
||||
{
|
||||
@@ -1016,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;
|
||||
@@ -1083,8 +1171,19 @@ int PianoRoll::selectionCount() const // how many notes are selected?
|
||||
|
||||
|
||||
|
||||
void PianoRoll::keyPressEvent(QKeyEvent* ke )
|
||||
void PianoRoll::keyPressEvent(QKeyEvent* ke)
|
||||
{
|
||||
if(m_stepRecorder.isRecording())
|
||||
{
|
||||
bool handled = m_stepRecorder.keyPressEvent(ke);
|
||||
if(handled)
|
||||
{
|
||||
ke->accept();
|
||||
update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( hasValidPattern() && ke->modifiers() == Qt::NoModifier )
|
||||
{
|
||||
const int key_num = PianoView::getKeyFromKeyEvent( ke ) + ( DefaultOctave - 1 ) * KeysPerOctave;
|
||||
@@ -1833,7 +1932,7 @@ void PianoRoll::testPlayNote( Note * n )
|
||||
{
|
||||
m_lastKey = n->key();
|
||||
|
||||
if( ! n->isPlaying() && ! m_recording )
|
||||
if( ! n->isPlaying() && ! m_recording && ! m_stepRecorder.isRecording())
|
||||
{
|
||||
n->setIsPlaying( true );
|
||||
|
||||
@@ -2066,6 +2165,8 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me )
|
||||
NOTE_EDIT_MIN_HEIGHT,
|
||||
height() - PR_TOP_MARGIN - NOTE_EDIT_RESIZE_BAR -
|
||||
PR_BOTTOM_MARGIN - KEY_AREA_MIN_HEIGHT );
|
||||
|
||||
m_stepRecorderWidget.setBottomMargin(PR_BOTTOM_MARGIN + m_notesEditHeight);
|
||||
repaint();
|
||||
return;
|
||||
}
|
||||
@@ -3024,6 +3125,50 @@ void PianoRoll::paintEvent(QPaintEvent * pe )
|
||||
|
||||
QPolygonF editHandles;
|
||||
|
||||
// -- Begin ghost pattern
|
||||
if( !m_ghostNotes.empty() )
|
||||
{
|
||||
for( const Note *note : m_ghostNotes )
|
||||
{
|
||||
int len_ticks = note->length();
|
||||
|
||||
if( len_ticks == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if( len_ticks < 0 )
|
||||
{
|
||||
len_ticks = 4;
|
||||
}
|
||||
const int key = note->key() - m_startKey + 1;
|
||||
|
||||
int pos_ticks = note->pos();
|
||||
|
||||
int note_width = len_ticks * m_ppt / MidiTime::ticksPerTact();
|
||||
const int x = ( pos_ticks - m_currentPosition ) *
|
||||
m_ppt / MidiTime::ticksPerTact();
|
||||
// skip this note if not in visible area at all
|
||||
if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// is the note in visible area?
|
||||
if( key > 0 && key <= visible_keys )
|
||||
{
|
||||
|
||||
// we've done and checked all, let's draw the
|
||||
// note
|
||||
drawNoteRect( p, x + WHITE_KEY_WIDTH,
|
||||
y_base - key * KEY_LINE_HEIGHT,
|
||||
note_width, note, ghostNoteColor(), ghostNoteTextColor(), selectedNoteColor(),
|
||||
ghostNoteOpacity(), ghostNoteBorders(), drawNoteNames );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// -- End ghost pattern
|
||||
|
||||
for( const Note *note : m_pattern->notes() )
|
||||
{
|
||||
int len_ticks = note->length();
|
||||
@@ -3112,6 +3257,41 @@ void PianoRoll::paintEvent(QPaintEvent * pe )
|
||||
}
|
||||
}
|
||||
|
||||
//draw current step recording notes
|
||||
for( const Note *note : m_stepRecorder.getCurStepNotes() )
|
||||
{
|
||||
int len_ticks = note->length();
|
||||
|
||||
if( len_ticks == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const int key = note->key() - m_startKey + 1;
|
||||
|
||||
int pos_ticks = note->pos();
|
||||
|
||||
int note_width = len_ticks * m_ppt / MidiTime::ticksPerTact();
|
||||
const int x = ( pos_ticks - m_currentPosition ) *
|
||||
m_ppt / MidiTime::ticksPerTact();
|
||||
// skip this note if not in visible area at all
|
||||
if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// is the note in visible area?
|
||||
if( key > 0 && key <= visible_keys )
|
||||
{
|
||||
|
||||
// we've done and checked all, let's draw the note
|
||||
drawNoteRect( p, x + WHITE_KEY_WIDTH,
|
||||
y_base - key * KEY_LINE_HEIGHT,
|
||||
note_width, note, m_stepRecorder.curStepNoteColor(), noteTextColor(), selectedNoteColor(),
|
||||
noteOpacity(), noteBorders(), drawNoteNames );
|
||||
}
|
||||
}
|
||||
|
||||
p.setPen( QPen( noteColor(), NOTE_EDIT_LINE_WIDTH + 2 ) );
|
||||
p.drawPoints( editHandles );
|
||||
|
||||
@@ -3269,7 +3449,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we )
|
||||
}
|
||||
if( nv.size() > 0 )
|
||||
{
|
||||
const int step = we->delta() > 0 ? 1.0 : -1.0;
|
||||
const int step = we->delta() > 0 ? 1 : -1;
|
||||
if( m_noteEditMode == NoteEditVolume )
|
||||
{
|
||||
for ( Note * n : nv )
|
||||
@@ -3511,6 +3691,34 @@ void PianoRoll::recordAccompany()
|
||||
|
||||
|
||||
|
||||
bool PianoRoll::toggleStepRecording()
|
||||
{
|
||||
if(m_stepRecorder.isRecording())
|
||||
{
|
||||
m_stepRecorder.stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(hasValidPattern())
|
||||
{
|
||||
if(Engine::getSong()->isPlaying())
|
||||
{
|
||||
m_stepRecorder.start(0, newNoteLen());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stepRecorder.start(
|
||||
Engine::getSong()->getPlayPos(
|
||||
Song::Mode_PlayPattern), newNoteLen());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_stepRecorder.isRecording();;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PianoRoll::stop()
|
||||
{
|
||||
@@ -3524,22 +3732,29 @@ void PianoRoll::stop()
|
||||
|
||||
void PianoRoll::startRecordNote(const Note & n )
|
||||
{
|
||||
if( m_recording && hasValidPattern() &&
|
||||
if(hasValidPattern())
|
||||
{
|
||||
if( m_recording &&
|
||||
Engine::getSong()->isPlaying() &&
|
||||
(Engine::getSong()->playMode() == desiredPlayModeForAccompany() ||
|
||||
Engine::getSong()->playMode() == Song::Mode_PlayPattern ))
|
||||
{
|
||||
MidiTime sub;
|
||||
if( Engine::getSong()->playMode() == Song::Mode_PlaySong )
|
||||
Engine::getSong()->playMode() == Song::Mode_PlayPattern ))
|
||||
{
|
||||
sub = m_pattern->startPosition();
|
||||
MidiTime sub;
|
||||
if( Engine::getSong()->playMode() == Song::Mode_PlaySong )
|
||||
{
|
||||
sub = m_pattern->startPosition();
|
||||
}
|
||||
Note n1( 1, Engine::getSong()->getPlayPos(
|
||||
Engine::getSong()->playMode() ) - sub,
|
||||
n.key(), n.getVolume(), n.getPanning() );
|
||||
if( n1.pos() >= 0 )
|
||||
{
|
||||
m_recordingNotes << n1;
|
||||
}
|
||||
}
|
||||
Note n1( 1, Engine::getSong()->getPlayPos(
|
||||
Engine::getSong()->playMode() ) - sub,
|
||||
n.key(), n.getVolume(), n.getPanning() );
|
||||
if( n1.pos() >= 0 )
|
||||
else if (m_stepRecorder.isRecording())
|
||||
{
|
||||
m_recordingNotes << n1;
|
||||
m_stepRecorder.notePressed(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3549,28 +3764,35 @@ void PianoRoll::startRecordNote(const Note & n )
|
||||
|
||||
void PianoRoll::finishRecordNote(const Note & n )
|
||||
{
|
||||
if( m_recording && hasValidPattern() &&
|
||||
Engine::getSong()->isPlaying() &&
|
||||
( Engine::getSong()->playMode() ==
|
||||
desiredPlayModeForAccompany() ||
|
||||
Engine::getSong()->playMode() ==
|
||||
Song::Mode_PlayPattern ) )
|
||||
if(hasValidPattern())
|
||||
{
|
||||
for( QList<Note>::Iterator it = m_recordingNotes.begin();
|
||||
it != m_recordingNotes.end(); ++it )
|
||||
if( m_recording &&
|
||||
Engine::getSong()->isPlaying() &&
|
||||
( Engine::getSong()->playMode() ==
|
||||
desiredPlayModeForAccompany() ||
|
||||
Engine::getSong()->playMode() ==
|
||||
Song::Mode_PlayPattern ) )
|
||||
{
|
||||
if( it->key() == n.key() )
|
||||
for( QList<Note>::Iterator it = m_recordingNotes.begin();
|
||||
it != m_recordingNotes.end(); ++it )
|
||||
{
|
||||
Note n1( n.length(), it->pos(),
|
||||
it->key(), it->getVolume(),
|
||||
it->getPanning() );
|
||||
n1.quantizeLength( quantization() );
|
||||
m_pattern->addNote( n1 );
|
||||
update();
|
||||
m_recordingNotes.erase( it );
|
||||
break;
|
||||
if( it->key() == n.key() )
|
||||
{
|
||||
Note n1( n.length(), it->pos(),
|
||||
it->key(), it->getVolume(),
|
||||
it->getPanning() );
|
||||
n1.quantizeLength( quantization() );
|
||||
m_pattern->addNote( n1 );
|
||||
update();
|
||||
m_recordingNotes.erase( it );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_stepRecorder.isRecording())
|
||||
{
|
||||
m_stepRecorder.noteReleased(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3580,6 +3802,7 @@ void PianoRoll::finishRecordNote(const Note & n )
|
||||
void PianoRoll::horScrolled(int new_pos )
|
||||
{
|
||||
m_currentPosition = new_pos;
|
||||
m_stepRecorderWidget.setCurrentPosition(m_currentPosition);
|
||||
emit positionChanged( m_currentPosition );
|
||||
update();
|
||||
}
|
||||
@@ -3950,6 +4173,13 @@ void PianoRoll::updatePositionAccompany( const MidiTime & t )
|
||||
}
|
||||
|
||||
|
||||
void PianoRoll::updatePositionStepRecording( const MidiTime & t )
|
||||
{
|
||||
if( m_stepRecorder.isRecording() )
|
||||
{
|
||||
autoScroll( t );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PianoRoll::zoomingChanged()
|
||||
@@ -3959,6 +4189,8 @@ void PianoRoll::zoomingChanged()
|
||||
assert( m_ppt > 0 );
|
||||
|
||||
m_timeLine->setPixelsPerTact( m_ppt );
|
||||
m_stepRecorderWidget.setPixelsPerTact( m_ppt );
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -3970,7 +4202,11 @@ void PianoRoll::quantizeChanged()
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
void PianoRoll::noteLengthChanged()
|
||||
{
|
||||
m_stepRecorder.setStepsLength(newNoteLen());
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
int PianoRoll::quantization() const
|
||||
@@ -4107,7 +4343,7 @@ Note * PianoRoll::noteUnderMouse()
|
||||
|
||||
|
||||
PianoRollWindow::PianoRollWindow() :
|
||||
Editor(true),
|
||||
Editor(true, true),
|
||||
m_editor(new PianoRoll())
|
||||
{
|
||||
setCentralWidget( m_editor );
|
||||
@@ -4115,6 +4351,7 @@ PianoRollWindow::PianoRollWindow() :
|
||||
m_playAction->setToolTip(tr( "Play/pause current pattern (Space)" ) );
|
||||
m_recordAction->setToolTip(tr( "Record notes from MIDI-device/channel-piano" ) );
|
||||
m_recordAccompanyAction->setToolTip( tr( "Record notes from MIDI-device/channel-piano while playing song or BB track" ) );
|
||||
m_toggleStepRecordingAction->setToolTip( tr( "Record notes from MIDI-device/channel-piano, one step at the time" ) );
|
||||
m_stopAction->setToolTip( tr( "Stop playing of current pattern (Space)" ) );
|
||||
|
||||
DropToolBar *notesActionsToolBar = addDropToolBarToTop( tr( "Edit actions" ) );
|
||||
@@ -4221,8 +4458,15 @@ PianoRollWindow::PianoRollWindow() :
|
||||
m_chordComboBox = new ComboBox( m_toolBar );
|
||||
m_chordComboBox->setModel( &m_editor->m_chordModel );
|
||||
m_chordComboBox->setFixedSize( 105, 22 );
|
||||
m_chordComboBox->setToolTip( tr( "Chord") );
|
||||
m_chordComboBox->setToolTip( tr( "Chord" ) );
|
||||
|
||||
// -- Clear ghost pattern button
|
||||
m_clearGhostButton = new QPushButton( m_toolBar );
|
||||
m_clearGhostButton->setIcon( embed::getIconPixmap( "clear_ghost_note" ) );
|
||||
m_clearGhostButton->setToolTip( tr( "Clear ghost notes" ) );
|
||||
m_clearGhostButton->setEnabled( false );
|
||||
connect( m_clearGhostButton, SIGNAL( clicked() ), m_editor, SLOT( clearGhostPattern() ) );
|
||||
connect( m_editor, SIGNAL( ghostPatternSet( bool ) ), this, SLOT( ghostPatternSet( bool ) ) );
|
||||
|
||||
zoomAndNotesToolBar->addWidget( zoom_lbl );
|
||||
zoomAndNotesToolBar->addWidget( m_zoomingComboBox );
|
||||
@@ -4243,6 +4487,9 @@ PianoRollWindow::PianoRollWindow() :
|
||||
zoomAndNotesToolBar->addWidget( chord_lbl );
|
||||
zoomAndNotesToolBar->addWidget( m_chordComboBox );
|
||||
|
||||
zoomAndNotesToolBar->addSeparator();
|
||||
zoomAndNotesToolBar->addWidget( m_clearGhostButton );
|
||||
|
||||
// setup our actual window
|
||||
setFocusPolicy( Qt::StrongFocus );
|
||||
setFocus();
|
||||
@@ -4251,7 +4498,7 @@ PianoRollWindow::PianoRollWindow() :
|
||||
|
||||
// Connections
|
||||
connect( m_editor, SIGNAL( currentPatternChanged() ), this, SIGNAL( currentPatternChanged() ) );
|
||||
connect( m_editor, SIGNAL( currentPatternChanged() ), this, SLOT( patternRenamed() ) );
|
||||
connect( m_editor, SIGNAL( currentPatternChanged() ), this, SLOT( updateAfterPatternChange() ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -4265,6 +4512,14 @@ const Pattern* PianoRollWindow::currentPattern() const
|
||||
|
||||
|
||||
|
||||
void PianoRollWindow::setGhostPattern( Pattern* pattern )
|
||||
{
|
||||
m_editor->setGhostPattern( pattern );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PianoRollWindow::setCurrentPattern( Pattern* pattern )
|
||||
{
|
||||
m_editor->setCurrentPattern( pattern );
|
||||
@@ -4272,8 +4527,8 @@ void PianoRollWindow::setCurrentPattern( Pattern* pattern )
|
||||
if ( pattern )
|
||||
{
|
||||
setWindowTitle( tr( "Piano-Roll - %1" ).arg( pattern->name() ) );
|
||||
connect( pattern->instrumentTrack(), SIGNAL( nameChanged() ), this, SLOT( patternRenamed()) );
|
||||
connect( pattern, SIGNAL( dataChanged() ), this, SLOT( patternRenamed() ) );
|
||||
connect( pattern->instrumentTrack(), SIGNAL( nameChanged() ), this, SLOT( updateAfterPatternChange()) );
|
||||
connect( pattern, SIGNAL( dataChanged() ), this, SLOT( updateAfterPatternChange() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4318,6 +4573,8 @@ void PianoRollWindow::stop()
|
||||
|
||||
void PianoRollWindow::record()
|
||||
{
|
||||
stopStepRecording(); //step recording mode is mutually exclusive with other record modes
|
||||
|
||||
m_editor->record();
|
||||
}
|
||||
|
||||
@@ -4326,11 +4583,25 @@ void PianoRollWindow::record()
|
||||
|
||||
void PianoRollWindow::recordAccompany()
|
||||
{
|
||||
stopStepRecording(); //step recording mode is mutually exclusive with other record modes
|
||||
|
||||
m_editor->recordAccompany();
|
||||
}
|
||||
|
||||
|
||||
void PianoRollWindow::toggleStepRecording()
|
||||
{
|
||||
if(isRecording())
|
||||
{
|
||||
// step recording mode is mutually exclusive with other record modes
|
||||
// stop them before starting step recording
|
||||
stop();
|
||||
}
|
||||
|
||||
m_editor->toggleStepRecording();
|
||||
|
||||
updateStepRecordingIcon();
|
||||
}
|
||||
|
||||
void PianoRollWindow::stopRecording()
|
||||
{
|
||||
@@ -4350,6 +4621,21 @@ void PianoRollWindow::reset()
|
||||
|
||||
void PianoRollWindow::saveSettings( QDomDocument & doc, QDomElement & de )
|
||||
{
|
||||
if( !m_editor->ghostNotes().empty() )
|
||||
{
|
||||
QDomElement ghostNotesRoot = doc.createElement( "ghostnotes" );
|
||||
for( Note *note : m_editor->ghostNotes() )
|
||||
{
|
||||
QDomElement ghostNoteNode = doc.createElement( "ghostnote" );
|
||||
ghostNoteNode.setAttribute( "len", note->length() );
|
||||
ghostNoteNode.setAttribute( "key", note->key() );
|
||||
ghostNoteNode.setAttribute( "pos", note->pos() );
|
||||
|
||||
ghostNotesRoot.appendChild(ghostNoteNode);
|
||||
}
|
||||
de.appendChild( ghostNotesRoot );
|
||||
}
|
||||
|
||||
MainWindow::saveWidgetState( this, de );
|
||||
}
|
||||
|
||||
@@ -4358,6 +4644,8 @@ void PianoRollWindow::saveSettings( QDomDocument & doc, QDomElement & de )
|
||||
|
||||
void PianoRollWindow::loadSettings( const QDomElement & de )
|
||||
{
|
||||
m_editor->loadGhostNotes( de.firstChildElement("ghostnotes") );
|
||||
|
||||
MainWindow::restoreWidgetState( this, de );
|
||||
}
|
||||
|
||||
@@ -4371,6 +4659,11 @@ QSize PianoRollWindow::sizeHint() const
|
||||
|
||||
|
||||
|
||||
void PianoRollWindow::updateAfterPatternChange()
|
||||
{
|
||||
patternRenamed();
|
||||
updateStepRecordingIcon(); //pattern change turn step recording OFF - update icon accordingly
|
||||
}
|
||||
|
||||
void PianoRollWindow::patternRenamed()
|
||||
{
|
||||
@@ -4387,8 +4680,37 @@ void PianoRollWindow::patternRenamed()
|
||||
|
||||
|
||||
|
||||
void PianoRollWindow::ghostPatternSet( bool state )
|
||||
{
|
||||
m_clearGhostButton->setEnabled( state );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PianoRollWindow::focusInEvent( QFocusEvent * event )
|
||||
{
|
||||
// when the window is given focus, also give focus to the actual piano roll
|
||||
m_editor->setFocus( event->reason() );
|
||||
}
|
||||
|
||||
void PianoRollWindow::stopStepRecording()
|
||||
{
|
||||
if(m_editor->isStepRecording())
|
||||
{
|
||||
m_editor->toggleStepRecording();
|
||||
updateStepRecordingIcon();
|
||||
}
|
||||
}
|
||||
|
||||
void PianoRollWindow::updateStepRecordingIcon()
|
||||
{
|
||||
if(m_editor->isStepRecording())
|
||||
{
|
||||
m_toggleStepRecordingAction->setIcon(embed::getIconPixmap("record_step_on"));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_toggleStepRecordingAction->setIcon(embed::getIconPixmap("record_step_off"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -654,7 +654,7 @@ ComboBoxModel *SongEditor::zoomingModel() const
|
||||
|
||||
|
||||
SongEditorWindow::SongEditorWindow(Song* song) :
|
||||
Editor(Engine::mixer()->audioDev()->supportsCapture()),
|
||||
Editor(Engine::mixer()->audioDev()->supportsCapture(), false),
|
||||
m_editor(new SongEditor(song)),
|
||||
m_crtlAction( NULL )
|
||||
{
|
||||
@@ -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();
|
||||
|
||||
@@ -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:" ), "" );
|
||||
|
||||
|
||||
|
||||
@@ -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 *
|
||||
|
||||
66
src/gui/widgets/FxLineLcdSpinBox.cpp
Normal file
66
src/gui/widgets/FxLineLcdSpinBox.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* FxLineLcdSpinBox.cpp - a specialization of LcdSpnBox for setting FX channels
|
||||
*
|
||||
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
*
|
||||
* 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 "FxLineLcdSpinBox.h"
|
||||
|
||||
#include "CaptionMenu.h"
|
||||
#include "FxMixerView.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "Track.h"
|
||||
|
||||
void FxLineLcdSpinBox::setTrackView(TrackView * tv)
|
||||
{
|
||||
m_tv = tv;
|
||||
}
|
||||
|
||||
void FxLineLcdSpinBox::mouseDoubleClickEvent(QMouseEvent* event)
|
||||
{
|
||||
gui->fxMixerView()->setCurrentFxLine(model()->value());
|
||||
|
||||
gui->fxMixerView()->parentWidget()->show();
|
||||
gui->fxMixerView()->show();// show fxMixer window
|
||||
gui->fxMixerView()->setFocus();// set focus to fxMixer window
|
||||
//engine::getFxMixerView()->raise();
|
||||
}
|
||||
|
||||
void FxLineLcdSpinBox::contextMenuEvent(QContextMenuEvent* event)
|
||||
{
|
||||
// for the case, the user clicked right while pressing left mouse-
|
||||
// button, the context-menu appears while mouse-cursor is still hidden
|
||||
// and it isn't shown again until user does something which causes
|
||||
// an QApplication::restoreOverrideCursor()-call...
|
||||
mouseReleaseEvent(nullptr);
|
||||
|
||||
QPointer<CaptionMenu> contextMenu = new CaptionMenu(model()->displayName(), this);
|
||||
|
||||
if (QMenu *fxMenu = m_tv->createFxMenu(
|
||||
tr("Assign to:"), tr("New FX Channel")))
|
||||
{
|
||||
contextMenu->addMenu(fxMenu);
|
||||
|
||||
contextMenu->addSeparator();
|
||||
}
|
||||
addDefaultActions(contextMenu);
|
||||
contextMenu->exec(QCursor::pos());
|
||||
}
|
||||
@@ -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 )
|
||||
{
|
||||
|
||||
155
src/gui/widgets/StepRecorderWidget.cpp
Normal file
155
src/gui/widgets/StepRecorderWidget.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* StepRecoderWidget.cpp - widget that provide gui markers for step recording
|
||||
*
|
||||
* 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 "StepRecorderWidget.h"
|
||||
#include "TextFloat.h"
|
||||
#include "embed.h"
|
||||
|
||||
StepRecorderWidget::StepRecorderWidget(
|
||||
QWidget * parent,
|
||||
const int ppt,
|
||||
const int marginTop,
|
||||
const int marginBottom,
|
||||
const int marginLeft,
|
||||
const int marginRight) :
|
||||
QWidget(parent),
|
||||
m_marginTop(marginTop),
|
||||
m_marginBottom(marginBottom),
|
||||
m_marginLeft(marginLeft),
|
||||
m_marginRight(marginRight)
|
||||
{
|
||||
const QColor baseColor = QColor(255, 0, 0);// QColor(204, 163, 0); // Orange
|
||||
m_colorLineEnd = baseColor.lighter(150);
|
||||
m_colorLineStart = baseColor.darker(120);
|
||||
|
||||
setAttribute(Qt::WA_NoSystemBackground, true);
|
||||
setPixelsPerTact(ppt);
|
||||
|
||||
m_top = m_marginTop;
|
||||
m_left = m_marginLeft;
|
||||
}
|
||||
|
||||
void StepRecorderWidget::setPixelsPerTact(int ppt)
|
||||
{
|
||||
m_ppt = ppt;
|
||||
}
|
||||
|
||||
void StepRecorderWidget::setCurrentPosition(MidiTime currentPosition)
|
||||
{
|
||||
m_currentPosition = currentPosition;
|
||||
}
|
||||
|
||||
void StepRecorderWidget::setBottomMargin(const int marginBottom)
|
||||
{
|
||||
m_marginBottom = marginBottom;
|
||||
}
|
||||
|
||||
void StepRecorderWidget::setStartPosition(MidiTime pos)
|
||||
{
|
||||
m_curStepStartPos = pos;
|
||||
}
|
||||
|
||||
void StepRecorderWidget::setEndPosition(MidiTime pos)
|
||||
{
|
||||
m_curStepEndPos = pos;
|
||||
emit positionChanged(m_curStepEndPos);
|
||||
}
|
||||
|
||||
void StepRecorderWidget::showHint()
|
||||
{
|
||||
TextFloat::displayMessage(tr( "Hint" ), tr("Move recording curser using <Left/Right> arrows"),
|
||||
embed::getIconPixmap("hint"));
|
||||
}
|
||||
|
||||
void StepRecorderWidget::setStepsLength(MidiTime stepsLength)
|
||||
{
|
||||
m_stepsLength = stepsLength;
|
||||
}
|
||||
|
||||
void StepRecorderWidget::paintEvent(QPaintEvent * pe)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
updateBoundaries();
|
||||
|
||||
move(0, 0);
|
||||
|
||||
//draw steps ruler
|
||||
painter.setPen(m_colorLineEnd);
|
||||
|
||||
MidiTime curPos = m_curStepEndPos;
|
||||
int x = xCoordOfTick(curPos);
|
||||
while(x <= m_right)
|
||||
{
|
||||
const int w = 2;
|
||||
const int h = 4;
|
||||
painter.drawRect(x - 1, m_top, w, h);
|
||||
curPos += m_stepsLength;
|
||||
x = xCoordOfTick(curPos);
|
||||
}
|
||||
|
||||
//draw current step start/end position lines
|
||||
if(m_curStepStartPos != m_curStepEndPos)
|
||||
{
|
||||
drawVerLine(&painter, m_curStepStartPos, m_colorLineStart, m_top, m_bottom);
|
||||
}
|
||||
|
||||
drawVerLine(&painter, m_curStepEndPos, m_colorLineEnd, m_top, m_bottom);
|
||||
|
||||
//if the line is adjacent to the keyboard at the left - it cannot be seen.
|
||||
//add another line to make it clearer
|
||||
if(m_curStepEndPos == 0)
|
||||
{
|
||||
drawVerLine(&painter, xCoordOfTick(m_curStepEndPos) + 1, m_colorLineEnd, m_top, m_bottom);
|
||||
}
|
||||
}
|
||||
|
||||
int StepRecorderWidget::xCoordOfTick(int tick)
|
||||
{
|
||||
return m_marginLeft + ((tick - m_currentPosition) * m_ppt / MidiTime::ticksPerTact());
|
||||
}
|
||||
|
||||
|
||||
void StepRecorderWidget::drawVerLine(QPainter* painter, int x, const QColor& color, int top, int bottom)
|
||||
{
|
||||
if(x >= m_marginLeft && x <= (width() - m_marginRight))
|
||||
{
|
||||
painter->setPen(color);
|
||||
painter->drawLine( x, top, x, bottom );
|
||||
}
|
||||
}
|
||||
|
||||
void StepRecorderWidget::drawVerLine(QPainter* painter, const MidiTime& pos, const QColor& color, int top, int bottom)
|
||||
{
|
||||
drawVerLine(painter, xCoordOfTick(pos), color, top, bottom);
|
||||
}
|
||||
|
||||
void StepRecorderWidget::updateBoundaries()
|
||||
{
|
||||
setFixedSize(parentWidget()->size());
|
||||
|
||||
m_bottom = height() - m_marginBottom;
|
||||
m_right = width() - m_marginTop;
|
||||
|
||||
//(no need to change top and left as they are static)
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "EffectRackView.h"
|
||||
#include "embed.h"
|
||||
#include "FileBrowser.h"
|
||||
#include "FxLineLcdSpinBox.h"
|
||||
#include "FxMixer.h"
|
||||
#include "FxMixerView.h"
|
||||
#include "GuiApplication.h"
|
||||
@@ -126,10 +127,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 );
|
||||
}
|
||||
|
||||
|
||||
@@ -1258,52 +1263,6 @@ QMenu * InstrumentTrackView::createFxMenu(QString title, QString newFxLabel)
|
||||
|
||||
|
||||
|
||||
class fxLineLcdSpinBox : public LcdSpinBox
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
fxLineLcdSpinBox( int _num_digits, QWidget * _parent,
|
||||
const QString & _name ) :
|
||||
LcdSpinBox( _num_digits, _parent, _name ) {}
|
||||
|
||||
protected:
|
||||
virtual void mouseDoubleClickEvent ( QMouseEvent * _me )
|
||||
{
|
||||
gui->fxMixerView()->setCurrentFxLine( model()->value() );
|
||||
|
||||
gui->fxMixerView()->parentWidget()->show();
|
||||
gui->fxMixerView()->show();// show fxMixer window
|
||||
gui->fxMixerView()->setFocus();// set focus to fxMixer window
|
||||
//engine::getFxMixerView()->raise();
|
||||
}
|
||||
|
||||
virtual void contextMenuEvent( QContextMenuEvent* event )
|
||||
{
|
||||
// for the case, the user clicked right while pressing left mouse-
|
||||
// button, the context-menu appears while mouse-cursor is still hidden
|
||||
// and it isn't shown again until user does something which causes
|
||||
// an QApplication::restoreOverrideCursor()-call...
|
||||
mouseReleaseEvent( NULL );
|
||||
|
||||
QPointer<CaptionMenu> contextMenu = new CaptionMenu( model()->displayName(), this );
|
||||
|
||||
// This condition is here just as a safety check, fxLineLcdSpinBox is aways
|
||||
// created inside a TabWidget inside an InstrumentTrackWindow
|
||||
if ( InstrumentTrackWindow* window = dynamic_cast<InstrumentTrackWindow*>( (QWidget *)this->parent()->parent() ) )
|
||||
{
|
||||
QMenu *fxMenu = window->instrumentTrackView()->createFxMenu( tr( "Assign to:" ), tr( "New FX channel" ) );
|
||||
contextMenu->addMenu( fxMenu );
|
||||
|
||||
contextMenu->addSeparator();
|
||||
}
|
||||
addDefaultActions( contextMenu );
|
||||
contextMenu->exec( QCursor::pos() );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
// #### ITW:
|
||||
InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) :
|
||||
QWidget(),
|
||||
@@ -1424,7 +1383,7 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) :
|
||||
|
||||
|
||||
// setup spinbox for selecting FX-channel
|
||||
m_effectChannelNumber = new fxLineLcdSpinBox( 2, NULL, tr( "FX channel" ) );
|
||||
m_effectChannelNumber = new FxLineLcdSpinBox( 2, NULL, tr( "FX channel" ), m_itv );
|
||||
|
||||
basicControlsLayout->addWidget( m_effectChannelNumber, 0, 6 );
|
||||
basicControlsLayout->setAlignment( m_effectChannelNumber, widgetAlignment );
|
||||
@@ -1545,6 +1504,7 @@ void InstrumentTrackWindow::setInstrumentTrackView( InstrumentTrackView* view )
|
||||
}
|
||||
|
||||
m_itv = view;
|
||||
m_effectChannelNumber->setTrackView(m_itv);
|
||||
}
|
||||
|
||||
|
||||
@@ -1663,6 +1623,8 @@ void InstrumentTrackWindow::updateInstrumentView()
|
||||
|
||||
modelChanged(); // Get the instrument window to refresh
|
||||
m_track->dataChanged(); // Get the text on the trackButton to change
|
||||
|
||||
m_pianoView->setVisible(m_track->m_instrument->hasNoteInput());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1717,7 +1679,9 @@ void InstrumentTrackWindow::closeEvent( QCloseEvent* event )
|
||||
|
||||
void InstrumentTrackWindow::focusInEvent( QFocusEvent* )
|
||||
{
|
||||
m_pianoView->setFocus();
|
||||
if(m_pianoView->isVisible()) {
|
||||
m_pianoView->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1851,6 +1815,9 @@ void InstrumentTrackWindow::viewInstrumentInDirection(int d)
|
||||
|
||||
// scroll the SongEditor/BB-editor to make sure the new trackview label is visible
|
||||
bringToFront->trackContainerView()->scrollToTrackView(bringToFront);
|
||||
|
||||
// get the instrument window to refresh
|
||||
modelChanged();
|
||||
}
|
||||
bringToFront->getInstrumentTrackWindow()->setFocus();
|
||||
}
|
||||
|
||||
@@ -637,6 +637,18 @@ void PatternView::openInPianoRoll()
|
||||
|
||||
|
||||
|
||||
|
||||
void PatternView::setGhostInPianoRoll()
|
||||
{
|
||||
gui->pianoRoll()->setGhostPattern( m_pat );
|
||||
gui->pianoRoll()->parentWidget()->show();
|
||||
gui->pianoRoll()->show();
|
||||
gui->pianoRoll()->setFocus();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PatternView::resetName()
|
||||
{
|
||||
m_pat->setName( m_pat->m_instrumentTrack->name() );
|
||||
@@ -663,8 +675,14 @@ void PatternView::constructContextMenu( QMenu * _cm )
|
||||
_cm->insertAction( _cm->actions()[0], a );
|
||||
connect( a, SIGNAL( triggered( bool ) ),
|
||||
this, SLOT( openInPianoRoll() ) );
|
||||
_cm->insertSeparator( _cm->actions()[1] );
|
||||
|
||||
QAction * b = new QAction( embed::getIconPixmap( "ghost_note" ),
|
||||
tr( "Set as ghost in piano-roll" ), _cm );
|
||||
if( m_pat->empty() ) { b->setEnabled( false ); }
|
||||
_cm->insertAction( _cm->actions()[1], b );
|
||||
connect( b, SIGNAL( triggered( bool ) ),
|
||||
this, SLOT( setGhostInPianoRoll() ) );
|
||||
_cm->insertSeparator( _cm->actions()[2] );
|
||||
_cm->addSeparator();
|
||||
|
||||
_cm->addAction( embed::getIconPixmap( "edit_erase" ),
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <QFileInfo>
|
||||
#include <QMenu>
|
||||
#include <QLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QMdiArea>
|
||||
#include <QMdiSubWindow>
|
||||
#include <QPainter>
|
||||
@@ -48,6 +49,8 @@
|
||||
#include "MainWindow.h"
|
||||
#include "Mixer.h"
|
||||
#include "EffectRackView.h"
|
||||
#include "FxMixerView.h"
|
||||
#include "TabWidget.h"
|
||||
#include "TrackLabelButton.h"
|
||||
|
||||
SampleTCO::SampleTCO( Track * _track ) :
|
||||
@@ -471,7 +474,7 @@ void SampleTCOView::paintEvent( QPaintEvent * pe )
|
||||
bool muted = m_tco->getTrack()->isMuted() || m_tco->isMuted();
|
||||
|
||||
// state: selected, muted, normal
|
||||
c = isSelected() ? selectedColor() : ( muted ? mutedBackgroundColor()
|
||||
c = isSelected() ? selectedColor() : ( muted ? mutedBackgroundColor()
|
||||
: painter.background().color() );
|
||||
|
||||
lingrad.setColorAt( 1, c.darker( 300 ) );
|
||||
@@ -515,7 +518,7 @@ void SampleTCOView::paintEvent( QPaintEvent * pe )
|
||||
|
||||
// inner border
|
||||
p.setPen( c.lighter( 160 ) );
|
||||
p.drawRect( 1, 1, rect().right() - TCO_BORDER_WIDTH,
|
||||
p.drawRect( 1, 1, rect().right() - TCO_BORDER_WIDTH,
|
||||
rect().bottom() - TCO_BORDER_WIDTH );
|
||||
|
||||
// outer border
|
||||
@@ -531,7 +534,7 @@ void SampleTCOView::paintEvent( QPaintEvent * pe )
|
||||
embed::getIconPixmap( "muted", size, size ) );
|
||||
}
|
||||
|
||||
// recording sample tracks is not possible at the moment
|
||||
// recording sample tracks is not possible at the moment
|
||||
|
||||
/* if( m_tco->isRecord() )
|
||||
{
|
||||
@@ -562,10 +565,14 @@ SampleTrack::SampleTrack( TrackContainer* tc ) :
|
||||
tr( "Volume" ) ),
|
||||
m_panningModel( DefaultPanning, PanningLeft, PanningRight, 0.1f,
|
||||
this, tr( "Panning" ) ),
|
||||
m_effectChannelModel( 0, 0, 0, this, tr( "FX channel" ) ),
|
||||
m_audioPort( tr( "Sample track" ), true, &m_volumeModel, &m_panningModel, &m_mutedModel )
|
||||
{
|
||||
setName( tr( "Sample track" ) );
|
||||
m_panningModel.setCenterValue( DefaultPanning );
|
||||
m_effectChannelModel.setRange( 0, Engine::fxMixer()->numChannels()-1, 1);
|
||||
|
||||
connect( &m_effectChannelModel, SIGNAL( dataChanged() ), this, SLOT( updateEffectChannel() ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -693,6 +700,7 @@ void SampleTrack::saveTrackSpecificSettings( QDomDocument & _doc,
|
||||
#endif
|
||||
m_volumeModel.saveSettings( _doc, _this, "vol" );
|
||||
m_panningModel.saveSettings( _doc, _this, "pan" );
|
||||
m_effectChannelModel.saveSettings( _doc, _this, "fxch" );
|
||||
}
|
||||
|
||||
|
||||
@@ -715,6 +723,8 @@ void SampleTrack::loadTrackSpecificSettings( const QDomElement & _this )
|
||||
}
|
||||
m_volumeModel.loadSettings( _this, "vol" );
|
||||
m_panningModel.loadSettings( _this, "pan" );
|
||||
m_effectChannelModel.setRange( 0, Engine::fxMixer()->numChannels() - 1 );
|
||||
m_effectChannelModel.loadSettings( _this, "fxch" );
|
||||
}
|
||||
|
||||
|
||||
@@ -742,6 +752,14 @@ void SampleTrack::setPlayingTcos( bool isPlaying )
|
||||
|
||||
|
||||
|
||||
void SampleTrack::updateEffectChannel()
|
||||
{
|
||||
m_audioPort.setNextFxChannel( m_effectChannelModel.value() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) :
|
||||
@@ -749,13 +767,13 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) :
|
||||
{
|
||||
setFixedHeight( 32 );
|
||||
|
||||
TrackLabelButton * tlb = new TrackLabelButton( this,
|
||||
getTrackSettingsWidget() );
|
||||
connect( tlb, SIGNAL( clicked( bool ) ),
|
||||
this, SLOT( showEffects() ) );
|
||||
tlb->setIcon( embed::getIconPixmap( "sample_track" ) );
|
||||
tlb->move( 3, 1 );
|
||||
tlb->show();
|
||||
m_tlb = new TrackLabelButton(this, getTrackSettingsWidget());
|
||||
m_tlb->setCheckable(true);
|
||||
connect(m_tlb, SIGNAL(clicked( bool )),
|
||||
this, SLOT(showEffects()));
|
||||
m_tlb->setIcon(embed::getIconPixmap("sample_track"));
|
||||
m_tlb->move(3, 1);
|
||||
m_tlb->show();
|
||||
|
||||
m_volumeKnob = new Knob( knobSmall_17, getTrackSettingsWidget(),
|
||||
tr( "Track volume" ) );
|
||||
@@ -779,16 +797,10 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) :
|
||||
m_panningKnob->setLabel( tr( "PAN" ) );
|
||||
m_panningKnob->show();
|
||||
|
||||
m_effectRack = new EffectRackView( _t->audioPort()->effects() );
|
||||
m_effectRack->setFixedSize( 240, 242 );
|
||||
|
||||
m_effWindow = gui->mainWindow()->addWindowedWidget( m_effectRack );
|
||||
m_effWindow->setAttribute( Qt::WA_DeleteOnClose, false );
|
||||
m_effWindow->layout()->setSizeConstraint( QLayout::SetFixedSize );
|
||||
m_effWindow->setWindowTitle( _t->name() );
|
||||
m_effWindow->hide();
|
||||
|
||||
setModel( _t );
|
||||
|
||||
m_window = new SampleTrackWindow(this);
|
||||
m_window->toggleVisibility(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -796,7 +808,50 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) :
|
||||
|
||||
SampleTrackView::~SampleTrackView()
|
||||
{
|
||||
m_effWindow->deleteLater();
|
||||
if(m_window != NULL)
|
||||
{
|
||||
m_window->setSampleTrackView(NULL);
|
||||
m_window->parentWidget()->hide();
|
||||
}
|
||||
m_window = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QMenu * SampleTrackView::createFxMenu(QString title, QString newFxLabel)
|
||||
{
|
||||
int channelIndex = model()->effectChannelModel()->value();
|
||||
|
||||
FxChannel *fxChannel = Engine::fxMixer()->effectChannel(channelIndex);
|
||||
|
||||
// If title allows interpolation, pass channel index and name
|
||||
if (title.contains("%2"))
|
||||
{
|
||||
title = title.arg(channelIndex).arg(fxChannel->m_name);
|
||||
}
|
||||
|
||||
QMenu *fxMenu = new QMenu(title);
|
||||
|
||||
QSignalMapper * fxMenuSignalMapper = new QSignalMapper(fxMenu);
|
||||
|
||||
fxMenu->addAction(newFxLabel, this, SLOT(createFxLine()));
|
||||
fxMenu->addSeparator();
|
||||
|
||||
for (int i = 0; i < Engine::fxMixer()->numChannels(); ++i)
|
||||
{
|
||||
FxChannel * currentChannel = Engine::fxMixer()->effectChannel(i);
|
||||
|
||||
if (currentChannel != fxChannel)
|
||||
{
|
||||
QString label = tr("FX %1: %2").arg(currentChannel->m_channelIndex).arg(currentChannel->m_name);
|
||||
QAction * action = fxMenu->addAction(label, fxMenuSignalMapper, SLOT(map()));
|
||||
fxMenuSignalMapper->setMapping(action, currentChannel->m_channelIndex);
|
||||
}
|
||||
}
|
||||
|
||||
connect(fxMenuSignalMapper, SIGNAL(mapped(int)), this, SLOT(assignFxLine(int)));
|
||||
|
||||
return fxMenu;
|
||||
}
|
||||
|
||||
|
||||
@@ -804,16 +859,7 @@ SampleTrackView::~SampleTrackView()
|
||||
|
||||
void SampleTrackView::showEffects()
|
||||
{
|
||||
if( m_effWindow->isHidden() )
|
||||
{
|
||||
m_effectRack->show();
|
||||
m_effWindow->show();
|
||||
m_effWindow->raise();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_effWindow->hide();
|
||||
}
|
||||
m_window->toggleVisibility(m_window->parentWidget()->isHidden());
|
||||
}
|
||||
|
||||
|
||||
@@ -821,7 +867,261 @@ void SampleTrackView::showEffects()
|
||||
void SampleTrackView::modelChanged()
|
||||
{
|
||||
SampleTrack * st = castModel<SampleTrack>();
|
||||
m_volumeKnob->setModel( &st->m_volumeModel );
|
||||
m_volumeKnob->setModel(&st->m_volumeModel);
|
||||
|
||||
TrackView::modelChanged();
|
||||
}
|
||||
|
||||
|
||||
|
||||
SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) :
|
||||
QWidget(),
|
||||
ModelView(NULL, this),
|
||||
m_track(tv->model()),
|
||||
m_stv(tv)
|
||||
{
|
||||
// init own layout + widgets
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
QVBoxLayout * vlayout = new QVBoxLayout(this);
|
||||
vlayout->setMargin(0);
|
||||
vlayout->setSpacing(0);
|
||||
|
||||
TabWidget* generalSettingsWidget = new TabWidget(tr("GENERAL SETTINGS"), this);
|
||||
|
||||
QVBoxLayout* generalSettingsLayout = new QVBoxLayout(generalSettingsWidget);
|
||||
|
||||
generalSettingsLayout->setContentsMargins(8, 18, 8, 8);
|
||||
generalSettingsLayout->setSpacing(6);
|
||||
|
||||
QWidget* nameWidget = new QWidget(generalSettingsWidget);
|
||||
QHBoxLayout* nameLayout = new QHBoxLayout(nameWidget);
|
||||
nameLayout->setContentsMargins(0, 0, 0, 0);
|
||||
nameLayout->setSpacing(2);
|
||||
|
||||
// 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 &)));
|
||||
|
||||
m_nameLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred));
|
||||
nameLayout->addWidget(m_nameLineEdit);
|
||||
|
||||
|
||||
generalSettingsLayout->addWidget(nameWidget);
|
||||
|
||||
|
||||
QGridLayout* basicControlsLayout = new QGridLayout;
|
||||
basicControlsLayout->setHorizontalSpacing(3);
|
||||
basicControlsLayout->setVerticalSpacing(0);
|
||||
basicControlsLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
QString labelStyleSheet = "font-size: 6pt;";
|
||||
Qt::Alignment labelAlignment = Qt::AlignHCenter | Qt::AlignTop;
|
||||
Qt::Alignment widgetAlignment = Qt::AlignHCenter | Qt::AlignCenter;
|
||||
|
||||
// set up volume knob
|
||||
m_volumeKnob = new Knob(knobBright_26, NULL, tr("Sample volume"));
|
||||
m_volumeKnob->setVolumeKnob(true);
|
||||
m_volumeKnob->setHintText(tr("Volume:"), "%");
|
||||
|
||||
basicControlsLayout->addWidget(m_volumeKnob, 0, 0);
|
||||
basicControlsLayout->setAlignment(m_volumeKnob, widgetAlignment);
|
||||
|
||||
QLabel *label = new QLabel(tr("VOL"), this);
|
||||
label->setStyleSheet(labelStyleSheet);
|
||||
basicControlsLayout->addWidget(label, 1, 0);
|
||||
basicControlsLayout->setAlignment(label, labelAlignment);
|
||||
|
||||
|
||||
// set up panning knob
|
||||
m_panningKnob = new Knob(knobBright_26, NULL, tr("Panning"));
|
||||
m_panningKnob->setHintText(tr("Panning:"), "");
|
||||
|
||||
basicControlsLayout->addWidget(m_panningKnob, 0, 1);
|
||||
basicControlsLayout->setAlignment(m_panningKnob, widgetAlignment);
|
||||
|
||||
label = new QLabel(tr("PAN"),this);
|
||||
label->setStyleSheet(labelStyleSheet);
|
||||
basicControlsLayout->addWidget(label, 1, 1);
|
||||
basicControlsLayout->setAlignment(label, labelAlignment);
|
||||
|
||||
|
||||
basicControlsLayout->setColumnStretch(2, 1);
|
||||
|
||||
|
||||
// setup spinbox for selecting FX-channel
|
||||
m_effectChannelNumber = new FxLineLcdSpinBox(2, NULL, tr("FX channel"), m_stv);
|
||||
|
||||
basicControlsLayout->addWidget(m_effectChannelNumber, 0, 3);
|
||||
basicControlsLayout->setAlignment(m_effectChannelNumber, widgetAlignment);
|
||||
|
||||
label = new QLabel(tr("FX"), this);
|
||||
label->setStyleSheet(labelStyleSheet);
|
||||
basicControlsLayout->addWidget(label, 1, 3);
|
||||
basicControlsLayout->setAlignment(label, labelAlignment);
|
||||
|
||||
generalSettingsLayout->addLayout(basicControlsLayout);
|
||||
|
||||
m_effectRack = new EffectRackView(tv->model()->audioPort()->effects());
|
||||
m_effectRack->setFixedSize(240, 242);
|
||||
|
||||
vlayout->addWidget(generalSettingsWidget);
|
||||
vlayout->addWidget(m_effectRack);
|
||||
|
||||
|
||||
setModel(tv->model());
|
||||
|
||||
QMdiSubWindow * subWin = gui->mainWindow()->addWindowedWidget(this);
|
||||
Qt::WindowFlags flags = subWin->windowFlags();
|
||||
flags |= Qt::MSWindowsFixedSizeDialogHint;
|
||||
flags &= ~Qt::WindowMaximizeButtonHint;
|
||||
subWin->setWindowFlags(flags);
|
||||
|
||||
// Hide the Size and Maximize options from the system menu
|
||||
// since the dialog size is fixed.
|
||||
QMenu * systemMenu = subWin->systemMenu();
|
||||
systemMenu->actions().at(2)->setVisible(false); // Size
|
||||
systemMenu->actions().at(4)->setVisible(false); // Maximize
|
||||
|
||||
subWin->setWindowIcon(embed::getIconPixmap("sample_track"));
|
||||
subWin->setFixedSize(subWin->size());
|
||||
subWin->hide();
|
||||
}
|
||||
|
||||
|
||||
|
||||
SampleTrackWindow::~SampleTrackWindow()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SampleTrackWindow::setSampleTrackView(SampleTrackView* tv)
|
||||
{
|
||||
if(m_stv && tv)
|
||||
{
|
||||
m_stv->m_tlb->setChecked(false);
|
||||
}
|
||||
|
||||
m_stv = tv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SampleTrackWindow::modelChanged()
|
||||
{
|
||||
m_track = castModel<SampleTrack>();
|
||||
|
||||
m_nameLineEdit->setText(m_track->name());
|
||||
|
||||
m_track->disconnect(SIGNAL(nameChanged()), this);
|
||||
|
||||
connect(m_track, SIGNAL(nameChanged()),
|
||||
this, SLOT(updateName()));
|
||||
|
||||
m_volumeKnob->setModel(&m_track->m_volumeModel);
|
||||
m_panningKnob->setModel(&m_track->m_panningModel);
|
||||
m_effectChannelNumber->setModel(&m_track->m_effectChannelModel);
|
||||
|
||||
updateName();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*! \brief Create and assign a new FX Channel for this track */
|
||||
void SampleTrackView::createFxLine()
|
||||
{
|
||||
int channelIndex = gui->fxMixerView()->addNewChannel();
|
||||
|
||||
Engine::fxMixer()->effectChannel(channelIndex)->m_name = getTrack()->name();
|
||||
|
||||
assignFxLine(channelIndex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*! \brief Assign a specific FX Channel for this track */
|
||||
void SampleTrackView::assignFxLine(int channelIndex)
|
||||
{
|
||||
model()->effectChannelModel()->setValue(channelIndex);
|
||||
|
||||
gui->fxMixerView()->setCurrentFxLine(channelIndex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SampleTrackWindow::updateName()
|
||||
{
|
||||
setWindowTitle(m_track->name().length() > 25 ? (m_track->name().left(24) + "...") : m_track->name());
|
||||
|
||||
if(m_nameLineEdit->text() != m_track->name())
|
||||
{
|
||||
m_nameLineEdit->setText(m_track->name());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SampleTrackWindow::textChanged(const QString& new_name)
|
||||
{
|
||||
m_track->setName(new_name);
|
||||
Engine::getSong()->setModified();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SampleTrackWindow::toggleVisibility(bool on)
|
||||
{
|
||||
if(on)
|
||||
{
|
||||
show();
|
||||
parentWidget()->show();
|
||||
parentWidget()->raise();
|
||||
}
|
||||
else
|
||||
{
|
||||
parentWidget()->hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void SampleTrackWindow::closeEvent(QCloseEvent* ce)
|
||||
{
|
||||
ce->ignore();
|
||||
|
||||
if(gui->mainWindow()->workspace())
|
||||
{
|
||||
parentWidget()->hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
hide();
|
||||
}
|
||||
|
||||
m_stv->m_tlb->setFocus();
|
||||
m_stv->m_tlb->setChecked(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SampleTrackWindow::saveSettings(QDomDocument& doc, QDomElement & element)
|
||||
{
|
||||
MainWindow::saveWidgetState(this, element);
|
||||
Q_UNUSED(element)
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SampleTrackWindow::loadSettings(const QDomElement& element)
|
||||
{
|
||||
MainWindow::restoreWidgetState(this, element);
|
||||
if(isVisible())
|
||||
{
|
||||
m_stv->m_tlb->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user