Merge remote-tracking branch 'upstream/master' into 2510-SetupDialogWithLayouts-MasterMerge
Merge master to integrate the changes of: *f10277715f: Classier enums *7aca8ae726: SetupDialog: Warn of unusual buffersizes *8fb9c3e6a2: Fix warnings in Clang build (#6893) Solved conflicts in: * src/gui/modals/SetupDialog.cpp
This commit is contained in:
6
src/3rdparty/CMakeLists.txt
vendored
6
src/3rdparty/CMakeLists.txt
vendored
@@ -7,6 +7,12 @@ ADD_SUBDIRECTORY(hiir)
|
||||
ADD_SUBDIRECTORY(rpmalloc)
|
||||
ADD_SUBDIRECTORY(weakjack)
|
||||
|
||||
if(MINGW)
|
||||
option(MINGW_STDTHREADS_GENERATE_STDHEADERS "" ON)
|
||||
add_subdirectory(mingw-std-threads)
|
||||
set(LMMS_USE_MINGW_STD_THREADS ON PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
# The lockless ring buffer library is compiled as part of the core
|
||||
SET(RINGBUFFER_DIR "${CMAKE_SOURCE_DIR}/src/3rdparty/ringbuffer/")
|
||||
SET(RINGBUFFER_DIR ${RINGBUFFER_DIR} PARENT_SCOPE)
|
||||
|
||||
2
src/3rdparty/mingw-std-threads
vendored
2
src/3rdparty/mingw-std-threads
vendored
Submodule src/3rdparty/mingw-std-threads updated: 10665829da...6c2061b7da
@@ -12,7 +12,7 @@ SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
# Enable C++17
|
||||
SET(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
IF(LMMS_BUILD_APPLE)
|
||||
IF(LMMS_BUILD_APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
ENDIF()
|
||||
|
||||
@@ -170,6 +170,10 @@ if(LMMS_HAVE_MP3LAME)
|
||||
list(APPEND EXTRA_LIBRARIES mp3lame::mp3lame)
|
||||
endif()
|
||||
|
||||
if(LMMS_USE_MINGW_STD_THREADS)
|
||||
list(APPEND EXTRA_LIBRARIES mingw_stdthreads)
|
||||
endif()
|
||||
|
||||
SET(LMMS_REQUIRED_LIBS ${LMMS_REQUIRED_LIBS}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${QT_LIBRARIES}
|
||||
|
||||
@@ -81,7 +81,7 @@ AudioEngine::AudioEngine( bool renderOnly ) :
|
||||
m_workers(),
|
||||
m_numWorkers( QThread::idealThreadCount()-1 ),
|
||||
m_newPlayHandles( PlayHandle::MaxNumber ),
|
||||
m_qualitySettings( qualitySettings::Mode_Draft ),
|
||||
m_qualitySettings( qualitySettings::Mode::Draft ),
|
||||
m_masterGain( 1.0f ),
|
||||
m_isProcessing( false ),
|
||||
m_audioDev( nullptr ),
|
||||
@@ -314,7 +314,7 @@ void AudioEngine::pushInputFrames( sampleFrame * _ab, const f_cnt_t _frames )
|
||||
|
||||
if( frames + _frames > size )
|
||||
{
|
||||
size = qMax( size * 2, frames + _frames );
|
||||
size = std::max(size * 2, frames + _frames);
|
||||
auto ab = new sampleFrame[size];
|
||||
memcpy( ab, buf, frames * sizeof( sampleFrame ) );
|
||||
delete [] buf;
|
||||
@@ -333,12 +333,9 @@ void AudioEngine::pushInputFrames( sampleFrame * _ab, const f_cnt_t _frames )
|
||||
|
||||
|
||||
|
||||
|
||||
const surroundSampleFrame * AudioEngine::renderNextBuffer()
|
||||
void AudioEngine::renderStageNoteSetup()
|
||||
{
|
||||
m_profiler.startPeriod();
|
||||
|
||||
s_renderingThread = true;
|
||||
AudioEngineProfiler::Probe profilerProbe(m_profiler, AudioEngineProfiler::DetailType::NoteSetup);
|
||||
|
||||
if( m_clearSignal )
|
||||
{
|
||||
@@ -357,7 +354,7 @@ const surroundSampleFrame * AudioEngine::renderNextBuffer()
|
||||
if( it != m_playHandles.end() )
|
||||
{
|
||||
( *it )->audioPort()->removePlayHandle( ( *it ) );
|
||||
if( ( *it )->type() == PlayHandle::TypeNotePlayHandle )
|
||||
if( ( *it )->type() == PlayHandle::Type::NotePlayHandle )
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*) *it );
|
||||
}
|
||||
@@ -387,9 +384,15 @@ const surroundSampleFrame * AudioEngine::renderNextBuffer()
|
||||
m_newPlayHandles.free( e );
|
||||
e = next;
|
||||
}
|
||||
}
|
||||
|
||||
// STAGE 1: run and render all play handles
|
||||
AudioEngineWorkerThread::fillJobQueue<PlayHandleList>( m_playHandles );
|
||||
|
||||
|
||||
void AudioEngine::renderStageInstruments()
|
||||
{
|
||||
AudioEngineProfiler::Probe profilerProbe(m_profiler, AudioEngineProfiler::DetailType::Instruments);
|
||||
|
||||
AudioEngineWorkerThread::fillJobQueue(m_playHandles);
|
||||
AudioEngineWorkerThread::startAndWaitForJobs();
|
||||
|
||||
// removed all play handles which are done
|
||||
@@ -405,7 +408,7 @@ const surroundSampleFrame * AudioEngine::renderNextBuffer()
|
||||
if( ( *it )->isFinished() )
|
||||
{
|
||||
( *it )->audioPort()->removePlayHandle( ( *it ) );
|
||||
if( ( *it )->type() == PlayHandle::TypeNotePlayHandle )
|
||||
if( ( *it )->type() == PlayHandle::Type::NotePlayHandle )
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*) *it );
|
||||
}
|
||||
@@ -417,16 +420,28 @@ const surroundSampleFrame * AudioEngine::renderNextBuffer()
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AudioEngine::renderStageEffects()
|
||||
{
|
||||
AudioEngineProfiler::Probe profilerProbe(m_profiler, AudioEngineProfiler::DetailType::Effects);
|
||||
|
||||
// STAGE 2: process effects of all instrument- and sampletracks
|
||||
AudioEngineWorkerThread::fillJobQueue<QVector<AudioPort *> >( m_audioPorts );
|
||||
AudioEngineWorkerThread::fillJobQueue(m_audioPorts);
|
||||
AudioEngineWorkerThread::startAndWaitForJobs();
|
||||
}
|
||||
|
||||
|
||||
// STAGE 3: do master mix in mixer
|
||||
|
||||
void AudioEngine::renderStageMix()
|
||||
{
|
||||
AudioEngineProfiler::Probe profilerProbe(m_profiler, AudioEngineProfiler::DetailType::Mixing);
|
||||
|
||||
Mixer *mixer = Engine::mixer();
|
||||
mixer->masterMix(m_outputBufferWrite);
|
||||
|
||||
|
||||
emit nextAudioBuffer(m_outputBufferRead);
|
||||
|
||||
runChangesInModel();
|
||||
@@ -435,10 +450,22 @@ const surroundSampleFrame * AudioEngine::renderNextBuffer()
|
||||
EnvelopeAndLfoParameters::instances()->trigger();
|
||||
Controller::triggerFrameCounter();
|
||||
AutomatableModel::incrementPeriodCounter();
|
||||
}
|
||||
|
||||
|
||||
|
||||
const surroundSampleFrame *AudioEngine::renderNextBuffer()
|
||||
{
|
||||
m_profiler.startPeriod();
|
||||
s_renderingThread = true;
|
||||
|
||||
renderStageNoteSetup(); // STAGE 0: clear old play handles and buffers, setup new play handles
|
||||
renderStageInstruments(); // STAGE 1: run and render all play handles
|
||||
renderStageEffects(); // STAGE 2: process effects of all instrument- and sampletracks
|
||||
renderStageMix(); // STAGE 3: do master mix in mixer
|
||||
|
||||
s_renderingThread = false;
|
||||
|
||||
m_profiler.finishPeriod( processingSampleRate(), m_framesPerPeriod );
|
||||
m_profiler.finishPeriod(processingSampleRate(), m_framesPerPeriod);
|
||||
|
||||
return m_outputBufferRead;
|
||||
}
|
||||
@@ -464,12 +491,12 @@ void AudioEngine::handleMetronome()
|
||||
static tick_t lastMetroTicks = -1;
|
||||
|
||||
Song * song = Engine::getSong();
|
||||
Song::PlayModes currentPlayMode = song->playMode();
|
||||
Song::PlayMode currentPlayMode = song->playMode();
|
||||
|
||||
bool metronomeSupported =
|
||||
currentPlayMode == Song::Mode_PlayMidiClip
|
||||
|| currentPlayMode == Song::Mode_PlaySong
|
||||
|| currentPlayMode == Song::Mode_PlayPattern;
|
||||
currentPlayMode == Song::PlayMode::MidiClip
|
||||
|| currentPlayMode == Song::PlayMode::Song
|
||||
|| currentPlayMode == Song::PlayMode::Pattern;
|
||||
|
||||
if (!metronomeSupported || !m_metronomeActive || song->isExporting())
|
||||
{
|
||||
@@ -534,7 +561,7 @@ void AudioEngine::clearInternal()
|
||||
// TODO: m_midiClient->noteOffAll();
|
||||
for (auto ph : m_playHandles)
|
||||
{
|
||||
if (ph->type() != PlayHandle::TypeInstrumentPlayHandle)
|
||||
if (ph->type() != PlayHandle::Type::InstrumentPlayHandle)
|
||||
{
|
||||
m_playHandlesToRemove.push_back(ph);
|
||||
}
|
||||
@@ -551,8 +578,8 @@ AudioEngine::StereoSample AudioEngine::getPeakValues(sampleFrame * ab, const f_c
|
||||
|
||||
for (f_cnt_t f = 0; f < frames; ++f)
|
||||
{
|
||||
float const absLeft = qAbs(ab[f][0]);
|
||||
float const absRight = qAbs(ab[f][1]);
|
||||
float const absLeft = std::abs(ab[f][0]);
|
||||
float const absRight = std::abs(ab[f][1]);
|
||||
if (absLeft > peakLeft)
|
||||
{
|
||||
peakLeft = absLeft;
|
||||
@@ -663,7 +690,7 @@ void AudioEngine::removeAudioPort(AudioPort * port)
|
||||
{
|
||||
requestChangeInModel();
|
||||
|
||||
QVector<AudioPort *>::Iterator it = std::find(m_audioPorts.begin(), m_audioPorts.end(), port);
|
||||
auto it = std::find(m_audioPorts.begin(), m_audioPorts.end(), port);
|
||||
if (it != m_audioPorts.end())
|
||||
{
|
||||
m_audioPorts.erase(it);
|
||||
@@ -674,14 +701,17 @@ void AudioEngine::removeAudioPort(AudioPort * port)
|
||||
|
||||
bool AudioEngine::addPlayHandle( PlayHandle* handle )
|
||||
{
|
||||
if( criticalXRuns() == false )
|
||||
// Only add play handles if we have the CPU capacity to process them.
|
||||
// Instrument play handles are not added during playback, but when the
|
||||
// associated instrument is created, so add those unconditionally.
|
||||
if (handle->type() == PlayHandle::Type::InstrumentPlayHandle || !criticalXRuns())
|
||||
{
|
||||
m_newPlayHandles.push( handle );
|
||||
handle->audioPort()->addPlayHandle( handle );
|
||||
return true;
|
||||
}
|
||||
|
||||
if( handle->type() == PlayHandle::TypeNotePlayHandle )
|
||||
if( handle->type() == PlayHandle::Type::NotePlayHandle )
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*)handle );
|
||||
}
|
||||
@@ -732,7 +762,7 @@ void AudioEngine::removePlayHandle(PlayHandle * ph)
|
||||
// (See tobydox's 2008 commit 4583e48)
|
||||
if ( removedFromList )
|
||||
{
|
||||
if (ph->type() == PlayHandle::TypeNotePlayHandle)
|
||||
if (ph->type() == PlayHandle::Type::NotePlayHandle)
|
||||
{
|
||||
NotePlayHandleManager::release(dynamic_cast<NotePlayHandle*>(ph));
|
||||
}
|
||||
@@ -749,7 +779,7 @@ void AudioEngine::removePlayHandle(PlayHandle * ph)
|
||||
|
||||
|
||||
|
||||
void AudioEngine::removePlayHandlesOfTypes(Track * track, const quint8 types)
|
||||
void AudioEngine::removePlayHandlesOfTypes(Track * track, PlayHandle::Types types)
|
||||
{
|
||||
requestChangeInModel();
|
||||
PlayHandleList::Iterator it = m_playHandles.begin();
|
||||
@@ -758,7 +788,7 @@ void AudioEngine::removePlayHandlesOfTypes(Track * track, const quint8 types)
|
||||
if ((*it)->isFromTrack(track) && ((*it)->type() & types))
|
||||
{
|
||||
( *it )->audioPort()->removePlayHandle( ( *it ) );
|
||||
if( ( *it )->type() == PlayHandle::TypeNotePlayHandle )
|
||||
if( ( *it )->type() == PlayHandle::Type::NotePlayHandle )
|
||||
{
|
||||
NotePlayHandleManager::release( (NotePlayHandle*) *it );
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
#include "AudioEngineProfiler.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
@@ -38,10 +40,24 @@ AudioEngineProfiler::AudioEngineProfiler() :
|
||||
|
||||
void AudioEngineProfiler::finishPeriod( sample_rate_t sampleRate, fpp_t framesPerPeriod )
|
||||
{
|
||||
int periodElapsed = m_periodTimer.elapsed();
|
||||
// Time taken to process all data and fill the audio buffer.
|
||||
const unsigned int periodElapsed = m_periodTimer.elapsed();
|
||||
// Maximum time the processing can take before causing buffer underflow. Convert to us.
|
||||
const uint64_t timeLimit = static_cast<uint64_t>(1000000) * framesPerPeriod / sampleRate;
|
||||
|
||||
const float newCpuLoad = periodElapsed / 10000.0f * sampleRate / framesPerPeriod;
|
||||
m_cpuLoad = qBound<int>( 0, ( newCpuLoad * 0.1f + m_cpuLoad * 0.9f ), 100 );
|
||||
// Compute new overall CPU load and apply exponential averaging.
|
||||
// The result is used for overload detection in AudioEngine::criticalXRuns()
|
||||
// → the weight of a new sample must be high enough to allow relatively fast changes!
|
||||
const auto newCpuLoad = 100.f * periodElapsed / timeLimit;
|
||||
m_cpuLoad = newCpuLoad * 0.1f + m_cpuLoad * 0.9f;
|
||||
|
||||
// Compute detailed load analysis. Can use stronger averaging to get more stable readout.
|
||||
for (std::size_t i = 0; i < DetailCount; i++)
|
||||
{
|
||||
const auto newLoad = 100.f * m_detailTime[i] / timeLimit;
|
||||
const auto oldLoad = m_detailLoad[i].load(std::memory_order_relaxed);
|
||||
m_detailLoad[i].store(newLoad * 0.05f + oldLoad * 0.95f, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
if( m_outputFile.isOpen() )
|
||||
{
|
||||
|
||||
@@ -91,7 +91,7 @@ void AudioEngineWorkerThread::JobQueue::run()
|
||||
}
|
||||
}
|
||||
// always exit loop if we're not in dynamic mode
|
||||
processedJob = processedJob && ( m_opMode == Dynamic );
|
||||
processedJob = processedJob && ( m_opMode == OperationMode::Dynamic );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ AutomatableModel::AutomatableModel(
|
||||
const float val, const float min, const float max, const float step,
|
||||
Model* parent, const QString & displayName, bool defaultConstructed ) :
|
||||
Model( parent, displayName, defaultConstructed ),
|
||||
m_scaleType( Linear ),
|
||||
m_scaleType( ScaleType::Linear ),
|
||||
m_minValue( min ),
|
||||
m_maxValue( max ),
|
||||
m_step( step ),
|
||||
@@ -71,7 +71,7 @@ AutomatableModel::~AutomatableModel()
|
||||
{
|
||||
while( m_linkedModels.empty() == false )
|
||||
{
|
||||
m_linkedModels.last()->unlinkModel( this );
|
||||
m_linkedModels.back()->unlinkModel(this);
|
||||
m_linkedModels.erase( m_linkedModels.end() - 1 );
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, co
|
||||
{
|
||||
bool mustQuote = mustQuoteName(name);
|
||||
|
||||
if( isAutomated() || m_scaleType != Linear )
|
||||
if( isAutomated() || m_scaleType != ScaleType::Linear )
|
||||
{
|
||||
// automation needs tuple of data (name, id, value)
|
||||
// scale type also needs an extra value
|
||||
@@ -114,7 +114,7 @@ void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, co
|
||||
QDomElement me = doc.createElement( mustQuote ? QString("automatablemodel") : name );
|
||||
me.setAttribute( "id", ProjectJournal::idToSave( id() ) );
|
||||
me.setAttribute( "value", m_value );
|
||||
me.setAttribute( "scale_type", m_scaleType == Logarithmic ? "log" : "linear" );
|
||||
me.setAttribute( "scale_type", m_scaleType == ScaleType::Logarithmic ? "log" : "linear" );
|
||||
if(mustQuote) {
|
||||
me.setAttribute( "nodename", name );
|
||||
}
|
||||
@@ -140,11 +140,11 @@ void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, co
|
||||
// the discardMIDIConnections option is true.
|
||||
auto controllerType = m_controllerConnection
|
||||
? m_controllerConnection->getController()->type()
|
||||
: Controller::DummyController;
|
||||
: Controller::ControllerType::Dummy;
|
||||
bool skipMidiController = Engine::getSong()->isSavingProject()
|
||||
&& Engine::getSong()->getSaveOptions().discardMIDIConnections.value();
|
||||
if (m_controllerConnection && controllerType != Controller::DummyController
|
||||
&& !(skipMidiController && controllerType == Controller::MidiController))
|
||||
if (m_controllerConnection && controllerType != Controller::ControllerType::Dummy
|
||||
&& !(skipMidiController && controllerType == Controller::ControllerType::Midi))
|
||||
{
|
||||
QDomElement controllerElement;
|
||||
|
||||
@@ -264,18 +264,18 @@ void AutomatableModel::loadSettings( const QDomElement& element, const QString&
|
||||
{
|
||||
if( nodeElement.attribute( "scale_type" ) == "linear" )
|
||||
{
|
||||
setScaleType( Linear );
|
||||
setScaleType( ScaleType::Linear );
|
||||
}
|
||||
else if( nodeElement.attribute( "scale_type" ) == "log" )
|
||||
{
|
||||
setScaleType( Logarithmic );
|
||||
setScaleType( ScaleType::Logarithmic );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
setScaleType( Linear );
|
||||
setScaleType( ScaleType::Linear );
|
||||
|
||||
if( element.hasAttribute( name ) )
|
||||
// attribute => read the element's value from the attribute list
|
||||
@@ -335,7 +335,7 @@ template<class T> T AutomatableModel::logToLinearScale( T value ) const
|
||||
|
||||
float AutomatableModel::scaledValue( float value ) const
|
||||
{
|
||||
return m_scaleType == Linear
|
||||
return m_scaleType == ScaleType::Linear
|
||||
? value
|
||||
: logToLinearScale<float>( ( value - minValue<float>() ) / m_range );
|
||||
}
|
||||
@@ -343,7 +343,7 @@ float AutomatableModel::scaledValue( float value ) const
|
||||
|
||||
float AutomatableModel::inverseScaledValue( float value ) const
|
||||
{
|
||||
return m_scaleType == Linear
|
||||
return m_scaleType == ScaleType::Linear
|
||||
? value
|
||||
: lmms::linearToLogScale( minValue<float>(), maxValue<float>(), value );
|
||||
}
|
||||
@@ -354,8 +354,8 @@ float AutomatableModel::inverseScaledValue( float value ) const
|
||||
template<class T>
|
||||
void roundAt( T& value, const T& where, const T& step_size )
|
||||
{
|
||||
if( qAbs<float>( value - where )
|
||||
< typeInfo<float>::minEps() * qAbs<float>( step_size ) )
|
||||
if (std::abs(value - where)
|
||||
< typeInfo<float>::minEps() * std::abs(step_size))
|
||||
{
|
||||
value = where;
|
||||
}
|
||||
@@ -444,7 +444,7 @@ void AutomatableModel::setStep( const float step )
|
||||
|
||||
float AutomatableModel::fittedValue( float value ) const
|
||||
{
|
||||
value = qBound<float>( m_minValue, value, m_maxValue );
|
||||
value = std::clamp(value, m_minValue, m_maxValue);
|
||||
|
||||
if( m_step != 0 && m_hasStrictStepSize )
|
||||
{
|
||||
@@ -473,7 +473,8 @@ float AutomatableModel::fittedValue( float value ) const
|
||||
|
||||
void AutomatableModel::linkModel( AutomatableModel* model )
|
||||
{
|
||||
if( !m_linkedModels.contains( model ) && model != this )
|
||||
auto containsModel = std::find(m_linkedModels.begin(), m_linkedModels.end(), model) != m_linkedModels.end();
|
||||
if (!containsModel && model != this)
|
||||
{
|
||||
m_linkedModels.push_back( model );
|
||||
|
||||
@@ -490,7 +491,7 @@ void AutomatableModel::linkModel( AutomatableModel* model )
|
||||
|
||||
void AutomatableModel::unlinkModel( AutomatableModel* model )
|
||||
{
|
||||
AutoModelVector::Iterator it = std::find( m_linkedModels.begin(), m_linkedModels.end(), model );
|
||||
auto it = std::find(m_linkedModels.begin(), m_linkedModels.end(), model);
|
||||
if( it != m_linkedModels.end() )
|
||||
{
|
||||
m_linkedModels.erase( it );
|
||||
@@ -504,7 +505,8 @@ void AutomatableModel::unlinkModel( AutomatableModel* model )
|
||||
|
||||
void AutomatableModel::linkModels( AutomatableModel* model1, AutomatableModel* model2 )
|
||||
{
|
||||
if (!model1->m_linkedModels.contains( model2 ) && model1 != model2)
|
||||
auto model1ContainsModel2 = std::find(model1->m_linkedModels.begin(), model1->m_linkedModels.end(), model2) != model1->m_linkedModels.end();
|
||||
if (!model1ContainsModel2 && model1 != model2)
|
||||
{
|
||||
// copy data
|
||||
model1->m_value = model2->m_value;
|
||||
@@ -569,10 +571,10 @@ float AutomatableModel::controllerValue( int frameOffset ) const
|
||||
float v = 0;
|
||||
switch(m_scaleType)
|
||||
{
|
||||
case Linear:
|
||||
case ScaleType::Linear:
|
||||
v = minValue<float>() + ( range() * controllerConnection()->currentValue( frameOffset ) );
|
||||
break;
|
||||
case Logarithmic:
|
||||
case ScaleType::Logarithmic:
|
||||
v = logToLinearScale(
|
||||
controllerConnection()->currentValue( frameOffset ));
|
||||
break;
|
||||
@@ -583,12 +585,12 @@ float AutomatableModel::controllerValue( int frameOffset ) const
|
||||
}
|
||||
if( typeInfo<float>::isEqual( m_step, 1 ) && m_hasStrictStepSize )
|
||||
{
|
||||
return qRound( v );
|
||||
return std::round(v);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
AutomatableModel* lm = m_linkedModels.first();
|
||||
AutomatableModel* lm = m_linkedModels.front();
|
||||
if (lm->controllerConnection() && lm->useControllerValue())
|
||||
{
|
||||
return fittedValue( lm->controllerValue( frameOffset ) );
|
||||
@@ -621,13 +623,13 @@ ValueBuffer * AutomatableModel::valueBuffer()
|
||||
float * nvalues = m_valueBuffer.values();
|
||||
switch( m_scaleType )
|
||||
{
|
||||
case Linear:
|
||||
case ScaleType::Linear:
|
||||
for( int i = 0; i < m_valueBuffer.length(); i++ )
|
||||
{
|
||||
nvalues[i] = minValue<float>() + ( range() * values[i] );
|
||||
}
|
||||
break;
|
||||
case Logarithmic:
|
||||
case ScaleType::Logarithmic:
|
||||
for( int i = 0; i < m_valueBuffer.length(); i++ )
|
||||
{
|
||||
nvalues[i] = logToLinearScale( values[i] );
|
||||
@@ -649,7 +651,7 @@ ValueBuffer * AutomatableModel::valueBuffer()
|
||||
AutomatableModel* lm = nullptr;
|
||||
if (hasLinkedModels())
|
||||
{
|
||||
lm = m_linkedModels.first();
|
||||
lm = m_linkedModels.front();
|
||||
}
|
||||
if (lm && lm->controllerConnection() && lm->useControllerValue() &&
|
||||
lm->controllerConnection()->getController()->isSampleExact())
|
||||
@@ -721,8 +723,8 @@ void AutomatableModel::reset()
|
||||
float AutomatableModel::globalAutomationValueAt( const TimePos& time )
|
||||
{
|
||||
// get clips that connect to this model
|
||||
QVector<AutomationClip *> clips = AutomationClip::clipsForModel( this );
|
||||
if( clips.isEmpty() )
|
||||
auto clips = AutomationClip::clipsForModel(this);
|
||||
if (clips.empty())
|
||||
{
|
||||
// if no such clips exist, return current value
|
||||
return m_value;
|
||||
@@ -731,17 +733,17 @@ float AutomatableModel::globalAutomationValueAt( const TimePos& time )
|
||||
{
|
||||
// of those clips:
|
||||
// find the clips which overlap with the time position
|
||||
QVector<AutomationClip *> clipsInRange;
|
||||
std::vector<AutomationClip*> clipsInRange;
|
||||
for (const auto& clip : clips)
|
||||
{
|
||||
int s = clip->startPosition();
|
||||
int e = clip->endPosition();
|
||||
if (s <= time && e >= time) { clipsInRange += clip; }
|
||||
if (s <= time && e >= time) { clipsInRange.push_back(clip); }
|
||||
}
|
||||
|
||||
AutomationClip * latestClip = nullptr;
|
||||
|
||||
if( ! clipsInRange.isEmpty() )
|
||||
if (!clipsInRange.empty())
|
||||
{
|
||||
// if there are more than one overlapping clips, just use the first one because
|
||||
// multiple clip behaviour is undefined anyway
|
||||
@@ -792,7 +794,7 @@ void AutomatableModel::setUseControllerValue(bool b)
|
||||
|
||||
float FloatModel::getRoundedValue() const
|
||||
{
|
||||
return qRound( value() / step<float>() ) * step<float>();
|
||||
return std::round(value() / step<float>()) * step<float>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ AutomationClip::AutomationClip( AutomationTrack * _auto_track ) :
|
||||
m_autoTrack( _auto_track ),
|
||||
m_objects(),
|
||||
m_tension( 1.0 ),
|
||||
m_progressionType( DiscreteProgression ),
|
||||
m_progressionType( ProgressionType::Discrete ),
|
||||
m_dragging( false ),
|
||||
m_isRecording( false ),
|
||||
m_lastRecordedValue( 0 )
|
||||
@@ -63,11 +63,11 @@ AutomationClip::AutomationClip( AutomationTrack * _auto_track ) :
|
||||
{
|
||||
switch( getTrack()->trackContainer()->type() )
|
||||
{
|
||||
case TrackContainer::PatternContainer:
|
||||
case TrackContainer::Type::Pattern:
|
||||
setAutoResize( true );
|
||||
break;
|
||||
|
||||
case TrackContainer::SongContainer:
|
||||
case TrackContainer::Type::Song:
|
||||
// move down
|
||||
default:
|
||||
setAutoResize( false );
|
||||
@@ -104,11 +104,11 @@ AutomationClip::AutomationClip( const AutomationClip & _clip_to_copy ) :
|
||||
if (!getTrack()){ return; }
|
||||
switch( getTrack()->trackContainer()->type() )
|
||||
{
|
||||
case TrackContainer::PatternContainer:
|
||||
case TrackContainer::Type::Pattern:
|
||||
setAutoResize( true );
|
||||
break;
|
||||
|
||||
case TrackContainer::SongContainer:
|
||||
case TrackContainer::Type::Song:
|
||||
// move down
|
||||
default:
|
||||
setAutoResize( false );
|
||||
@@ -120,19 +120,19 @@ bool AutomationClip::addObject( AutomatableModel * _obj, bool _search_dup )
|
||||
{
|
||||
QMutexLocker m(&m_clipMutex);
|
||||
|
||||
if( _search_dup && m_objects.contains(_obj) )
|
||||
if (_search_dup && std::find(m_objects.begin(), m_objects.end(), _obj) != m_objects.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// the automation track is unconnected and there is nothing in the track
|
||||
if( m_objects.isEmpty() && hasAutomation() == false )
|
||||
if (m_objects.empty() && hasAutomation() == false)
|
||||
{
|
||||
// then initialize first value
|
||||
putValue( TimePos(0), _obj->inverseScaledValue( _obj->value<float>() ), false );
|
||||
}
|
||||
|
||||
m_objects += _obj;
|
||||
m_objects.push_back(_obj);
|
||||
|
||||
connect( _obj, SIGNAL(destroyed(lmms::jo_id_t)),
|
||||
this, SLOT(objectDestroyed(lmms::jo_id_t)),
|
||||
@@ -147,13 +147,13 @@ bool AutomationClip::addObject( AutomatableModel * _obj, bool _search_dup )
|
||||
|
||||
|
||||
void AutomationClip::setProgressionType(
|
||||
ProgressionTypes _new_progression_type )
|
||||
ProgressionType _new_progression_type )
|
||||
{
|
||||
QMutexLocker m(&m_clipMutex);
|
||||
|
||||
if ( _new_progression_type == DiscreteProgression ||
|
||||
_new_progression_type == LinearProgression ||
|
||||
_new_progression_type == CubicHermiteProgression )
|
||||
if ( _new_progression_type == ProgressionType::Discrete ||
|
||||
_new_progression_type == ProgressionType::Linear ||
|
||||
_new_progression_type == ProgressionType::CubicHermite )
|
||||
{
|
||||
m_progressionType = _new_progression_type;
|
||||
emit dataChanged();
|
||||
@@ -184,7 +184,7 @@ const AutomatableModel * AutomationClip::firstObject() const
|
||||
QMutexLocker m(&m_clipMutex);
|
||||
|
||||
AutomatableModel* model;
|
||||
if (!m_objects.isEmpty() && (model = m_objects.first()) != nullptr)
|
||||
if (!m_objects.empty() && (model = m_objects.front()) != nullptr)
|
||||
{
|
||||
return model;
|
||||
}
|
||||
@@ -225,7 +225,7 @@ TimePos AutomationClip::timeMapLength() const
|
||||
void AutomationClip::updateLength()
|
||||
{
|
||||
// Do not resize down in case user manually extended up
|
||||
changeLength(qMax(length(), timeMapLength()));
|
||||
changeLength(std::max(length(), timeMapLength()));
|
||||
}
|
||||
|
||||
|
||||
@@ -374,17 +374,17 @@ void AutomationClip::removeNodes(const int tick0, const int tick1)
|
||||
return;
|
||||
}
|
||||
|
||||
auto start = TimePos(qMin(tick0, tick1));
|
||||
auto end = TimePos(qMax(tick0, tick1));
|
||||
auto start = TimePos(std::min(tick0, tick1));
|
||||
auto end = TimePos(std::max(tick0, tick1));
|
||||
|
||||
// Make a list of TimePos with nodes to be removed
|
||||
// because we can't simply remove the nodes from
|
||||
// the timeMap while we are iterating it.
|
||||
QVector<TimePos> nodesToRemove;
|
||||
std::vector<TimePos> nodesToRemove;
|
||||
|
||||
for (auto it = m_timeMap.lowerBound(start), endIt = m_timeMap.upperBound(end); it != endIt; ++it)
|
||||
{
|
||||
nodesToRemove.append(POS(it));
|
||||
nodesToRemove.push_back(POS(it));
|
||||
}
|
||||
|
||||
for (auto node: nodesToRemove)
|
||||
@@ -410,8 +410,8 @@ void AutomationClip::resetNodes(const int tick0, const int tick1)
|
||||
return;
|
||||
}
|
||||
|
||||
auto start = TimePos(qMin(tick0, tick1));
|
||||
auto end = TimePos(qMax(tick0, tick1));
|
||||
auto start = TimePos(std::min(tick0, tick1));
|
||||
auto end = TimePos(std::max(tick0, tick1));
|
||||
|
||||
for (auto it = m_timeMap.lowerBound(start), endIt = m_timeMap.upperBound(end); it != endIt; ++it)
|
||||
{
|
||||
@@ -560,11 +560,11 @@ float AutomationClip::valueAt( timeMap::const_iterator v, int offset ) const
|
||||
// value if we do
|
||||
if (offset == 0) { return INVAL(v); }
|
||||
|
||||
if (m_progressionType == DiscreteProgression)
|
||||
if (m_progressionType == ProgressionType::Discrete)
|
||||
{
|
||||
return OUTVAL(v);
|
||||
}
|
||||
else if( m_progressionType == LinearProgression )
|
||||
else if( m_progressionType == ProgressionType::Linear )
|
||||
{
|
||||
float slope =
|
||||
(INVAL(v + 1) - OUTVAL(v))
|
||||
@@ -572,7 +572,7 @@ float AutomationClip::valueAt( timeMap::const_iterator v, int offset ) const
|
||||
|
||||
return OUTVAL(v) + offset * slope;
|
||||
}
|
||||
else /* CubicHermiteProgression */
|
||||
else /* ProgressionType::CubicHermite */
|
||||
{
|
||||
// Implements a Cubic Hermite spline as explained at:
|
||||
// http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Unit_interval_.280.2C_1.29
|
||||
@@ -767,7 +767,7 @@ void AutomationClip::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
_this.setAttribute( "pos", startPosition() );
|
||||
_this.setAttribute( "len", length() );
|
||||
_this.setAttribute( "name", name() );
|
||||
_this.setAttribute( "prog", QString::number( progressionType() ) );
|
||||
_this.setAttribute( "prog", QString::number( static_cast<int>(progressionType()) ) );
|
||||
_this.setAttribute( "tens", QString::number( getTension() ) );
|
||||
_this.setAttribute( "mute", QString::number( isMuted() ) );
|
||||
|
||||
@@ -808,7 +808,7 @@ void AutomationClip::loadSettings( const QDomElement & _this )
|
||||
|
||||
movePosition( _this.attribute( "pos" ).toInt() );
|
||||
setName( _this.attribute( "name" ) );
|
||||
setProgressionType( static_cast<ProgressionTypes>( _this.attribute(
|
||||
setProgressionType( static_cast<ProgressionType>( _this.attribute(
|
||||
"prog" ).toInt() ) );
|
||||
setTension( _this.attribute( "tens" ) );
|
||||
setMuted(_this.attribute( "mute", QString::number( false ) ).toInt() );
|
||||
@@ -831,7 +831,7 @@ void AutomationClip::loadSettings( const QDomElement & _this )
|
||||
}
|
||||
else if( element.tagName() == "object" )
|
||||
{
|
||||
m_idsToResolve << element.attribute( "id" ).toInt();
|
||||
m_idsToResolve.push_back(element.attribute("id").toInt());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -865,9 +865,9 @@ QString AutomationClip::name() const
|
||||
{
|
||||
return Clip::name();
|
||||
}
|
||||
if( !m_objects.isEmpty() && m_objects.first() != nullptr )
|
||||
if (!m_objects.empty() && m_objects.front() != nullptr)
|
||||
{
|
||||
return m_objects.first()->fullDisplayName();
|
||||
return m_objects.front()->fullDisplayName();
|
||||
}
|
||||
return tr( "Drag a control while pressing <%1>" ).arg(UI_CTRL_KEY);
|
||||
}
|
||||
@@ -888,14 +888,10 @@ gui::ClipView * AutomationClip::createView( gui::TrackView * _tv )
|
||||
|
||||
bool AutomationClip::isAutomated( const AutomatableModel * _m )
|
||||
{
|
||||
TrackContainer::TrackList l;
|
||||
l += Engine::getSong()->tracks();
|
||||
l += Engine::patternStore()->tracks();
|
||||
l += Engine::getSong()->globalAutomationTrack();
|
||||
|
||||
for (const auto& track : l)
|
||||
auto l = combineAllTracks();
|
||||
for (const auto track : l)
|
||||
{
|
||||
if (track->type() == Track::AutomationTrack || track->type() == Track::HiddenAutomationTrack)
|
||||
if (track->type() == Track::Type::Automation || track->type() == Track::Type::HiddenAutomation)
|
||||
{
|
||||
for (const auto& clip : track->getClips())
|
||||
{
|
||||
@@ -921,19 +917,16 @@ bool AutomationClip::isAutomated( const AutomatableModel * _m )
|
||||
* @brief returns a list of all the automation clips that are connected to a specific model
|
||||
* @param _m the model we want to look for
|
||||
*/
|
||||
QVector<AutomationClip *> AutomationClip::clipsForModel( const AutomatableModel * _m )
|
||||
std::vector<AutomationClip *> AutomationClip::clipsForModel(const AutomatableModel* _m)
|
||||
{
|
||||
QVector<AutomationClip*> clips;
|
||||
TrackContainer::TrackList tracks;
|
||||
tracks += Engine::getSong()->tracks();
|
||||
tracks += Engine::patternStore()->tracks();
|
||||
tracks += Engine::getSong()->globalAutomationTrack();
|
||||
std::vector<AutomationClip *> clips;
|
||||
auto l = combineAllTracks();
|
||||
|
||||
// go through all tracks...
|
||||
for (const auto& track : tracks)
|
||||
for (const auto track : l)
|
||||
{
|
||||
// we want only automation tracks...
|
||||
if (track->type() == Track::AutomationTrack || track->type() == Track::HiddenAutomationTrack )
|
||||
if (track->type() == Track::Type::Automation || track->type() == Track::Type::HiddenAutomation )
|
||||
{
|
||||
// go through all the clips...
|
||||
for (const auto& trackClip : track->getClips())
|
||||
@@ -953,7 +946,7 @@ QVector<AutomationClip *> AutomationClip::clipsForModel( const AutomatableModel
|
||||
}
|
||||
}
|
||||
// if the clips is connected to the model, add it to the list
|
||||
if( has_object ) { clips += a; }
|
||||
if (has_object) { clips.push_back(a); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -989,12 +982,10 @@ AutomationClip * AutomationClip::globalAutomationClip(
|
||||
|
||||
void AutomationClip::resolveAllIDs()
|
||||
{
|
||||
TrackContainer::TrackList l = Engine::getSong()->tracks() +
|
||||
Engine::patternStore()->tracks();
|
||||
l += Engine::getSong()->globalAutomationTrack();
|
||||
auto l = combineAllTracks();
|
||||
for (const auto& track : l)
|
||||
{
|
||||
if (track->type() == Track::AutomationTrack || track->type() == Track::HiddenAutomationTrack)
|
||||
if (track->type() == Track::Type::Automation || track->type() == Track::Type::HiddenAutomation)
|
||||
{
|
||||
for (const auto& clip : track->getClips())
|
||||
{
|
||||
@@ -1060,10 +1051,9 @@ void AutomationClip::objectDestroyed( jo_id_t _id )
|
||||
// when switching samplerate) and real deletions because in the latter
|
||||
// case we had to remove ourselves if we're the global automation
|
||||
// clip of the destroyed object
|
||||
m_idsToResolve += _id;
|
||||
m_idsToResolve.push_back(_id);
|
||||
|
||||
for( objectVector::Iterator objIt = m_objects.begin();
|
||||
objIt != m_objects.end(); objIt++ )
|
||||
for (auto objIt = m_objects.begin(); objIt != m_objects.end(); objIt++)
|
||||
{
|
||||
Q_ASSERT( !(*objIt).isNull() );
|
||||
if( (*objIt)->id() == _id )
|
||||
@@ -1116,16 +1106,16 @@ void AutomationClip::generateTangents(timeMap::iterator it, int numToGenerate)
|
||||
{
|
||||
QMutexLocker m(&m_clipMutex);
|
||||
|
||||
if( m_timeMap.size() < 2 && numToGenerate > 0 )
|
||||
for (int i = 0; i < numToGenerate && it != m_timeMap.end(); ++i, ++it)
|
||||
{
|
||||
it.value().setInTangent(0);
|
||||
it.value().setOutTangent(0);
|
||||
return;
|
||||
}
|
||||
|
||||
for( int i = 0; i < numToGenerate; i++ )
|
||||
{
|
||||
if( it == m_timeMap.begin() )
|
||||
if (it + 1 == m_timeMap.end())
|
||||
{
|
||||
// Previously, the last value's tangent was always set to 0. That logic was kept for both tangents
|
||||
// of the last node
|
||||
it.value().setInTangent(0);
|
||||
it.value().setOutTangent(0);
|
||||
}
|
||||
else if (it == m_timeMap.begin())
|
||||
{
|
||||
// On the first node there's no curve behind it, so we will only calculate the outTangent
|
||||
// and inTangent will be set to 0.
|
||||
@@ -1133,14 +1123,6 @@ void AutomationClip::generateTangents(timeMap::iterator it, int numToGenerate)
|
||||
it.value().setInTangent(0);
|
||||
it.value().setOutTangent(tangent);
|
||||
}
|
||||
else if( it+1 == m_timeMap.end() )
|
||||
{
|
||||
// Previously, the last value's tangent was always set to 0. That logic was kept for both tangents
|
||||
// of the last node
|
||||
it.value().setInTangent(0);
|
||||
it.value().setOutTangent(0);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// When we are in a node that is in the middle of two other nodes, we need to check if we
|
||||
@@ -1169,8 +1151,21 @@ void AutomationClip::generateTangents(timeMap::iterator it, int numToGenerate)
|
||||
it.value().setOutTangent(outTangent);
|
||||
}
|
||||
}
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Track*> AutomationClip::combineAllTracks()
|
||||
{
|
||||
std::vector<Track*> combinedTrackList;
|
||||
|
||||
auto& songTracks = Engine::getSong()->tracks();
|
||||
auto& patternStoreTracks = Engine::patternStore()->tracks();
|
||||
|
||||
combinedTrackList.insert(combinedTrackList.end(), songTracks.begin(), songTracks.end());
|
||||
combinedTrackList.insert(combinedTrackList.end(), patternStoreTracks.begin(), patternStoreTracks.end());
|
||||
combinedTrackList.push_back(Engine::getSong()->globalAutomationTrack());
|
||||
|
||||
return combinedTrackList;
|
||||
}
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
std::array<WaveMipMap, BandLimitedWave::Waveforms::NumBLWaveforms> BandLimitedWave::s_waveforms = { };
|
||||
std::array<WaveMipMap, BandLimitedWave::NumWaveforms> BandLimitedWave::s_waveforms = { };
|
||||
bool BandLimitedWave::s_wavesGenerated = false;
|
||||
QString BandLimitedWave::s_wavetableDir = "";
|
||||
|
||||
@@ -84,7 +84,7 @@ void BandLimitedWave::generateWaves()
|
||||
{
|
||||
saw_file.open( QIODevice::ReadOnly );
|
||||
QDataStream in( &saw_file );
|
||||
in >> s_waveforms[ BandLimitedWave::BLSaw ];
|
||||
in >> s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLSaw)];
|
||||
saw_file.close();
|
||||
}
|
||||
else
|
||||
@@ -108,14 +108,14 @@ void BandLimitedWave::generateWaves()
|
||||
s += amp * /*a2 **/sin( static_cast<double>( ph * harm ) / static_cast<double>( len ) * F_2PI );
|
||||
harm++;
|
||||
} while( hlen > 2.0 );
|
||||
s_waveforms[ BandLimitedWave::BLSaw ].setSampleAt( i, ph, s );
|
||||
max = qMax( max, qAbs( s ) );
|
||||
s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLSaw)].setSampleAt( i, ph, s );
|
||||
max = std::max(max, std::abs(s));
|
||||
}
|
||||
// normalize
|
||||
for( int ph = 0; ph < len; ph++ )
|
||||
{
|
||||
sample_t s = s_waveforms[ BandLimitedWave::BLSaw ].sampleAt( i, ph ) / max;
|
||||
s_waveforms[ BandLimitedWave::BLSaw ].setSampleAt( i, ph, s );
|
||||
sample_t s = s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLSaw)].sampleAt( i, ph ) / max;
|
||||
s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLSaw)].setSampleAt( i, ph, s );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,7 @@ void BandLimitedWave::generateWaves()
|
||||
{
|
||||
sqr_file.open( QIODevice::ReadOnly );
|
||||
QDataStream in( &sqr_file );
|
||||
in >> s_waveforms[ BandLimitedWave::BLSquare ];
|
||||
in >> s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLSquare)];
|
||||
sqr_file.close();
|
||||
}
|
||||
else
|
||||
@@ -150,14 +150,14 @@ void BandLimitedWave::generateWaves()
|
||||
s += amp * /*a2 **/ sin( static_cast<double>( ph * harm ) / static_cast<double>( len ) * F_2PI );
|
||||
harm += 2;
|
||||
} while( hlen > 2.0 );
|
||||
s_waveforms[ BandLimitedWave::BLSquare ].setSampleAt( i, ph, s );
|
||||
max = qMax( max, qAbs( s ) );
|
||||
s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLSquare)].setSampleAt( i, ph, s );
|
||||
max = std::max(max, std::abs(s));
|
||||
}
|
||||
// normalize
|
||||
for( int ph = 0; ph < len; ph++ )
|
||||
{
|
||||
sample_t s = s_waveforms[ BandLimitedWave::BLSquare ].sampleAt( i, ph ) / max;
|
||||
s_waveforms[ BandLimitedWave::BLSquare ].setSampleAt( i, ph, s );
|
||||
sample_t s = s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLSquare)].sampleAt( i, ph ) / max;
|
||||
s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLSquare)].setSampleAt( i, ph, s );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,7 +167,7 @@ void BandLimitedWave::generateWaves()
|
||||
{
|
||||
tri_file.open( QIODevice::ReadOnly );
|
||||
QDataStream in( &tri_file );
|
||||
in >> s_waveforms[ BandLimitedWave::BLTriangle ];
|
||||
in >> s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLTriangle)];
|
||||
tri_file.close();
|
||||
}
|
||||
else
|
||||
@@ -192,14 +192,14 @@ void BandLimitedWave::generateWaves()
|
||||
( ( harm + 1 ) % 4 == 0 ? 0.5 : 0.0 ) ) * F_2PI );
|
||||
harm += 2;
|
||||
} while( hlen > 2.0 );
|
||||
s_waveforms[ BandLimitedWave::BLTriangle ].setSampleAt( i, ph, s );
|
||||
max = qMax( max, qAbs( s ) );
|
||||
s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLTriangle)].setSampleAt( i, ph, s );
|
||||
max = std::max(max, std::abs(s));
|
||||
}
|
||||
// normalize
|
||||
for( int ph = 0; ph < len; ph++ )
|
||||
{
|
||||
sample_t s = s_waveforms[ BandLimitedWave::BLTriangle ].sampleAt( i, ph ) / max;
|
||||
s_waveforms[ BandLimitedWave::BLTriangle ].setSampleAt( i, ph, s );
|
||||
sample_t s = s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLTriangle)].sampleAt( i, ph ) / max;
|
||||
s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLTriangle)].setSampleAt( i, ph, s );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,7 +210,7 @@ void BandLimitedWave::generateWaves()
|
||||
{
|
||||
moog_file.open( QIODevice::ReadOnly );
|
||||
QDataStream in( &moog_file );
|
||||
in >> s_waveforms[ BandLimitedWave::BLMoog ];
|
||||
in >> s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLMoog)];
|
||||
moog_file.close();
|
||||
}
|
||||
else
|
||||
@@ -222,9 +222,9 @@ void BandLimitedWave::generateWaves()
|
||||
for( int ph = 0; ph < len; ph++ )
|
||||
{
|
||||
const int sawph = ( ph + static_cast<int>( len * 0.75 ) ) % len;
|
||||
const sample_t saw = s_waveforms[ BandLimitedWave::BLSaw ].sampleAt( i, sawph );
|
||||
const sample_t tri = s_waveforms[ BandLimitedWave::BLTriangle ].sampleAt( i, ph );
|
||||
s_waveforms[ BandLimitedWave::BLMoog ].setSampleAt( i, ph, ( saw + tri ) * 0.5f );
|
||||
const sample_t saw = s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLSaw)].sampleAt( i, sawph );
|
||||
const sample_t tri = s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLTriangle)].sampleAt( i, ph );
|
||||
s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLMoog)].setSampleAt( i, ph, ( saw + tri ) * 0.5f );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -252,22 +252,22 @@ QFile moogfile( "path-to-wavetables/moog.bin" );
|
||||
|
||||
sawfile.open( QIODevice::WriteOnly );
|
||||
QDataStream sawout( &sawfile );
|
||||
sawout << s_waveforms[ BandLimitedWave::BLSaw ];
|
||||
sawout << s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLSaw)];
|
||||
sawfile.close();
|
||||
|
||||
sqrfile.open( QIODevice::WriteOnly );
|
||||
QDataStream sqrout( &sqrfile );
|
||||
sqrout << s_waveforms[ BandLimitedWave::BLSquare ];
|
||||
sqrout << s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLSquare)];
|
||||
sqrfile.close();
|
||||
|
||||
trifile.open( QIODevice::WriteOnly );
|
||||
QDataStream triout( &trifile );
|
||||
triout << s_waveforms[ BandLimitedWave::BLTriangle ];
|
||||
triout << s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLTriangle)];
|
||||
trifile.close();
|
||||
|
||||
moogfile.open( QIODevice::WriteOnly );
|
||||
QDataStream moogout( &moogfile );
|
||||
moogout << s_waveforms[ BandLimitedWave::BLMoog ];
|
||||
moogout << s_waveforms[static_cast<std::size_t>(BandLimitedWave::Waveform::BLMoog)];
|
||||
moogfile.close();
|
||||
|
||||
*/
|
||||
|
||||
@@ -70,6 +70,7 @@ set(LMMS_SRCS
|
||||
core/SamplePlayHandle.cpp
|
||||
core/SampleRecordHandle.cpp
|
||||
core/Scale.cpp
|
||||
core/LmmsSemaphore.cpp
|
||||
core/SerializingObject.cpp
|
||||
core/Song.cpp
|
||||
core/TempoSyncKnobModel.cpp
|
||||
@@ -77,6 +78,8 @@ set(LMMS_SRCS
|
||||
core/ToolPlugin.cpp
|
||||
core/Track.cpp
|
||||
core/TrackContainer.cpp
|
||||
core/UpgradeExtendedNoteRange.h
|
||||
core/UpgradeExtendedNoteRange.cpp
|
||||
core/Clip.cpp
|
||||
core/ValueBuffer.cpp
|
||||
core/VstSyncController.cpp
|
||||
@@ -110,6 +113,7 @@ set(LMMS_SRCS
|
||||
core/lv2/Lv2SubPluginFeatures.cpp
|
||||
core/lv2/Lv2UridCache.cpp
|
||||
core/lv2/Lv2UridMap.cpp
|
||||
core/lv2/Lv2Worker.cpp
|
||||
|
||||
core/midi/MidiAlsaRaw.cpp
|
||||
core/midi/MidiAlsaSeq.cpp
|
||||
|
||||
@@ -92,7 +92,7 @@ Clip::~Clip()
|
||||
*/
|
||||
void Clip::movePosition( const TimePos & pos )
|
||||
{
|
||||
TimePos newPos = qMax(0, pos.getTicks());
|
||||
TimePos newPos = std::max(0, pos.getTicks());
|
||||
if (m_startPosition != newPos)
|
||||
{
|
||||
Engine::audioEngine()->requestChangeInModel();
|
||||
|
||||
@@ -77,9 +77,9 @@ ConfigManager::ConfigManager() :
|
||||
m_sf2Dir = m_workingDir + SF2_PATH;
|
||||
m_gigDir = m_workingDir + GIG_PATH;
|
||||
m_themeDir = defaultThemeDir();
|
||||
if (!qgetenv("LMMS_DATA_DIR").isEmpty())
|
||||
if (std::getenv("LMMS_DATA_DIR"))
|
||||
{
|
||||
QDir::addSearchPath("data", QString::fromLocal8Bit(qgetenv("LMMS_DATA_DIR")));
|
||||
QDir::addSearchPath("data", QString::fromLocal8Bit(std::getenv("LMMS_DATA_DIR")));
|
||||
}
|
||||
initDevelopmentWorkingDir();
|
||||
|
||||
@@ -173,7 +173,7 @@ void ConfigManager::upgrade()
|
||||
ProjectVersion createdWith = m_version;
|
||||
|
||||
// Don't use old themes as they break the UI (i.e. 0.4 != 1.0, etc)
|
||||
if (createdWith.setCompareType(ProjectVersion::Minor) != LMMS_VERSION)
|
||||
if (createdWith.setCompareType(ProjectVersion::CompareType::Minor) != LMMS_VERSION)
|
||||
{
|
||||
m_themeDir = defaultThemeDir();
|
||||
}
|
||||
@@ -334,10 +334,9 @@ void ConfigManager::addRecentlyOpenedProject(const QString & file)
|
||||
|
||||
|
||||
|
||||
const QString & ConfigManager::value(const QString & cls,
|
||||
const QString & attribute) const
|
||||
QString ConfigManager::value(const QString& cls, const QString& attribute, const QString& defaultVal) const
|
||||
{
|
||||
if(m_settings.contains(cls))
|
||||
if (m_settings.find(cls) != m_settings.end())
|
||||
{
|
||||
for (const auto& setting : m_settings[cls])
|
||||
{
|
||||
@@ -347,18 +346,7 @@ const QString & ConfigManager::value(const QString & cls,
|
||||
}
|
||||
}
|
||||
}
|
||||
static QString empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const QString & ConfigManager::value(const QString & cls,
|
||||
const QString & attribute,
|
||||
const QString & defaultVal) const
|
||||
{
|
||||
const QString & val = value(cls, attribute);
|
||||
return val.isEmpty() ? defaultVal : val;
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
|
||||
@@ -569,8 +557,8 @@ void ConfigManager::loadConfigFile(const QString & configFile)
|
||||
upgrade();
|
||||
|
||||
QStringList searchPaths;
|
||||
if(! qgetenv("LMMS_THEME_PATH").isNull())
|
||||
searchPaths << qgetenv("LMMS_THEME_PATH");
|
||||
if (std::getenv("LMMS_THEME_PATH"))
|
||||
searchPaths << std::getenv("LMMS_THEME_PATH");
|
||||
searchPaths << themeDir() << defaultThemeDir();
|
||||
QDir::setSearchPaths("resources", searchPaths);
|
||||
|
||||
@@ -719,7 +707,7 @@ unsigned int ConfigManager::legacyConfigVersion()
|
||||
{
|
||||
ProjectVersion createdWith = m_version;
|
||||
|
||||
createdWith.setCompareType(ProjectVersion::Build);
|
||||
createdWith.setCompareType(ProjectVersion::CompareType::Build);
|
||||
|
||||
if( createdWith < "1.1.90" )
|
||||
{
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
*/
|
||||
|
||||
#include <QDomElement>
|
||||
#include <QVector>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "AudioEngine.h"
|
||||
#include "ControllerConnection.h"
|
||||
@@ -40,11 +40,11 @@ namespace lmms
|
||||
|
||||
|
||||
long Controller::s_periods = 0;
|
||||
QVector<Controller *> Controller::s_controllers;
|
||||
std::vector<Controller*> Controller::s_controllers;
|
||||
|
||||
|
||||
|
||||
Controller::Controller( ControllerTypes _type, Model * _parent,
|
||||
Controller::Controller( ControllerType _type, Model * _parent,
|
||||
const QString & _display_name ) :
|
||||
Model( _parent, _display_name ),
|
||||
JournallingObject(),
|
||||
@@ -53,9 +53,9 @@ Controller::Controller( ControllerTypes _type, Model * _parent,
|
||||
m_connectionCount( 0 ),
|
||||
m_type( _type )
|
||||
{
|
||||
if( _type != DummyController && _type != MidiController )
|
||||
if( _type != ControllerType::Dummy && _type != ControllerType::Midi )
|
||||
{
|
||||
s_controllers.append( this );
|
||||
s_controllers.push_back(this);
|
||||
// Determine which name to use
|
||||
for ( uint i=s_controllers.size(); ; i++ )
|
||||
{
|
||||
@@ -86,10 +86,10 @@ Controller::Controller( ControllerTypes _type, Model * _parent,
|
||||
|
||||
Controller::~Controller()
|
||||
{
|
||||
int idx = s_controllers.indexOf( this );
|
||||
if( idx >= 0 )
|
||||
auto it = std::find(s_controllers.begin(), s_controllers.end(), this);
|
||||
if (it != s_controllers.end())
|
||||
{
|
||||
s_controllers.remove( idx );
|
||||
s_controllers.erase(it);
|
||||
}
|
||||
|
||||
m_valueBuffer.clear();
|
||||
@@ -182,30 +182,30 @@ void Controller::resetFrameCounter()
|
||||
|
||||
|
||||
|
||||
Controller * Controller::create( ControllerTypes _ct, Model * _parent )
|
||||
Controller * Controller::create( ControllerType _ct, Model * _parent )
|
||||
{
|
||||
static Controller * dummy = nullptr;
|
||||
Controller * c = nullptr;
|
||||
|
||||
switch( _ct )
|
||||
{
|
||||
case Controller::DummyController:
|
||||
case ControllerType::Dummy:
|
||||
if (!dummy)
|
||||
dummy = new Controller( DummyController, nullptr,
|
||||
dummy = new Controller( ControllerType::Dummy, nullptr,
|
||||
QString() );
|
||||
c = dummy;
|
||||
break;
|
||||
|
||||
case Controller::LfoController:
|
||||
case ControllerType::Lfo:
|
||||
c = new class LfoController( _parent );
|
||||
break;
|
||||
|
||||
case Controller::PeakController:
|
||||
case ControllerType::Peak:
|
||||
//Already instantiated in EffectChain::loadSettings()
|
||||
Q_ASSERT( false );
|
||||
break;
|
||||
|
||||
case Controller::MidiController:
|
||||
case ControllerType::Midi:
|
||||
c = new class MidiController( _parent );
|
||||
break;
|
||||
|
||||
@@ -221,14 +221,14 @@ Controller * Controller::create( ControllerTypes _ct, Model * _parent )
|
||||
Controller * Controller::create( const QDomElement & _this, Model * _parent )
|
||||
{
|
||||
Controller * c;
|
||||
if( _this.attribute( "type" ).toInt() == Controller::PeakController )
|
||||
if( static_cast<ControllerType>(_this.attribute( "type" ).toInt()) == ControllerType::Peak )
|
||||
{
|
||||
c = PeakController::getControllerBySetting( _this );
|
||||
}
|
||||
else
|
||||
{
|
||||
c = create(
|
||||
static_cast<ControllerTypes>( _this.attribute( "type" ).toInt() ),
|
||||
static_cast<ControllerType>( _this.attribute( "type" ).toInt() ),
|
||||
_parent );
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ bool Controller::hasModel( const Model * m ) const
|
||||
|
||||
void Controller::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
{
|
||||
_this.setAttribute( "type", type() );
|
||||
_this.setAttribute( "type", static_cast<int>(type()) );
|
||||
_this.setAttribute( "name", name() );
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ void Controller::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
|
||||
void Controller::loadSettings( const QDomElement & _this )
|
||||
{
|
||||
if( _this.attribute( "type" ).toInt() != type() )
|
||||
if( static_cast<ControllerType>(_this.attribute( "type" ).toInt()) != type() )
|
||||
{
|
||||
qWarning( "controller-type does not match controller-type of "
|
||||
"settings-node!\n" );
|
||||
|
||||
@@ -50,21 +50,21 @@ ControllerConnection::ControllerConnection(Controller * _controller) :
|
||||
}
|
||||
else
|
||||
{
|
||||
m_controller = Controller::create( Controller::DummyController,
|
||||
m_controller = Controller::create( Controller::ControllerType::Dummy,
|
||||
nullptr );
|
||||
}
|
||||
s_connections.append( this );
|
||||
s_connections.push_back(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ControllerConnection::ControllerConnection( int _controllerId ) :
|
||||
m_controller( Controller::create( Controller::DummyController, nullptr ) ),
|
||||
m_controller( Controller::create( Controller::ControllerType::Dummy, nullptr ) ),
|
||||
m_controllerId( _controllerId ),
|
||||
m_ownsController( false )
|
||||
{
|
||||
s_connections.append( this );
|
||||
s_connections.push_back(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -72,11 +72,14 @@ ControllerConnection::ControllerConnection( int _controllerId ) :
|
||||
|
||||
ControllerConnection::~ControllerConnection()
|
||||
{
|
||||
if( m_controller && m_controller->type() != Controller::DummyController )
|
||||
if( m_controller && m_controller->type() != Controller::ControllerType::Dummy )
|
||||
{
|
||||
m_controller->removeConnection( this );
|
||||
}
|
||||
s_connections.remove( s_connections.indexOf( this ) );
|
||||
|
||||
auto it = std::find(s_connections.begin(), s_connections.end(), this);
|
||||
if (it != s_connections.end()) { s_connections.erase(it); };
|
||||
|
||||
if( m_ownsController )
|
||||
{
|
||||
delete m_controller;
|
||||
@@ -101,14 +104,14 @@ void ControllerConnection::setController( Controller * _controller )
|
||||
m_controller = nullptr;
|
||||
}
|
||||
|
||||
if( m_controller && m_controller->type() != Controller::DummyController )
|
||||
if( m_controller && m_controller->type() != Controller::ControllerType::Dummy )
|
||||
{
|
||||
m_controller->removeConnection( this );
|
||||
}
|
||||
|
||||
if( !_controller )
|
||||
{
|
||||
m_controller = Controller::create( Controller::DummyController, nullptr );
|
||||
m_controller = Controller::create( Controller::ControllerType::Dummy, nullptr );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -116,7 +119,7 @@ void ControllerConnection::setController( Controller * _controller )
|
||||
}
|
||||
m_controllerId = -1;
|
||||
|
||||
if( _controller->type() != Controller::DummyController )
|
||||
if( _controller->type() != Controller::ControllerType::Dummy )
|
||||
{
|
||||
_controller->addConnection( this );
|
||||
QObject::connect( _controller, SIGNAL(valueChanged()),
|
||||
@@ -124,7 +127,7 @@ void ControllerConnection::setController( Controller * _controller )
|
||||
}
|
||||
|
||||
m_ownsController =
|
||||
(_controller->type() == Controller::MidiController);
|
||||
(_controller->type() == Controller::ControllerType::Midi);
|
||||
|
||||
// If we don't own the controller, allow deletion of controller
|
||||
// to delete the connection
|
||||
@@ -165,7 +168,7 @@ void ControllerConnection::finalizeConnections()
|
||||
c->setController( Engine::getSong()->
|
||||
controllers().at( c->m_controllerId ) );
|
||||
}
|
||||
else if (c->getController()->type() == Controller::DummyController)
|
||||
else if (c->getController()->type() == Controller::ControllerType::Dummy)
|
||||
{
|
||||
delete c;
|
||||
--i;
|
||||
@@ -186,10 +189,12 @@ void ControllerConnection::saveSettings( QDomDocument & _doc, QDomElement & _thi
|
||||
}
|
||||
else
|
||||
{
|
||||
int id = Engine::getSong()->controllers().indexOf( m_controller );
|
||||
if( id >= 0 )
|
||||
const auto& controllers = Engine::getSong()->controllers();
|
||||
const auto it = std::find(controllers.begin(), controllers.end(), m_controller);
|
||||
if (it != controllers.end())
|
||||
{
|
||||
_this.setAttribute( "id", id );
|
||||
const int id = std::distance(controllers.begin(), it);
|
||||
_this.setAttribute("id", id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,7 +228,7 @@ void ControllerConnection::loadSettings( const QDomElement & _this )
|
||||
}
|
||||
else
|
||||
{
|
||||
m_controller = Controller::create( Controller::DummyController, nullptr );
|
||||
m_controller = Controller::create( Controller::ControllerType::Dummy, nullptr );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "DataFile.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
|
||||
@@ -47,6 +48,7 @@
|
||||
#include "TextFloat.h"
|
||||
#include "Track.h"
|
||||
#include "PathUtil.h"
|
||||
#include "UpgradeExtendedNoteRange.h"
|
||||
|
||||
#include "lmmsversion.h"
|
||||
|
||||
@@ -77,6 +79,7 @@ const std::vector<DataFile::UpgradeMethod> DataFile::UPGRADE_METHODS = {
|
||||
&DataFile::upgrade_automationNodes , &DataFile::upgrade_extendedNoteRange,
|
||||
&DataFile::upgrade_defaultTripleOscillatorHQ,
|
||||
&DataFile::upgrade_mixerRename , &DataFile::upgrade_bbTcoRename,
|
||||
&DataFile::upgrade_sampleAndHold , &DataFile::upgrade_midiCCIndexing
|
||||
};
|
||||
|
||||
// Vector of all versions that have upgrade routines.
|
||||
@@ -97,17 +100,16 @@ namespace
|
||||
QString m_name;
|
||||
};
|
||||
|
||||
const auto s_types = std::array<TypeDescStruct, DataFile::TypeCount>
|
||||
{
|
||||
TypeDescStruct{ DataFile::UnknownType, "unknown" },
|
||||
TypeDescStruct{ DataFile::SongProject, "song" },
|
||||
TypeDescStruct{ DataFile::SongProjectTemplate, "songtemplate" },
|
||||
TypeDescStruct{ DataFile::InstrumentTrackSettings, "instrumenttracksettings" },
|
||||
TypeDescStruct{ DataFile::DragNDropData, "dnddata" },
|
||||
TypeDescStruct{ DataFile::ClipboardData, "clipboard-data" },
|
||||
TypeDescStruct{ DataFile::JournalData, "journaldata" },
|
||||
TypeDescStruct{ DataFile::EffectSettings, "effectsettings" },
|
||||
TypeDescStruct{ DataFile::MidiClip, "midiclip" }
|
||||
const auto s_types = std::array{
|
||||
TypeDescStruct{ DataFile::Type::Unknown, "unknown" },
|
||||
TypeDescStruct{ DataFile::Type::SongProject, "song" },
|
||||
TypeDescStruct{ DataFile::Type::SongProjectTemplate, "songtemplate" },
|
||||
TypeDescStruct{ DataFile::Type::InstrumentTrackSettings, "instrumenttracksettings" },
|
||||
TypeDescStruct{ DataFile::Type::DragNDropData, "dnddata" },
|
||||
TypeDescStruct{ DataFile::Type::ClipboardData, "clipboard-data" },
|
||||
TypeDescStruct{ DataFile::Type::JournalData, "journaldata" },
|
||||
TypeDescStruct{ DataFile::Type::EffectSettings, "effectsettings" },
|
||||
TypeDescStruct{ DataFile::Type::MidiClip, "midiclip" }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -213,7 +215,7 @@ bool DataFile::validate( QString extension )
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case Type::UnknownType:
|
||||
case Type::Unknown:
|
||||
if (! ( extension == "mmp" || extension == "mpt" || extension == "mmpz" ||
|
||||
extension == "xpf" || extension == "xml" ||
|
||||
( extension == "xiz" && ! getPluginFactory()->pluginSupportingExtension(extension).isNull()) ||
|
||||
@@ -250,7 +252,7 @@ QString DataFile::nameWithExtension( const QString & _fn ) const
|
||||
|
||||
switch( type() )
|
||||
{
|
||||
case SongProject:
|
||||
case Type::SongProject:
|
||||
if( extension != "mmp" &&
|
||||
extension != "mpt" &&
|
||||
extension != "mmpz" )
|
||||
@@ -263,13 +265,13 @@ QString DataFile::nameWithExtension( const QString & _fn ) const
|
||||
return _fn + ".mmp";
|
||||
}
|
||||
break;
|
||||
case SongProjectTemplate:
|
||||
case Type::SongProjectTemplate:
|
||||
if( extension != "mpt" )
|
||||
{
|
||||
return _fn + ".mpt";
|
||||
}
|
||||
break;
|
||||
case InstrumentTrackSettings:
|
||||
case Type::InstrumentTrackSettings:
|
||||
if( extension != "xpf" )
|
||||
{
|
||||
return _fn + ".xpf";
|
||||
@@ -285,8 +287,8 @@ QString DataFile::nameWithExtension( const QString & _fn ) const
|
||||
|
||||
void DataFile::write( QTextStream & _strm )
|
||||
{
|
||||
if( type() == SongProject || type() == SongProjectTemplate
|
||||
|| type() == InstrumentTrackSettings )
|
||||
if( type() == Type::SongProject || type() == Type::SongProjectTemplate
|
||||
|| type() == Type::InstrumentTrackSettings )
|
||||
{
|
||||
cleanMetaNodes( documentElement() );
|
||||
}
|
||||
@@ -300,7 +302,7 @@ void DataFile::write( QTextStream & _strm )
|
||||
bool DataFile::writeFile(const QString& filename, bool withResources)
|
||||
{
|
||||
// Small lambda function for displaying errors
|
||||
auto showError = [this](QString title, QString body){
|
||||
auto showError = [](QString title, QString body){
|
||||
if (gui::getGUI() != nullptr)
|
||||
{
|
||||
QMessageBox mb;
|
||||
@@ -584,21 +586,17 @@ bool DataFile::hasLocalPlugins(QDomElement parent /* = QDomElement()*/, bool fir
|
||||
|
||||
DataFile::Type DataFile::type( const QString& typeName )
|
||||
{
|
||||
for( int i = 0; i < TypeCount; ++i )
|
||||
{
|
||||
if( s_types[i].m_name == typeName )
|
||||
{
|
||||
return static_cast<DataFile::Type>( i );
|
||||
}
|
||||
}
|
||||
const auto it = std::find_if(s_types.begin(), s_types.end(),
|
||||
[&typeName](const TypeDescStruct& type) { return type.m_name == typeName; });
|
||||
if (it != s_types.end()) { return it->m_type; }
|
||||
|
||||
// compat code
|
||||
if( typeName == "channelsettings" )
|
||||
{
|
||||
return DataFile::InstrumentTrackSettings;
|
||||
return Type::InstrumentTrackSettings;
|
||||
}
|
||||
|
||||
return UnknownType;
|
||||
return Type::Unknown;
|
||||
}
|
||||
|
||||
|
||||
@@ -606,12 +604,7 @@ DataFile::Type DataFile::type( const QString& typeName )
|
||||
|
||||
QString DataFile::typeName( Type type )
|
||||
{
|
||||
if( type >= UnknownType && type < TypeCount )
|
||||
{
|
||||
return s_types[type].m_name;
|
||||
}
|
||||
|
||||
return s_types[UnknownType].m_name;
|
||||
return s_types[static_cast<std::size_t>(type)].m_name;
|
||||
}
|
||||
|
||||
|
||||
@@ -1677,74 +1670,10 @@ void DataFile::upgrade_automationNodes()
|
||||
*/
|
||||
void DataFile::upgrade_extendedNoteRange()
|
||||
{
|
||||
auto affected = [](const QDomElement& instrument)
|
||||
{
|
||||
return instrument.attribute("name") == "zynaddsubfx" ||
|
||||
instrument.attribute("name") == "vestige" ||
|
||||
instrument.attribute("name") == "lv2instrument" ||
|
||||
instrument.attribute("name") == "carlapatchbay" ||
|
||||
instrument.attribute("name") == "carlarack";
|
||||
};
|
||||
auto root = documentElement();
|
||||
UpgradeExtendedNoteRange upgradeExtendedNoteRange(root);
|
||||
|
||||
if (!elementsByTagName("song").item(0).isNull())
|
||||
{
|
||||
// Dealing with a project file, go through all the tracks
|
||||
QDomNodeList tracks = elementsByTagName("track");
|
||||
for (int i = 0; !tracks.item(i).isNull(); i++)
|
||||
{
|
||||
// Ignore BB container tracks
|
||||
if (tracks.item(i).toElement().attribute("type").toInt() == 1) { continue; }
|
||||
|
||||
QDomNodeList instruments = tracks.item(i).toElement().elementsByTagName("instrument");
|
||||
if (instruments.isEmpty()) { continue; }
|
||||
QDomElement instrument = instruments.item(0).toElement();
|
||||
// Raise the base note of every instrument by 12 to compensate for the change
|
||||
// of A4 key code from 57 to 69. This ensures that notes are labeled correctly.
|
||||
instrument.parentNode().toElement().setAttribute(
|
||||
"basenote",
|
||||
instrument.parentNode().toElement().attribute("basenote").toInt() + 12);
|
||||
// Raise the pitch of all notes in patterns assigned to instruments not affected
|
||||
// by #1857 by an octave. This negates the base note change for normal instruments,
|
||||
// but leaves the MIDI-based instruments sounding an octave lower, preserving their
|
||||
// pitch in existing projects.
|
||||
if (!affected(instrument))
|
||||
{
|
||||
QDomNodeList patterns = tracks.item(i).toElement().elementsByTagName("pattern");
|
||||
for (int i = 0; !patterns.item(i).isNull(); i++)
|
||||
{
|
||||
QDomNodeList notes = patterns.item(i).toElement().elementsByTagName("note");
|
||||
for (int i = 0; !notes.item(i).isNull(); i++)
|
||||
{
|
||||
notes.item(i).toElement().setAttribute(
|
||||
"key",
|
||||
notes.item(i).toElement().attribute("key").toInt() + 12
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dealing with a preset, not a song
|
||||
QDomNodeList presets = elementsByTagName("instrumenttrack");
|
||||
if (presets.item(0).isNull()) { return; }
|
||||
QDomElement preset = presets.item(0).toElement();
|
||||
// Common correction for all instrument presets (make base notes match the new MIDI range).
|
||||
// NOTE: Many older presets do not have any basenote defined, assume they were "made for 57".
|
||||
// (Specifying a default like this also happens to fix a FileBrowser bug where previews of presets
|
||||
// with undefined basenote always play with the basenote inherited from previously played preview.)
|
||||
int oldBase = preset.attribute("basenote", "57").toInt();
|
||||
preset.setAttribute("basenote", oldBase + 12);
|
||||
// Extra correction for Zyn, VeSTige, LV2 and Carla (to preserve the original buggy behavior).
|
||||
QDomNodeList instruments = presets.item(0).toElement().elementsByTagName("instrument");
|
||||
if (instruments.isEmpty()) { return; }
|
||||
QDomElement instrument = instruments.item(0).toElement();
|
||||
if (affected(instrument))
|
||||
{
|
||||
preset.setAttribute("basenote", preset.attribute("basenote").toInt() + 12);
|
||||
}
|
||||
}
|
||||
upgradeExtendedNoteRange.upgrade();
|
||||
}
|
||||
|
||||
|
||||
@@ -1775,26 +1704,56 @@ void DataFile::upgrade_mixerRename()
|
||||
{
|
||||
// Change nodename <fxmixer> to <mixer>
|
||||
QDomNodeList fxmixer = elementsByTagName("fxmixer");
|
||||
for (int i = 0; !fxmixer.item(i).isNull(); ++i)
|
||||
for (int i = 0; i < fxmixer.length(); ++i)
|
||||
{
|
||||
fxmixer.item(i).toElement().setTagName("mixer");
|
||||
auto item = fxmixer.item(i).toElement();
|
||||
if (item.isNull())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
item.setTagName("mixer");
|
||||
}
|
||||
|
||||
// Change nodename <fxchannel> to <mixerchannel>
|
||||
QDomNodeList fxchannel = elementsByTagName("fxchannel");
|
||||
for (int i = 0; !fxchannel.item(i).isNull(); ++i)
|
||||
for (int i = 0; i < fxchannel.length(); ++i)
|
||||
{
|
||||
fxchannel.item(i).toElement().setTagName("mixerchannel");
|
||||
auto item = fxchannel.item(i).toElement();
|
||||
if (item.isNull())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
item.setTagName("mixerchannel");
|
||||
}
|
||||
|
||||
// Change the attribute fxch of element <instrumenttrack> to mixch
|
||||
QDomNodeList fxch = elementsByTagName("instrumenttrack");
|
||||
for(int i = 0; !fxch.item(i).isNull(); ++i)
|
||||
for (int i = 0; i < fxch.length(); ++i)
|
||||
{
|
||||
if(fxch.item(i).toElement().hasAttribute("fxch"))
|
||||
auto item = fxch.item(i).toElement();
|
||||
if (item.isNull())
|
||||
{
|
||||
fxch.item(i).toElement().setAttribute("mixch", fxch.item(i).toElement().attribute("fxch"));
|
||||
fxch.item(i).toElement().removeAttribute("fxch");
|
||||
continue;
|
||||
}
|
||||
if (item.hasAttribute("fxch"))
|
||||
{
|
||||
item.setAttribute("mixch", item.attribute("fxch"));
|
||||
item.removeAttribute("fxch");
|
||||
}
|
||||
}
|
||||
// Change the attribute fxch of element <sampletrack> to mixch
|
||||
fxch = elementsByTagName("sampletrack");
|
||||
for (int i = 0; i < fxch.length(); ++i)
|
||||
{
|
||||
auto item = fxch.item(i).toElement();
|
||||
if (item.isNull())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (item.hasAttribute("fxch"))
|
||||
{
|
||||
item.setAttribute("mixch", item.attribute("fxch"));
|
||||
item.removeAttribute("fxch");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1825,8 +1784,8 @@ void DataFile::upgrade_bbTcoRename()
|
||||
for (int i = 0; !elements.item(i).isNull(); ++i)
|
||||
{
|
||||
auto e = elements.item(i).toElement();
|
||||
static_assert(Track::PatternTrack == 1, "Must be type=1 for backwards compatibility");
|
||||
if (e.attribute("type").toInt() == Track::PatternTrack)
|
||||
static_assert(Track::Type::Pattern == static_cast<Track::Type>(1), "Must be type=1 for backwards compatibility");
|
||||
if (static_cast<Track::Type>(e.attribute("type").toInt()) == Track::Type::Pattern)
|
||||
{
|
||||
e.setAttribute("name", e.attribute("name").replace("Beat/Bassline", "Pattern"));
|
||||
}
|
||||
@@ -1834,6 +1793,44 @@ void DataFile::upgrade_bbTcoRename()
|
||||
}
|
||||
|
||||
|
||||
// Set LFO speed to 0.01 on projects made before sample-and-hold PR
|
||||
void DataFile::upgrade_sampleAndHold()
|
||||
{
|
||||
QDomNodeList elements = elementsByTagName("lfocontroller");
|
||||
for (int i = 0; i < elements.length(); ++i)
|
||||
{
|
||||
if (elements.item(i).isNull()) { continue; }
|
||||
auto e = elements.item(i).toElement();
|
||||
// Correct old random wave LFO speeds
|
||||
if (e.attribute("wave").toInt() == 6)
|
||||
{
|
||||
e.setAttribute("speed",0.01f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Update MIDI CC indexes, so that they are counted from 0. Older releases of LMMS
|
||||
//! count the CCs from 1.
|
||||
void DataFile::upgrade_midiCCIndexing()
|
||||
{
|
||||
static constexpr std::array attributesToUpdate{"inputcontroller", "outputcontroller"};
|
||||
|
||||
QDomNodeList elements = elementsByTagName("Midicontroller");
|
||||
for(int i = 0; i < elements.length(); i++)
|
||||
{
|
||||
if (elements.item(i).isNull()) { continue; }
|
||||
auto element = elements.item(i).toElement();
|
||||
for (const char* attrName : attributesToUpdate)
|
||||
{
|
||||
if (element.hasAttribute(attrName))
|
||||
{
|
||||
int cc = element.attribute(attrName).toInt();
|
||||
element.setAttribute(attrName, cc - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DataFile::upgrade()
|
||||
{
|
||||
// Runs all necessary upgrade methods
|
||||
@@ -1854,7 +1851,7 @@ void DataFile::upgrade()
|
||||
documentElement().setAttribute( "creator", "LMMS" );
|
||||
documentElement().setAttribute( "creatorversion", LMMS_VERSION );
|
||||
|
||||
if( type() == SongProject || type() == SongProjectTemplate )
|
||||
if( type() == Type::SongProject || type() == Type::SongProjectTemplate )
|
||||
{
|
||||
// Time-signature
|
||||
if ( !m_head.hasAttribute( "timesig_numerator" ) )
|
||||
@@ -1932,8 +1929,8 @@ void DataFile::loadData( const QByteArray & _data, const QString & _sourceFile )
|
||||
ProjectVersion createdWith = root.attribute("creatorversion");
|
||||
ProjectVersion openedWith = LMMS_VERSION;
|
||||
|
||||
if (createdWith.setCompareType(ProjectVersion::Minor)
|
||||
!= openedWith.setCompareType(ProjectVersion::Minor)
|
||||
if (createdWith.setCompareType(ProjectVersion::CompareType::Minor)
|
||||
!= openedWith.setCompareType(ProjectVersion::CompareType::Minor)
|
||||
&& gui::getGUI() != nullptr && root.attribute("type") == "song"
|
||||
){
|
||||
auto projectType = _sourceFile.endsWith(".mpt") ?
|
||||
|
||||
@@ -273,7 +273,9 @@ int DrumSynth::GetDSFileSamples(QString dsfile, int16_t *&wave, int channels, sa
|
||||
//generation
|
||||
long Length, tpos=0, tplus, totmp, t, i, j;
|
||||
float x[3] = {0.f, 0.f, 0.f};
|
||||
float MasterTune, randmax, randmax2;
|
||||
float MasterTune;
|
||||
constexpr float randmax = 1.f / static_cast<float>(RAND_MAX);
|
||||
constexpr float randmax2 = 2.f / static_cast<float>(RAND_MAX);
|
||||
int MainFilter, HighPass;
|
||||
|
||||
long NON, NT, TON, DiON, TDroop=0, DStep;
|
||||
@@ -454,7 +456,6 @@ int DrumSynth::GetDSFileSamples(QString dsfile, int16_t *&wave, int channels, sa
|
||||
}
|
||||
|
||||
//prepare envelopes
|
||||
randmax = 1.f / RAND_MAX; randmax2 = 2.f * randmax;
|
||||
for (i=1;i<8;i++) { envData[i][NEXTT]=0; envData[i][PNT]=0; }
|
||||
Length = LongestEnv();
|
||||
|
||||
@@ -745,4 +746,4 @@ int DrumSynth::GetDSFileSamples(QString dsfile, int16_t *&wave, int channels, sa
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
} // namespace lmms
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
|
||||
#include <QDomElement>
|
||||
#include <cassert>
|
||||
|
||||
#include "EffectChain.h"
|
||||
#include "Effect.h"
|
||||
@@ -56,7 +57,7 @@ EffectChain::~EffectChain()
|
||||
void EffectChain::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
{
|
||||
m_enabledModel.saveSettings( _doc, _this, "enabled" );
|
||||
_this.setAttribute( "numofeffects", m_effects.count() );
|
||||
_this.setAttribute("numofeffects", static_cast<int>(m_effects.size()));
|
||||
|
||||
for( Effect* effect : m_effects)
|
||||
{
|
||||
@@ -121,7 +122,7 @@ void EffectChain::loadSettings( const QDomElement & _this )
|
||||
void EffectChain::appendEffect( Effect * _effect )
|
||||
{
|
||||
Engine::audioEngine()->requestChangeInModel();
|
||||
m_effects.append( _effect );
|
||||
m_effects.push_back(_effect);
|
||||
Engine::audioEngine()->doneChangeInModel();
|
||||
|
||||
m_enabledModel.setValue( true );
|
||||
@@ -136,7 +137,7 @@ void EffectChain::removeEffect( Effect * _effect )
|
||||
{
|
||||
Engine::audioEngine()->requestChangeInModel();
|
||||
|
||||
Effect ** found = std::find( m_effects.begin(), m_effects.end(), _effect );
|
||||
auto found = std::find(m_effects.begin(), m_effects.end(), _effect);
|
||||
if( found == m_effects.end() )
|
||||
{
|
||||
Engine::audioEngine()->doneChangeInModel();
|
||||
@@ -146,7 +147,7 @@ void EffectChain::removeEffect( Effect * _effect )
|
||||
|
||||
Engine::audioEngine()->doneChangeInModel();
|
||||
|
||||
if( m_effects.isEmpty() )
|
||||
if (m_effects.empty())
|
||||
{
|
||||
m_enabledModel.setValue( false );
|
||||
}
|
||||
@@ -159,10 +160,11 @@ void EffectChain::removeEffect( Effect * _effect )
|
||||
|
||||
void EffectChain::moveDown( Effect * _effect )
|
||||
{
|
||||
if( _effect != m_effects.last() )
|
||||
if (_effect != m_effects.back())
|
||||
{
|
||||
int i = m_effects.indexOf(_effect);
|
||||
std::swap(m_effects[i + 1], m_effects[i]);
|
||||
auto it = std::find(m_effects.begin(), m_effects.end(), _effect);
|
||||
assert(it != m_effects.end());
|
||||
std::swap(*std::next(it), *it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,10 +173,11 @@ void EffectChain::moveDown( Effect * _effect )
|
||||
|
||||
void EffectChain::moveUp( Effect * _effect )
|
||||
{
|
||||
if( _effect != m_effects.first() )
|
||||
if (_effect != m_effects.front())
|
||||
{
|
||||
int i = m_effects.indexOf(_effect);
|
||||
std::swap(m_effects[i - 1], m_effects[i]);
|
||||
auto it = std::find(m_effects.begin(), m_effects.end(), _effect);
|
||||
assert(it != m_effects.end());
|
||||
std::swap(*std::prev(it), *it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,9 +231,9 @@ void EffectChain::clear()
|
||||
|
||||
Engine::audioEngine()->requestChangeInModel();
|
||||
|
||||
while( m_effects.count() )
|
||||
while (m_effects.size())
|
||||
{
|
||||
Effect * e = m_effects[m_effects.count() - 1];
|
||||
auto e = m_effects[m_effects.size() - 1];
|
||||
m_effects.pop_back();
|
||||
delete e;
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ EnvelopeAndLfoParameters::EnvelopeAndLfoParameters(
|
||||
SECS_PER_LFO_OSCILLATION * 1000.0, this,
|
||||
tr( "LFO frequency" ) ),
|
||||
m_lfoAmountModel( 0.0, -1.0, 1.0, 0.005, this, tr( "LFO mod amount" ) ),
|
||||
m_lfoWaveModel( SineWave, 0, NumLfoShapes, this, tr( "LFO wave shape" ) ),
|
||||
m_lfoWaveModel( static_cast<int>(LfoShape::SineWave), 0, NumLfoShapes, this, tr( "LFO wave shape" ) ),
|
||||
m_x100Model( false, this, tr( "LFO frequency x 100" ) ),
|
||||
m_controlEnvAmountModel( false, this, tr( "Modulate env amount" ) ),
|
||||
m_lfoFrame( 0 ),
|
||||
@@ -209,28 +209,28 @@ inline sample_t EnvelopeAndLfoParameters::lfoShapeSample( fpp_t _frame_offset )
|
||||
const float phase = frame / static_cast<float>(
|
||||
m_lfoOscillationFrames );
|
||||
sample_t shape_sample;
|
||||
switch( m_lfoWaveModel.value() )
|
||||
switch( static_cast<LfoShape>(m_lfoWaveModel.value()) )
|
||||
{
|
||||
case TriangleWave:
|
||||
case LfoShape::TriangleWave:
|
||||
shape_sample = Oscillator::triangleSample( phase );
|
||||
break;
|
||||
case SquareWave:
|
||||
case LfoShape::SquareWave:
|
||||
shape_sample = Oscillator::squareSample( phase );
|
||||
break;
|
||||
case SawWave:
|
||||
case LfoShape::SawWave:
|
||||
shape_sample = Oscillator::sawSample( phase );
|
||||
break;
|
||||
case UserDefinedWave:
|
||||
case LfoShape::UserDefinedWave:
|
||||
shape_sample = m_userWave.userWaveSample( phase );
|
||||
break;
|
||||
case RandomWave:
|
||||
case LfoShape::RandomWave:
|
||||
if( frame == 0 )
|
||||
{
|
||||
m_random = Oscillator::noiseSample( 0.0f );
|
||||
}
|
||||
shape_sample = m_random;
|
||||
break;
|
||||
case SineWave:
|
||||
case LfoShape::SineWave:
|
||||
default:
|
||||
shape_sample = Oscillator::sinSample( phase );
|
||||
break;
|
||||
@@ -274,7 +274,7 @@ inline void EnvelopeAndLfoParameters::fillLfoLevel( float * _buf,
|
||||
}
|
||||
|
||||
fpp_t offset = 0;
|
||||
const float lafI = 1.0f / qMax( minimumFrames, m_lfoAttackFrames );
|
||||
const float lafI = 1.0f / std::max(minimumFrames, m_lfoAttackFrames);
|
||||
for( ; offset < _frames && _frame < m_lfoAttackFrames; ++offset,
|
||||
++_frame )
|
||||
{
|
||||
@@ -404,16 +404,16 @@ void EnvelopeAndLfoParameters::updateSampleVars()
|
||||
// TODO: Remove the expKnobVals, time should be linear
|
||||
const auto predelay_frames = static_cast<f_cnt_t>(frames_per_env_seg * expKnobVal(m_predelayModel.value()));
|
||||
|
||||
const f_cnt_t attack_frames = qMax( minimumFrames,
|
||||
static_cast<f_cnt_t>( frames_per_env_seg *
|
||||
expKnobVal( m_attackModel.value() ) ) );
|
||||
const f_cnt_t attack_frames = std::max(minimumFrames,
|
||||
static_cast<f_cnt_t>(frames_per_env_seg *
|
||||
expKnobVal(m_attackModel.value())));
|
||||
|
||||
const auto hold_frames = static_cast<f_cnt_t>(frames_per_env_seg * expKnobVal(m_holdModel.value()));
|
||||
|
||||
const f_cnt_t decay_frames = qMax( minimumFrames,
|
||||
static_cast<f_cnt_t>( frames_per_env_seg *
|
||||
expKnobVal( m_decayModel.value() *
|
||||
( 1 - m_sustainModel.value() ) ) ) );
|
||||
const f_cnt_t decay_frames = std::max(minimumFrames,
|
||||
static_cast<f_cnt_t>(frames_per_env_seg *
|
||||
expKnobVal(m_decayModel.value() *
|
||||
(1 - m_sustainModel.value()))));
|
||||
|
||||
m_sustainLevel = m_sustainModel.value();
|
||||
m_amount = m_amountModel.value();
|
||||
@@ -430,7 +430,7 @@ void EnvelopeAndLfoParameters::updateSampleVars()
|
||||
decay_frames;
|
||||
m_rFrames = static_cast<f_cnt_t>( frames_per_env_seg *
|
||||
expKnobVal( m_releaseModel.value() ) );
|
||||
m_rFrames = qMax( minimumFrames, m_rFrames );
|
||||
m_rFrames = std::max(minimumFrames, m_rFrames);
|
||||
|
||||
if( static_cast<int>( floorf( m_amount * 1000.0f ) ) == 0 )
|
||||
{
|
||||
|
||||
@@ -61,7 +61,7 @@ void ImportFilter::import( const QString & _file_to_import,
|
||||
const bool j = Engine::projectJournal()->isJournalling();
|
||||
Engine::projectJournal()->setJournalling( false );
|
||||
|
||||
for (const Plugin::Descriptor* desc : getPluginFactory()->descriptors(Plugin::ImportFilter))
|
||||
for (const Plugin::Descriptor* desc : getPluginFactory()->descriptors(Plugin::Type::ImportFilter))
|
||||
{
|
||||
unique_ptr<Plugin> p(Plugin::instantiate( desc->name, nullptr, s.data() ));
|
||||
if( dynamic_cast<ImportFilter *>( p.get() ) != nullptr &&
|
||||
|
||||
@@ -185,8 +185,8 @@ void Instrument::applyRelease( sampleFrame * buf, const NotePlayHandle * _n )
|
||||
if( fl <= desiredReleaseFrames()+fpp )
|
||||
{
|
||||
for( fpp_t f = (fpp_t)( ( fl > desiredReleaseFrames() ) ?
|
||||
( qMax( fpp - desiredReleaseFrames(), 0 ) +
|
||||
fl % fpp ) : 0 ); f < frames; ++f )
|
||||
(std::max(fpp - desiredReleaseFrames(), 0) +
|
||||
fl % fpp) : 0); f < frames; ++f)
|
||||
{
|
||||
const float fac = (float)( fl-f-1 ) /
|
||||
desiredReleaseFrames();
|
||||
|
||||
@@ -177,12 +177,11 @@ bool InstrumentFunctionNoteStacking::Chord::hasSemiTone( int8_t semi_tone ) cons
|
||||
|
||||
|
||||
|
||||
InstrumentFunctionNoteStacking::ChordTable::ChordTable() :
|
||||
QVector<Chord>()
|
||||
InstrumentFunctionNoteStacking::ChordTable::ChordTable()
|
||||
{
|
||||
for (const auto& chord : s_initTable)
|
||||
{
|
||||
push_back(Chord(chord.m_name, chord.m_semiTones));
|
||||
m_chords.emplace_back(chord.m_name, chord.m_semiTones);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,10 +190,12 @@ InstrumentFunctionNoteStacking::ChordTable::ChordTable() :
|
||||
|
||||
const InstrumentFunctionNoteStacking::Chord & InstrumentFunctionNoteStacking::ChordTable::getByName( const QString & name, bool is_scale ) const
|
||||
{
|
||||
for( int i = 0; i < size(); i++ )
|
||||
for (const auto& chord : m_chords)
|
||||
{
|
||||
if( at( i ).getName() == name && is_scale == at( i ).isScale() )
|
||||
return at( i );
|
||||
if (chord.getName() == name && is_scale == chord.isScale())
|
||||
{
|
||||
return chord;
|
||||
}
|
||||
}
|
||||
|
||||
static Chord empty;
|
||||
@@ -211,9 +212,10 @@ InstrumentFunctionNoteStacking::InstrumentFunctionNoteStacking( Model * _parent
|
||||
m_chordRangeModel( 1.0f, 1.0f, 9.0f, 1.0f, this, tr( "Chord range" ) )
|
||||
{
|
||||
const ChordTable & chord_table = ChordTable::getInstance();
|
||||
for( int i = 0; i < chord_table.size(); ++i )
|
||||
|
||||
for (const auto& chord : chord_table.chords())
|
||||
{
|
||||
m_chordsModel.addItem( chord_table[i].getName() );
|
||||
m_chordsModel.addItem(chord.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +234,7 @@ void InstrumentFunctionNoteStacking::processNote( NotePlayHandle * _n )
|
||||
// at the same time we only add sub-notes if nothing of the note was
|
||||
// played yet, because otherwise we would add chord-subnotes every
|
||||
// time an audio-buffer is rendered...
|
||||
if( ( _n->origin() == NotePlayHandle::OriginArpeggio || ( _n->hasParent() == false && _n->instrumentTrack()->isArpeggioEnabled() == false ) ) &&
|
||||
if( ( _n->origin() == NotePlayHandle::Origin::Arpeggio || ( _n->hasParent() == false && _n->instrumentTrack()->isArpeggioEnabled() == false ) ) &&
|
||||
_n->totalFramesPlayed() == 0 &&
|
||||
m_chordsEnabledModel.value() == true && ! _n->isReleased() )
|
||||
{
|
||||
@@ -244,10 +246,10 @@ void InstrumentFunctionNoteStacking::processNote( NotePlayHandle * _n )
|
||||
const int sub_note_key_base = base_note_key + octave_cnt * KeysPerOctave;
|
||||
|
||||
// process all notes in the chord
|
||||
for( int i = 0; i < chord_table[selected_chord].size(); ++i )
|
||||
for( int i = 0; i < chord_table.chords()[selected_chord].size(); ++i )
|
||||
{
|
||||
// add interval to sub-note-key
|
||||
const int sub_note_key = sub_note_key_base + (int) chord_table[selected_chord][i];
|
||||
const int sub_note_key = sub_note_key_base + (int) chord_table.chords()[selected_chord][i];
|
||||
// maybe we're out of range -> let's get outta
|
||||
// here!
|
||||
if( sub_note_key > NumKeys )
|
||||
@@ -261,7 +263,7 @@ void InstrumentFunctionNoteStacking::processNote( NotePlayHandle * _n )
|
||||
// different
|
||||
Engine::audioEngine()->addPlayHandle(
|
||||
NotePlayHandleManager::acquire( _n->instrumentTrack(), _n->offset(), _n->frames(), note_copy,
|
||||
_n, -1, NotePlayHandle::OriginNoteStacking )
|
||||
_n, -1, NotePlayHandle::Origin::NoteStacking )
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -309,9 +311,9 @@ InstrumentFunctionArpeggio::InstrumentFunctionArpeggio( Model * _parent ) :
|
||||
m_arpModeModel( this, tr( "Arpeggio mode" ) )
|
||||
{
|
||||
const InstrumentFunctionNoteStacking::ChordTable & chord_table = InstrumentFunctionNoteStacking::ChordTable::getInstance();
|
||||
for( int i = 0; i < chord_table.size(); ++i )
|
||||
for (auto& chord : chord_table.chords())
|
||||
{
|
||||
m_arpModel.addItem( chord_table[i].getName() );
|
||||
m_arpModel.addItem(chord.getName());
|
||||
}
|
||||
|
||||
m_arpDirectionModel.addItem( tr( "Up" ), std::make_unique<PixmapLoader>( "arp_up" ) );
|
||||
@@ -319,7 +321,7 @@ InstrumentFunctionArpeggio::InstrumentFunctionArpeggio( Model * _parent ) :
|
||||
m_arpDirectionModel.addItem( tr( "Up and down" ), std::make_unique<PixmapLoader>( "arp_up_and_down" ) );
|
||||
m_arpDirectionModel.addItem( tr( "Down and up" ), std::make_unique<PixmapLoader>( "arp_up_and_down" ) );
|
||||
m_arpDirectionModel.addItem( tr( "Random" ), std::make_unique<PixmapLoader>( "arp_random" ) );
|
||||
m_arpDirectionModel.setInitValue( ArpDirUp );
|
||||
m_arpDirectionModel.setInitValue( static_cast<float>(ArpDirection::Up) );
|
||||
|
||||
m_arpModeModel.addItem( tr( "Free" ), std::make_unique<PixmapLoader>( "arp_free" ) );
|
||||
m_arpModeModel.addItem( tr( "Sort" ), std::make_unique<PixmapLoader>( "arp_sort" ) );
|
||||
@@ -334,8 +336,8 @@ InstrumentFunctionArpeggio::InstrumentFunctionArpeggio( Model * _parent ) :
|
||||
void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
{
|
||||
const int base_note_key = _n->key();
|
||||
if( _n->origin() == NotePlayHandle::OriginArpeggio ||
|
||||
_n->origin() == NotePlayHandle::OriginNoteStacking ||
|
||||
if( _n->origin() == NotePlayHandle::Origin::Arpeggio ||
|
||||
_n->origin() == NotePlayHandle::Origin::NoteStacking ||
|
||||
!m_arpEnabledModel.value() ||
|
||||
_n->isReleased() )
|
||||
{
|
||||
@@ -349,7 +351,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
|
||||
ConstNotePlayHandleList cnphv = NotePlayHandle::nphsOfInstrumentTrack( _n->instrumentTrack() );
|
||||
|
||||
if( m_arpModeModel.value() != FreeMode && cnphv.size() == 0 )
|
||||
if( static_cast<ArpMode>(m_arpModeModel.value()) != ArpMode::Free && cnphv.size() == 0 )
|
||||
{
|
||||
// maybe we're playing only a preset-preview-note?
|
||||
cnphv = PresetPreviewPlayHandle::nphsOfInstrumentTrack( _n->instrumentTrack() );
|
||||
@@ -362,7 +364,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
}
|
||||
|
||||
const InstrumentFunctionNoteStacking::ChordTable & chord_table = InstrumentFunctionNoteStacking::ChordTable::getInstance();
|
||||
const int cur_chord_size = chord_table[selected_arp].size();
|
||||
const int cur_chord_size = chord_table.chords()[selected_arp].size();
|
||||
const int range = static_cast<int>(cur_chord_size * m_arpRangeModel.value() * m_arpRepeatsModel.value());
|
||||
const int total_range = range * cnphv.size();
|
||||
|
||||
@@ -373,11 +375,11 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
// used for calculating remaining frames for arp-note, we have to add
|
||||
// arp_frames-1, otherwise the first arp-note will not be setup
|
||||
// correctly... -> arp_frames frames silence at the start of every note!
|
||||
int cur_frame = ( ( m_arpModeModel.value() != FreeMode ) ?
|
||||
int cur_frame = ( ( static_cast<ArpMode>(m_arpModeModel.value()) != ArpMode::Free ) ?
|
||||
cnphv.first()->totalFramesPlayed() :
|
||||
_n->totalFramesPlayed() ) + arp_frames - 1;
|
||||
// used for loop
|
||||
f_cnt_t frames_processed = ( m_arpModeModel.value() != FreeMode ) ? cnphv.first()->noteOffset() : _n->noteOffset();
|
||||
f_cnt_t frames_processed = ( static_cast<ArpMode>(m_arpModeModel.value()) != ArpMode::Free ) ? cnphv.first()->noteOffset() : _n->noteOffset();
|
||||
|
||||
while( frames_processed < Engine::audioEngine()->framesPerPeriod() )
|
||||
{
|
||||
@@ -395,7 +397,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
|
||||
// in sorted mode: is it our turn or do we have to be quiet for
|
||||
// now?
|
||||
if( m_arpModeModel.value() == SortMode &&
|
||||
if( static_cast<ArpMode>(m_arpModeModel.value()) == ArpMode::Sort &&
|
||||
( ( cur_frame / arp_frames ) % total_range ) / range != (f_cnt_t) _n->index() )
|
||||
{
|
||||
// update counters
|
||||
@@ -407,7 +409,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
// Skip notes randomly
|
||||
if( m_arpSkipModel.value() )
|
||||
{
|
||||
if( 100 * ( (float) rand() / (float)( RAND_MAX + 1.0f ) ) < m_arpSkipModel.value() )
|
||||
if (100 * static_cast<float>(rand()) / (static_cast<float>(RAND_MAX) + 1.0f) < m_arpSkipModel.value())
|
||||
{
|
||||
// update counters
|
||||
frames_processed += arp_frames;
|
||||
@@ -416,31 +418,31 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
}
|
||||
}
|
||||
|
||||
int dir = m_arpDirectionModel.value();
|
||||
auto dir = static_cast<ArpDirection>(m_arpDirectionModel.value());
|
||||
|
||||
// Miss notes randomly. We intercept int dir and abuse it
|
||||
// after need. :)
|
||||
|
||||
if( m_arpMissModel.value() )
|
||||
{
|
||||
if( 100 * ( (float) rand() / (float)( RAND_MAX + 1.0f ) ) < m_arpMissModel.value() )
|
||||
if (100 * static_cast<float>(rand()) / (static_cast<float>(RAND_MAX) + 1.0f) < m_arpMissModel.value())
|
||||
{
|
||||
dir = ArpDirRandom;
|
||||
dir = ArpDirection::Random;
|
||||
}
|
||||
}
|
||||
|
||||
int cur_arp_idx = 0;
|
||||
// process according to arpeggio-direction...
|
||||
if( dir == ArpDirUp )
|
||||
if( dir == ArpDirection::Up )
|
||||
{
|
||||
cur_arp_idx = ( cur_frame / arp_frames ) % range;
|
||||
}
|
||||
else if( dir == ArpDirDown )
|
||||
else if( dir == ArpDirection::Down )
|
||||
{
|
||||
cur_arp_idx = range - ( cur_frame / arp_frames ) %
|
||||
range - 1;
|
||||
}
|
||||
else if( dir == ArpDirUpAndDown && range > 1 )
|
||||
else if( dir == ArpDirection::UpAndDown && range > 1 )
|
||||
{
|
||||
// imagine, we had to play the arp once up and then
|
||||
// once down -> makes 2 * range possible notes...
|
||||
@@ -454,9 +456,9 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
cur_arp_idx = range - cur_arp_idx % ( range - 1 ) - 1;
|
||||
}
|
||||
}
|
||||
else if( dir == ArpDirDownAndUp && range > 1 )
|
||||
else if( dir == ArpDirection::DownAndUp && range > 1 )
|
||||
{
|
||||
// copied from ArpDirUpAndDown above
|
||||
// copied from ArpDirection::UpAndDown above
|
||||
cur_arp_idx = ( cur_frame / arp_frames ) % ( range * 2 - 2 );
|
||||
// if greater than range, we have to play down...
|
||||
// looks like the code for arp_dir==DOWN... :)
|
||||
@@ -467,7 +469,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
// inverts direction
|
||||
cur_arp_idx = range - cur_arp_idx - 1;
|
||||
}
|
||||
else if( dir == ArpDirRandom )
|
||||
else if( dir == ArpDirection::Random )
|
||||
{
|
||||
// just pick a random chord-index
|
||||
cur_arp_idx = (int)( range * ( (float) rand() / (float) RAND_MAX ) );
|
||||
@@ -477,7 +479,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
cur_arp_idx = static_cast<int>(cur_arp_idx / m_arpRepeatsModel.value());
|
||||
|
||||
// Cycle notes
|
||||
if( m_arpCycleModel.value() && dir != ArpDirRandom )
|
||||
if( m_arpCycleModel.value() && dir != ArpDirection::Random )
|
||||
{
|
||||
cur_arp_idx *= m_arpCycleModel.value() + 1;
|
||||
cur_arp_idx %= static_cast<int>( range / m_arpRepeatsModel.value() );
|
||||
@@ -485,7 +487,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
|
||||
// now calculate final key for our arp-note
|
||||
const int sub_note_key = base_note_key + (cur_arp_idx / cur_chord_size ) *
|
||||
KeysPerOctave + chord_table[selected_arp][cur_arp_idx % cur_chord_size];
|
||||
KeysPerOctave + chord_table.chords()[selected_arp][cur_arp_idx % cur_chord_size];
|
||||
|
||||
// range-checking
|
||||
if( sub_note_key >= NumKeys ||
|
||||
@@ -505,7 +507,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n )
|
||||
gated_frames,
|
||||
Note( TimePos( 0 ), TimePos( 0 ), sub_note_key, _n->getVolume(),
|
||||
_n->getPanning(), _n->detuning() ),
|
||||
_n, -1, NotePlayHandle::OriginArpeggio )
|
||||
_n, -1, NotePlayHandle::Origin::Arpeggio )
|
||||
);
|
||||
|
||||
// update counters
|
||||
|
||||
@@ -24,18 +24,57 @@
|
||||
|
||||
|
||||
#include "InstrumentPlayHandle.h"
|
||||
#include "Instrument.h"
|
||||
#include "InstrumentTrack.h"
|
||||
#include "Engine.h"
|
||||
#include "AudioEngine.h"
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
InstrumentPlayHandle::InstrumentPlayHandle( Instrument * instrument, InstrumentTrack* instrumentTrack ) :
|
||||
PlayHandle( TypeInstrumentPlayHandle ),
|
||||
m_instrument( instrument )
|
||||
InstrumentPlayHandle::InstrumentPlayHandle(Instrument * instrument, InstrumentTrack* instrumentTrack) :
|
||||
PlayHandle(Type::InstrumentPlayHandle),
|
||||
m_instrument(instrument)
|
||||
{
|
||||
setAudioPort( instrumentTrack->audioPort() );
|
||||
setAudioPort(instrumentTrack->audioPort());
|
||||
}
|
||||
|
||||
void InstrumentPlayHandle::play(sampleFrame * working_buffer)
|
||||
{
|
||||
InstrumentTrack * instrumentTrack = m_instrument->instrumentTrack();
|
||||
|
||||
// ensure that all our nph's have been processed first
|
||||
auto nphv = NotePlayHandle::nphsOfInstrumentTrack(instrumentTrack, true);
|
||||
|
||||
bool nphsLeft;
|
||||
do
|
||||
{
|
||||
nphsLeft = false;
|
||||
for (const NotePlayHandle * constNotePlayHandle : nphv)
|
||||
{
|
||||
if (constNotePlayHandle->state() != ThreadableJob::ProcessingState::Done &&
|
||||
!constNotePlayHandle->isFinished())
|
||||
{
|
||||
nphsLeft = true;
|
||||
NotePlayHandle * notePlayHandle = const_cast<NotePlayHandle *>(constNotePlayHandle);
|
||||
notePlayHandle->process();
|
||||
}
|
||||
}
|
||||
}
|
||||
while (nphsLeft);
|
||||
|
||||
m_instrument->play(working_buffer);
|
||||
|
||||
// Process the audio buffer that the instrument has just worked on...
|
||||
const fpp_t frames = Engine::audioEngine()->framesPerPeriod();
|
||||
instrumentTrack->processAudioBuffer(working_buffer, frames, nullptr);
|
||||
}
|
||||
|
||||
bool InstrumentPlayHandle::isFromTrack(const Track* track) const
|
||||
{
|
||||
return m_instrument->isFromTrack(track);
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
} // namespace lmms
|
||||
|
||||
@@ -69,7 +69,7 @@ InstrumentSoundShaping::InstrumentSoundShaping(
|
||||
for( int i = 0; i < NumTargets; ++i )
|
||||
{
|
||||
float value_for_zero_amount = 0.0;
|
||||
if( i == Volume )
|
||||
if( static_cast<Target>(i) == Target::Volume )
|
||||
{
|
||||
value_for_zero_amount = 1.0;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ float InstrumentSoundShaping::volumeLevel( NotePlayHandle* n, const f_cnt_t fram
|
||||
}
|
||||
|
||||
float level;
|
||||
m_envLfoParameters[Volume]->fillLevel( &level, frame, envReleaseBegin, 1 );
|
||||
m_envLfoParameters[static_cast<std::size_t>(Target::Volume)]->fillLevel( &level, frame, envReleaseBegin, 1 );
|
||||
|
||||
return level;
|
||||
}
|
||||
@@ -160,22 +160,22 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer,
|
||||
{
|
||||
n->m_filter = std::make_unique<BasicFilters<>>( Engine::audioEngine()->processingSampleRate() );
|
||||
}
|
||||
n->m_filter->setFilterType( m_filterModel.value() );
|
||||
n->m_filter->setFilterType( static_cast<BasicFilters<>::FilterType>(m_filterModel.value()) );
|
||||
|
||||
if( m_envLfoParameters[Cut]->isUsed() )
|
||||
if( m_envLfoParameters[static_cast<std::size_t>(Target::Cut)]->isUsed() )
|
||||
{
|
||||
m_envLfoParameters[Cut]->fillLevel( cutBuffer.data(), envTotalFrames, envReleaseBegin, frames );
|
||||
m_envLfoParameters[static_cast<std::size_t>(Target::Cut)]->fillLevel( cutBuffer.data(), envTotalFrames, envReleaseBegin, frames );
|
||||
}
|
||||
if( m_envLfoParameters[Resonance]->isUsed() )
|
||||
if( m_envLfoParameters[static_cast<std::size_t>(Target::Resonance)]->isUsed() )
|
||||
{
|
||||
m_envLfoParameters[Resonance]->fillLevel( resBuffer.data(), envTotalFrames, envReleaseBegin, frames );
|
||||
m_envLfoParameters[static_cast<std::size_t>(Target::Resonance)]->fillLevel( resBuffer.data(), envTotalFrames, envReleaseBegin, frames );
|
||||
}
|
||||
|
||||
const float fcv = m_filterCutModel.value();
|
||||
const float frv = m_filterResModel.value();
|
||||
|
||||
if( m_envLfoParameters[Cut]->isUsed() &&
|
||||
m_envLfoParameters[Resonance]->isUsed() )
|
||||
if( m_envLfoParameters[static_cast<std::size_t>(Target::Cut)]->isUsed() &&
|
||||
m_envLfoParameters[static_cast<std::size_t>(Target::Resonance)]->isUsed() )
|
||||
{
|
||||
for( fpp_t frame = 0; frame < frames; ++frame )
|
||||
{
|
||||
@@ -196,7 +196,7 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer,
|
||||
buffer[frame][1] = n->m_filter->update( buffer[frame][1], 1 );
|
||||
}
|
||||
}
|
||||
else if( m_envLfoParameters[Cut]->isUsed() )
|
||||
else if( m_envLfoParameters[static_cast<std::size_t>(Target::Cut)]->isUsed() )
|
||||
{
|
||||
for( fpp_t frame = 0; frame < frames; ++frame )
|
||||
{
|
||||
@@ -213,7 +213,7 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer,
|
||||
buffer[frame][1] = n->m_filter->update( buffer[frame][1], 1 );
|
||||
}
|
||||
}
|
||||
else if( m_envLfoParameters[Resonance]->isUsed() )
|
||||
else if( m_envLfoParameters[static_cast<std::size_t>(Target::Resonance)]->isUsed() )
|
||||
{
|
||||
for( fpp_t frame = 0; frame < frames; ++frame )
|
||||
{
|
||||
@@ -241,10 +241,10 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer,
|
||||
}
|
||||
}
|
||||
|
||||
if( m_envLfoParameters[Volume]->isUsed() )
|
||||
if( m_envLfoParameters[static_cast<std::size_t>(Target::Volume)]->isUsed() )
|
||||
{
|
||||
QVarLengthArray<float> volBuffer(frames);
|
||||
m_envLfoParameters[Volume]->fillLevel( volBuffer.data(), envTotalFrames, envReleaseBegin, frames );
|
||||
m_envLfoParameters[static_cast<std::size_t>(Target::Volume)]->fillLevel( volBuffer.data(), envTotalFrames, envReleaseBegin, frames );
|
||||
|
||||
for( fpp_t frame = 0; frame < frames; ++frame )
|
||||
{
|
||||
@@ -255,7 +255,7 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer,
|
||||
}
|
||||
}
|
||||
|
||||
/* else if( m_envLfoParameters[Volume]->isUsed() == false && m_envLfoParameters[PANNING]->isUsed() )
|
||||
/* else if( m_envLfoParameters[static_cast<std::size_t>(Target::Volume)]->isUsed() == false && m_envLfoParameters[PANNING]->isUsed() )
|
||||
{
|
||||
// only use panning-envelope...
|
||||
for( fpp_t frame = 0; frame < frames; ++frame )
|
||||
@@ -275,11 +275,11 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer,
|
||||
|
||||
f_cnt_t InstrumentSoundShaping::envFrames( const bool _only_vol ) const
|
||||
{
|
||||
f_cnt_t ret_val = m_envLfoParameters[Volume]->PAHD_Frames();
|
||||
f_cnt_t ret_val = m_envLfoParameters[static_cast<std::size_t>(Target::Volume)]->PAHD_Frames();
|
||||
|
||||
if( _only_vol == false )
|
||||
{
|
||||
for( int i = Volume+1; i < NumTargets; ++i )
|
||||
for( int i = static_cast<std::size_t>(Target::Volume)+1; i < NumTargets; ++i )
|
||||
{
|
||||
if( m_envLfoParameters[i]->isUsed() &&
|
||||
m_envLfoParameters[i]->PAHD_Frames() > ret_val )
|
||||
@@ -303,21 +303,21 @@ f_cnt_t InstrumentSoundShaping::releaseFrames() const
|
||||
|
||||
f_cnt_t ret_val = m_instrumentTrack->instrument()->desiredReleaseFrames();
|
||||
|
||||
if( m_instrumentTrack->instrument()->flags().testFlag( Instrument::IsSingleStreamed ) )
|
||||
if( m_instrumentTrack->instrument()->flags().testFlag( Instrument::Flag::IsSingleStreamed ) )
|
||||
{
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
if( m_envLfoParameters[Volume]->isUsed() )
|
||||
if( m_envLfoParameters[static_cast<std::size_t>(Target::Volume)]->isUsed() )
|
||||
{
|
||||
return m_envLfoParameters[Volume]->releaseFrames();
|
||||
return m_envLfoParameters[static_cast<std::size_t>(Target::Volume)]->releaseFrames();
|
||||
}
|
||||
|
||||
for( int i = Volume+1; i < NumTargets; ++i )
|
||||
for( int i = static_cast<std::size_t>(Target::Volume)+1; i < NumTargets; ++i )
|
||||
{
|
||||
if( m_envLfoParameters[i]->isUsed() )
|
||||
{
|
||||
ret_val = qMax( ret_val, m_envLfoParameters[i]->releaseFrames() );
|
||||
ret_val = std::max(ret_val, m_envLfoParameters[i]->releaseFrames());
|
||||
}
|
||||
}
|
||||
return ret_val;
|
||||
|
||||
@@ -40,12 +40,12 @@ Ladspa2LMMS::Ladspa2LMMS()
|
||||
ladspa_key_t key = plugin.second;
|
||||
LadspaManagerDescription * desc = getDescription( key );
|
||||
|
||||
if( desc->type == SOURCE )
|
||||
if( desc->type == LadspaPluginType::Source )
|
||||
{
|
||||
m_instruments.append( qMakePair( getName( key ),
|
||||
key ) );
|
||||
}
|
||||
else if( desc->type == TRANSFER &&
|
||||
else if( desc->type == LadspaPluginType::Transfer &&
|
||||
( desc->inputChannels == desc->outputChannels &&
|
||||
( desc->inputChannels == 1 ||
|
||||
desc->inputChannels == 2 ||
|
||||
@@ -55,7 +55,7 @@ Ladspa2LMMS::Ladspa2LMMS()
|
||||
m_validEffects.append( qMakePair( getName( key ),
|
||||
key ) );
|
||||
}
|
||||
else if( desc->type == TRANSFER &&
|
||||
else if( desc->type == LadspaPluginType::Transfer &&
|
||||
( desc->inputChannels != desc->outputChannels ||
|
||||
( desc->inputChannels != 1 &&
|
||||
desc->inputChannels != 2 &&
|
||||
@@ -65,12 +65,12 @@ Ladspa2LMMS::Ladspa2LMMS()
|
||||
m_invalidEffects.append( qMakePair( getName( key ),
|
||||
key ) );
|
||||
}
|
||||
else if( desc->type == SINK )
|
||||
else if( desc->type == LadspaPluginType::Sink )
|
||||
{
|
||||
m_analysisTools.append( qMakePair( getName( key ),
|
||||
key ) );
|
||||
}
|
||||
else if( desc->type == OTHER )
|
||||
else if( desc->type == LadspaPluginType::Other )
|
||||
{
|
||||
m_otherPlugins.append( qMakePair( getName( key ),
|
||||
key ) );
|
||||
|
||||
@@ -53,7 +53,7 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port,
|
||||
|
||||
switch( m_port->data_type )
|
||||
{
|
||||
case TOGGLED:
|
||||
case BufferDataType::Toggled:
|
||||
m_toggledModel.setInitValue(
|
||||
static_cast<bool>( m_port->def ) );
|
||||
connect( &m_toggledModel, SIGNAL(dataChanged()),
|
||||
@@ -66,8 +66,8 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port,
|
||||
m_toggledModel.setScaleLogarithmic( m_port->suggests_logscale );
|
||||
break;
|
||||
|
||||
case INTEGER:
|
||||
case ENUM:
|
||||
case BufferDataType::Integer:
|
||||
case BufferDataType::Enum:
|
||||
m_knobModel.setRange( static_cast<int>( m_port->max ),
|
||||
static_cast<int>( m_port->min ),
|
||||
1 + static_cast<int>( m_port->max -
|
||||
@@ -80,7 +80,7 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port,
|
||||
m_knobModel.setScaleLogarithmic( m_port->suggests_logscale );
|
||||
break;
|
||||
|
||||
case FLOATING:
|
||||
case BufferDataType::Floating:
|
||||
m_knobModel.setRange( m_port->min, m_port->max,
|
||||
( m_port->max - m_port->min )
|
||||
/ ( m_port->name.toUpper() == "GAIN"
|
||||
@@ -93,7 +93,7 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port,
|
||||
m_knobModel.setScaleLogarithmic( m_port->suggests_logscale );
|
||||
break;
|
||||
|
||||
case TIME:
|
||||
case BufferDataType::Time:
|
||||
m_tempoSyncKnobModel.setRange( m_port->min, m_port->max,
|
||||
( m_port->max -
|
||||
m_port->min ) / 800.0f );
|
||||
@@ -116,13 +116,13 @@ LADSPA_Data LadspaControl::value()
|
||||
{
|
||||
switch( m_port->data_type )
|
||||
{
|
||||
case TOGGLED:
|
||||
case BufferDataType::Toggled:
|
||||
return static_cast<LADSPA_Data>( m_toggledModel.value() );
|
||||
case INTEGER:
|
||||
case ENUM:
|
||||
case FLOATING:
|
||||
case BufferDataType::Integer:
|
||||
case BufferDataType::Enum:
|
||||
case BufferDataType::Floating:
|
||||
return static_cast<LADSPA_Data>( m_knobModel.value() );
|
||||
case TIME:
|
||||
case BufferDataType::Time:
|
||||
return static_cast<LADSPA_Data>( m_tempoSyncKnobModel.value() );
|
||||
default:
|
||||
qWarning( "LadspaControl::value(): BAD BAD BAD\n" );
|
||||
@@ -137,13 +137,13 @@ ValueBuffer * LadspaControl::valueBuffer()
|
||||
{
|
||||
switch( m_port->data_type )
|
||||
{
|
||||
case TOGGLED:
|
||||
case INTEGER:
|
||||
case ENUM:
|
||||
case BufferDataType::Toggled:
|
||||
case BufferDataType::Integer:
|
||||
case BufferDataType::Enum:
|
||||
return nullptr;
|
||||
case FLOATING:
|
||||
case BufferDataType::Floating:
|
||||
return m_knobModel.valueBuffer();
|
||||
case TIME:
|
||||
case BufferDataType::Time:
|
||||
return m_tempoSyncKnobModel.valueBuffer();
|
||||
default:
|
||||
qWarning( "LadspaControl::valueBuffer(): BAD BAD BAD\n" );
|
||||
@@ -159,17 +159,17 @@ void LadspaControl::setValue( LADSPA_Data _value )
|
||||
{
|
||||
switch( m_port->data_type )
|
||||
{
|
||||
case TOGGLED:
|
||||
case BufferDataType::Toggled:
|
||||
m_toggledModel.setValue( static_cast<bool>( _value ) );
|
||||
break;
|
||||
case INTEGER:
|
||||
case ENUM:
|
||||
case BufferDataType::Integer:
|
||||
case BufferDataType::Enum:
|
||||
m_knobModel.setValue( static_cast<int>( _value ) );
|
||||
break;
|
||||
case FLOATING:
|
||||
case BufferDataType::Floating:
|
||||
m_knobModel.setValue( static_cast<float>( _value ) );
|
||||
break;
|
||||
case TIME:
|
||||
case BufferDataType::Time:
|
||||
m_tempoSyncKnobModel.setValue( static_cast<float>(
|
||||
_value ) );
|
||||
break;
|
||||
@@ -194,15 +194,15 @@ void LadspaControl::saveSettings( QDomDocument& doc,
|
||||
}
|
||||
switch( m_port->data_type )
|
||||
{
|
||||
case TOGGLED:
|
||||
case BufferDataType::Toggled:
|
||||
m_toggledModel.saveSettings( doc, e, "data" );
|
||||
break;
|
||||
case INTEGER:
|
||||
case ENUM:
|
||||
case FLOATING:
|
||||
case BufferDataType::Integer:
|
||||
case BufferDataType::Enum:
|
||||
case BufferDataType::Floating:
|
||||
m_knobModel.saveSettings( doc, e, "data" );
|
||||
break;
|
||||
case TIME:
|
||||
case BufferDataType::Time:
|
||||
m_tempoSyncKnobModel.saveSettings( doc, e, "data" );
|
||||
break;
|
||||
default:
|
||||
@@ -230,15 +230,15 @@ void LadspaControl::loadSettings( const QDomElement& parent, const QString& name
|
||||
m_linkEnabledModel.setValue(m_linkEnabledModel.initValue());
|
||||
switch( m_port->data_type )
|
||||
{
|
||||
case TOGGLED:
|
||||
case BufferDataType::Toggled:
|
||||
m_toggledModel.setValue(m_toggledModel.initValue());
|
||||
break;
|
||||
case INTEGER:
|
||||
case ENUM:
|
||||
case FLOATING:
|
||||
case BufferDataType::Integer:
|
||||
case BufferDataType::Enum:
|
||||
case BufferDataType::Floating:
|
||||
m_knobModel.setValue(m_knobModel.initValue());
|
||||
break;
|
||||
case TIME:
|
||||
case BufferDataType::Time:
|
||||
m_tempoSyncKnobModel.setValue(m_tempoSyncKnobModel.initValue());
|
||||
break;
|
||||
default:
|
||||
@@ -265,15 +265,15 @@ void LadspaControl::loadSettings( const QDomElement& parent, const QString& name
|
||||
|
||||
switch( m_port->data_type )
|
||||
{
|
||||
case TOGGLED:
|
||||
case BufferDataType::Toggled:
|
||||
m_toggledModel.loadSettings( e, dataModelName );
|
||||
break;
|
||||
case INTEGER:
|
||||
case ENUM:
|
||||
case FLOATING:
|
||||
case BufferDataType::Integer:
|
||||
case BufferDataType::Enum:
|
||||
case BufferDataType::Floating:
|
||||
m_knobModel.loadSettings( e, dataModelName );
|
||||
break;
|
||||
case TIME:
|
||||
case BufferDataType::Time:
|
||||
m_tempoSyncKnobModel.loadSettings( e, dataModelName );
|
||||
break;
|
||||
default:
|
||||
@@ -290,15 +290,15 @@ void LadspaControl::linkControls( LadspaControl * _control )
|
||||
{
|
||||
switch( m_port->data_type )
|
||||
{
|
||||
case TOGGLED:
|
||||
case BufferDataType::Toggled:
|
||||
BoolModel::linkModels( &m_toggledModel, _control->toggledModel() );
|
||||
break;
|
||||
case INTEGER:
|
||||
case ENUM:
|
||||
case FLOATING:
|
||||
case BufferDataType::Integer:
|
||||
case BufferDataType::Enum:
|
||||
case BufferDataType::Floating:
|
||||
FloatModel::linkModels( &m_knobModel, _control->knobModel() );
|
||||
break;
|
||||
case TIME:
|
||||
case BufferDataType::Time:
|
||||
TempoSyncKnobModel::linkModels( &m_tempoSyncKnobModel,
|
||||
_control->tempoSyncKnobModel() );
|
||||
break;
|
||||
@@ -341,15 +341,15 @@ void LadspaControl::unlinkControls( LadspaControl * _control )
|
||||
{
|
||||
switch( m_port->data_type )
|
||||
{
|
||||
case TOGGLED:
|
||||
case BufferDataType::Toggled:
|
||||
BoolModel::unlinkModels( &m_toggledModel, _control->toggledModel() );
|
||||
break;
|
||||
case INTEGER:
|
||||
case ENUM:
|
||||
case FLOATING:
|
||||
case BufferDataType::Integer:
|
||||
case BufferDataType::Enum:
|
||||
case BufferDataType::Floating:
|
||||
FloatModel::unlinkModels( &m_knobModel, _control->knobModel() );
|
||||
break;
|
||||
case TIME:
|
||||
case BufferDataType::Time:
|
||||
TempoSyncKnobModel::unlinkModels( &m_tempoSyncKnobModel,
|
||||
_control->tempoSyncKnobModel() );
|
||||
break;
|
||||
|
||||
@@ -159,21 +159,21 @@ void LadspaManager::addPlugins(
|
||||
|
||||
if( plugIn->inputChannels == 0 && plugIn->outputChannels > 0 )
|
||||
{
|
||||
plugIn->type = SOURCE;
|
||||
plugIn->type = LadspaPluginType::Source;
|
||||
}
|
||||
else if( plugIn->inputChannels > 0 &&
|
||||
plugIn->outputChannels > 0 )
|
||||
{
|
||||
plugIn->type = TRANSFER;
|
||||
plugIn->type = LadspaPluginType::Transfer;
|
||||
}
|
||||
else if( plugIn->inputChannels > 0 &&
|
||||
plugIn->outputChannels == 0 )
|
||||
{
|
||||
plugIn->type = SINK;
|
||||
plugIn->type = LadspaPluginType::Sink;
|
||||
}
|
||||
else
|
||||
{
|
||||
plugIn->type = OTHER;
|
||||
plugIn->type = LadspaPluginType::Other;
|
||||
}
|
||||
|
||||
m_ladspaManagerMap[key] = plugIn;
|
||||
|
||||
@@ -36,12 +36,12 @@ namespace lmms
|
||||
|
||||
|
||||
LfoController::LfoController( Model * _parent ) :
|
||||
Controller( Controller::LfoController, _parent, tr( "LFO Controller" ) ),
|
||||
Controller( ControllerType::Lfo, _parent, tr( "LFO Controller" ) ),
|
||||
m_baseModel( 0.5, 0.0, 1.0, 0.001, this, tr( "Base value" ) ),
|
||||
m_speedModel( 2.0, 0.01, 20.0, 0.0001, 20000.0, this, tr( "Oscillator speed" ) ),
|
||||
m_amountModel( 1.0, -1.0, 1.0, 0.005, this, tr( "Oscillator amount" ) ),
|
||||
m_phaseModel( 0.0, 0.0, 360.0, 4.0, this, tr( "Oscillator phase" ) ),
|
||||
m_waveModel( Oscillator::SineWave, 0, Oscillator::NumWaveShapes,
|
||||
m_waveModel( static_cast<int>(Oscillator::WaveShape::Sine), 0, Oscillator::NumWaveShapes,
|
||||
this, tr( "Oscillator waveform" ) ),
|
||||
m_multiplierModel( 0, 0, 2, this, tr( "Frequency Multiplier" ) ),
|
||||
m_duration( 1000 ),
|
||||
@@ -88,6 +88,7 @@ void LfoController::updateValueBuffer()
|
||||
{
|
||||
m_phaseOffset = m_phaseModel.value() / 360.0;
|
||||
float phase = m_currentPhase + m_phaseOffset;
|
||||
float phasePrev = 0.0f;
|
||||
|
||||
// roll phase up until we're in sync with period counter
|
||||
m_bufferLastUpdated++;
|
||||
@@ -102,20 +103,45 @@ void LfoController::updateValueBuffer()
|
||||
ValueBuffer *amountBuffer = m_amountModel.valueBuffer();
|
||||
int amountInc = amountBuffer ? 1 : 0;
|
||||
float *amountPtr = amountBuffer ? &(amountBuffer->values()[ 0 ] ) : &amount;
|
||||
Oscillator::WaveShape waveshape = static_cast<Oscillator::WaveShape>(m_waveModel.value());
|
||||
|
||||
for( float& f : m_valueBuffer )
|
||||
{
|
||||
const float currentSample = m_sampleFunction != nullptr
|
||||
? m_sampleFunction( phase )
|
||||
: m_userDefSampleBuffer->userWaveSample( phase );
|
||||
float currentSample = 0;
|
||||
switch (waveshape)
|
||||
{
|
||||
case Oscillator::WaveShape::WhiteNoise:
|
||||
{
|
||||
if (absFraction(phase) < absFraction(phasePrev))
|
||||
{
|
||||
// Resample when phase period has completed
|
||||
m_heldSample = m_sampleFunction(phase);
|
||||
}
|
||||
currentSample = m_heldSample;
|
||||
break;
|
||||
}
|
||||
case Oscillator::WaveShape::UserDefined:
|
||||
{
|
||||
currentSample = m_userDefSampleBuffer->userWaveSample(phase);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (m_sampleFunction != nullptr)
|
||||
{
|
||||
currentSample = m_sampleFunction(phase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f = qBound( 0.0f, m_baseModel.value() + ( *amountPtr * currentSample / 2.0f ), 1.0f );
|
||||
f = std::clamp(m_baseModel.value() + (*amountPtr * currentSample / 2.0f), 0.0f, 1.0f);
|
||||
|
||||
phasePrev = phase;
|
||||
phase += 1.0 / m_duration;
|
||||
amountPtr += amountInc;
|
||||
}
|
||||
|
||||
m_currentPhase = absFraction( phase - m_phaseOffset );
|
||||
m_currentPhase = absFraction(phase - m_phaseOffset);
|
||||
m_bufferLastUpdated = s_periods;
|
||||
}
|
||||
|
||||
@@ -149,30 +175,31 @@ void LfoController::updateDuration()
|
||||
|
||||
void LfoController::updateSampleFunction()
|
||||
{
|
||||
switch( m_waveModel.value() )
|
||||
switch( static_cast<Oscillator::WaveShape>(m_waveModel.value()) )
|
||||
{
|
||||
case Oscillator::SineWave:
|
||||
case Oscillator::WaveShape::Sine:
|
||||
default:
|
||||
m_sampleFunction = &Oscillator::sinSample;
|
||||
break;
|
||||
case Oscillator::TriangleWave:
|
||||
case Oscillator::WaveShape::Triangle:
|
||||
m_sampleFunction = &Oscillator::triangleSample;
|
||||
break;
|
||||
case Oscillator::SawWave:
|
||||
case Oscillator::WaveShape::Saw:
|
||||
m_sampleFunction = &Oscillator::sawSample;
|
||||
break;
|
||||
case Oscillator::SquareWave:
|
||||
case Oscillator::WaveShape::Square:
|
||||
m_sampleFunction = &Oscillator::squareSample;
|
||||
break;
|
||||
case Oscillator::MoogSawWave:
|
||||
case Oscillator::WaveShape::MoogSaw:
|
||||
m_sampleFunction = &Oscillator::moogSawSample;
|
||||
break;
|
||||
case Oscillator::ExponentialWave:
|
||||
case Oscillator::WaveShape::Exponential:
|
||||
m_sampleFunction = &Oscillator::expSample;
|
||||
break;
|
||||
case Oscillator::WhiteNoise:
|
||||
case Oscillator::WaveShape::WhiteNoise:
|
||||
m_sampleFunction = &Oscillator::noiseSample;
|
||||
break;
|
||||
case Oscillator::UserDefinedWave:
|
||||
case Oscillator::WaveShape::UserDefined:
|
||||
m_sampleFunction = nullptr;
|
||||
/*TODO: If C++11 is allowed, should change the type of
|
||||
m_sampleFunction be std::function<sample_t(const float)>
|
||||
|
||||
143
src/core/LmmsSemaphore.cpp
Normal file
143
src/core/LmmsSemaphore.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Semaphore.cpp - Semaphore implementation
|
||||
*
|
||||
* Copyright (c) 2022-2022 Johannes Lorenz <jlsf2013$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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code has been copied and adapted from https://github.com/drobilla/jalv
|
||||
* File src/zix/sem.h
|
||||
*/
|
||||
|
||||
#include "LmmsSemaphore.h"
|
||||
|
||||
#if defined(LMMS_BUILD_WIN32)
|
||||
# include <limits.h>
|
||||
#else
|
||||
# include <errno.h>
|
||||
#endif
|
||||
|
||||
#include <system_error>
|
||||
|
||||
namespace lmms {
|
||||
|
||||
#ifdef LMMS_BUILD_APPLE
|
||||
Semaphore::Semaphore(unsigned val)
|
||||
{
|
||||
kern_return_t rval = semaphore_create(mach_task_self(), &m_sem, SYNC_POLICY_FIFO, val);
|
||||
if(rval != 0) {
|
||||
throw std::system_error(rval, std::system_category(), "Could not create semaphore");
|
||||
}
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore()
|
||||
{
|
||||
semaphore_destroy(mach_task_self(), m_sem);
|
||||
}
|
||||
|
||||
void Semaphore::post()
|
||||
{
|
||||
semaphore_signal(m_sem);
|
||||
}
|
||||
|
||||
void Semaphore::wait()
|
||||
{
|
||||
kern_return_t rval = semaphore_wait(m_sem);
|
||||
if (rval != KERN_SUCCESS) {
|
||||
throw std::system_error(rval, std::system_category(), "Waiting for semaphore failed");
|
||||
}
|
||||
}
|
||||
|
||||
bool Semaphore::tryWait()
|
||||
{
|
||||
const mach_timespec_t zero = { 0, 0 };
|
||||
return semaphore_timedwait(m_sem, zero) == KERN_SUCCESS;
|
||||
}
|
||||
|
||||
#elif defined(LMMS_BUILD_WIN32)
|
||||
|
||||
Semaphore::Semaphore(unsigned initial)
|
||||
{
|
||||
if(CreateSemaphore(nullptr, initial, LONG_MAX, nullptr) == nullptr) {
|
||||
throw std::system_error(GetLastError(), std::system_category(), "Could not create semaphore");
|
||||
}
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore()
|
||||
{
|
||||
CloseHandle(m_sem);
|
||||
}
|
||||
|
||||
void Semaphore::post()
|
||||
{
|
||||
ReleaseSemaphore(m_sem, 1, nullptr);
|
||||
}
|
||||
|
||||
void Semaphore::wait()
|
||||
{
|
||||
if (WaitForSingleObject(m_sem, INFINITE) != WAIT_OBJECT_0) {
|
||||
throw std::system_error(GetLastError(), std::system_category(), "Waiting for semaphore failed");
|
||||
}
|
||||
}
|
||||
|
||||
bool Semaphore::tryWait()
|
||||
{
|
||||
return WaitForSingleObject(m_sem, 0) == WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
#else /* !defined(LMMS_BUILD_APPLE) && !defined(LMMS_BUILD_WIN32) */
|
||||
|
||||
Semaphore::Semaphore(unsigned initial)
|
||||
{
|
||||
if(sem_init(&m_sem, 0, initial) != 0) {
|
||||
throw std::system_error(errno, std::generic_category(), "Could not create semaphore");
|
||||
}
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore()
|
||||
{
|
||||
sem_destroy(&m_sem);
|
||||
}
|
||||
|
||||
void Semaphore::post()
|
||||
{
|
||||
sem_post(&m_sem);
|
||||
}
|
||||
|
||||
void Semaphore::wait()
|
||||
{
|
||||
while (sem_wait(&m_sem) != 0) {
|
||||
if (errno != EINTR) {
|
||||
throw std::system_error(errno, std::generic_category(), "Waiting for semaphore failed");
|
||||
}
|
||||
/* Otherwise, interrupted, so try again. */
|
||||
}
|
||||
}
|
||||
|
||||
bool Semaphore::tryWait()
|
||||
{
|
||||
return (sem_trywait(&m_sem) == 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
@@ -121,7 +121,7 @@ bool sanitize( sampleFrame * src, int frames )
|
||||
}
|
||||
else
|
||||
{
|
||||
src[f][c] = qBound( -1000.0f, src[f][c], 1000.0f );
|
||||
src[f][c] = std::clamp(src[f][c], -1000.0f, 1000.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,8 +173,8 @@ void MixerChannel::doProcessing()
|
||||
m_stillRunning = m_fxChain.processAudioBuffer( m_buffer, fpp, m_hasInput );
|
||||
|
||||
AudioEngine::StereoSample peakSamples = Engine::audioEngine()->getPeakValues(m_buffer, fpp);
|
||||
m_peakLeft = qMax( m_peakLeft, peakSamples.left * v );
|
||||
m_peakRight = qMax( m_peakRight, peakSamples.right * v );
|
||||
m_peakLeft = std::max(m_peakLeft, peakSamples.left * v);
|
||||
m_peakRight = std::max(m_peakRight, peakSamples.right * v);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -201,9 +201,9 @@ Mixer::Mixer() :
|
||||
|
||||
Mixer::~Mixer()
|
||||
{
|
||||
while( ! m_mixerRoutes.isEmpty() )
|
||||
while (!m_mixerRoutes.empty())
|
||||
{
|
||||
deleteChannelSend( m_mixerRoutes.first() );
|
||||
deleteChannelSend(m_mixerRoutes.front());
|
||||
}
|
||||
while( m_mixerChannels.size() )
|
||||
{
|
||||
@@ -293,12 +293,15 @@ void Mixer::deleteChannel( int index )
|
||||
|
||||
// go through every instrument and adjust for the channel index change
|
||||
TrackContainer::TrackList tracks;
|
||||
tracks += Engine::getSong()->tracks();
|
||||
tracks += Engine::patternStore()->tracks();
|
||||
|
||||
auto& songTracks = Engine::getSong()->tracks();
|
||||
auto& patternStoreTracks = Engine::patternStore()->tracks();
|
||||
tracks.insert(tracks.end(), songTracks.begin(), songTracks.end());
|
||||
tracks.insert(tracks.end(), patternStoreTracks.begin(), patternStoreTracks.end());
|
||||
|
||||
for( Track* t : tracks )
|
||||
{
|
||||
if( t->type() == Track::InstrumentTrack )
|
||||
if( t->type() == Track::Type::Instrument )
|
||||
{
|
||||
auto inst = dynamic_cast<InstrumentTrack*>(t);
|
||||
int val = inst->mixerChannelModel()->value(0);
|
||||
@@ -314,7 +317,7 @@ void Mixer::deleteChannel( int index )
|
||||
inst->mixerChannelModel()->setValue(val-1);
|
||||
}
|
||||
}
|
||||
else if( t->type() == Track::SampleTrack )
|
||||
else if( t->type() == Track::Type::Sample )
|
||||
{
|
||||
auto strk = dynamic_cast<SampleTrack*>(t);
|
||||
int val = strk->mixerChannelModel()->value(0);
|
||||
@@ -335,13 +338,13 @@ void Mixer::deleteChannel( int index )
|
||||
MixerChannel * ch = m_mixerChannels[index];
|
||||
|
||||
// delete all of this channel's sends and receives
|
||||
while( ! ch->m_sends.isEmpty() )
|
||||
while (!ch->m_sends.empty())
|
||||
{
|
||||
deleteChannelSend( ch->m_sends.first() );
|
||||
deleteChannelSend(ch->m_sends.front());
|
||||
}
|
||||
while( ! ch->m_receives.isEmpty() )
|
||||
while (!ch->m_receives.empty())
|
||||
{
|
||||
deleteChannelSend( ch->m_receives.first() );
|
||||
deleteChannelSend(ch->m_receives.front());
|
||||
}
|
||||
|
||||
// if m_lastSoloed was our index, reset it
|
||||
@@ -350,7 +353,7 @@ void Mixer::deleteChannel( int index )
|
||||
else if (m_lastSoloed > index) { --m_lastSoloed; }
|
||||
|
||||
// actually delete the channel
|
||||
m_mixerChannels.remove(index);
|
||||
m_mixerChannels.erase(m_mixerChannels.begin() + index);
|
||||
delete ch;
|
||||
|
||||
for( int i = index; i < m_mixerChannels.size(); ++i )
|
||||
@@ -391,14 +394,14 @@ void Mixer::moveChannelLeft( int index )
|
||||
else if (m_lastSoloed == b) { m_lastSoloed = a; }
|
||||
|
||||
// go through every instrument and adjust for the channel index change
|
||||
TrackContainer::TrackList songTrackList = Engine::getSong()->tracks();
|
||||
TrackContainer::TrackList patternTrackList = Engine::patternStore()->tracks();
|
||||
const TrackContainer::TrackList& songTrackList = Engine::getSong()->tracks();
|
||||
const TrackContainer::TrackList& patternTrackList = Engine::patternStore()->tracks();
|
||||
|
||||
for (const auto& trackList : {songTrackList, patternTrackList})
|
||||
{
|
||||
for (const auto& track : trackList)
|
||||
{
|
||||
if (track->type() == Track::InstrumentTrack)
|
||||
if (track->type() == Track::Type::Instrument)
|
||||
{
|
||||
auto inst = (InstrumentTrack*)track;
|
||||
int val = inst->mixerChannelModel()->value(0);
|
||||
@@ -411,7 +414,7 @@ void Mixer::moveChannelLeft( int index )
|
||||
inst->mixerChannelModel()->setValue(a);
|
||||
}
|
||||
}
|
||||
else if (track->type() == Track::SampleTrack)
|
||||
else if (track->type() == Track::Type::Sample)
|
||||
{
|
||||
auto strk = (SampleTrack*)track;
|
||||
int val = strk->mixerChannelModel()->value(0);
|
||||
@@ -477,13 +480,13 @@ MixerRoute * Mixer::createRoute( MixerChannel * from, MixerChannel * to, float a
|
||||
auto route = new MixerRoute(from, to, amount);
|
||||
|
||||
// add us to from's sends
|
||||
from->m_sends.append( route );
|
||||
from->m_sends.push_back(route);
|
||||
|
||||
// add us to to's receives
|
||||
to->m_receives.append( route );
|
||||
to->m_receives.push_back(route);
|
||||
|
||||
// add us to mixer's list
|
||||
Engine::mixer()->m_mixerRoutes.append( route );
|
||||
Engine::mixer()->m_mixerRoutes.push_back(route);
|
||||
Engine::audioEngine()->doneChangeInModel();
|
||||
|
||||
return route;
|
||||
@@ -512,12 +515,22 @@ void Mixer::deleteChannelSend( mix_ch_t fromChannel, mix_ch_t toChannel )
|
||||
void Mixer::deleteChannelSend( MixerRoute * route )
|
||||
{
|
||||
Engine::audioEngine()->requestChangeInModel();
|
||||
|
||||
auto removeFromMixerRoute = [route](MixerRouteVector& routeVec)
|
||||
{
|
||||
auto it = std::find(routeVec.begin(), routeVec.end(), route);
|
||||
if (it != routeVec.end()) { routeVec.erase(it); }
|
||||
};
|
||||
|
||||
// remove us from from's sends
|
||||
route->sender()->m_sends.remove( route->sender()->m_sends.indexOf( route ) );
|
||||
removeFromMixerRoute(route->sender()->m_sends);
|
||||
|
||||
// remove us from to's receives
|
||||
route->receiver()->m_receives.remove( route->receiver()->m_receives.indexOf( route ) );
|
||||
removeFromMixerRoute(route->receiver()->m_receives);
|
||||
|
||||
// remove us from mixer's list
|
||||
Engine::mixer()->m_mixerRoutes.remove( Engine::mixer()->m_mixerRoutes.indexOf( route ) );
|
||||
removeFromMixerRoute(Engine::mixer()->m_mixerRoutes);
|
||||
|
||||
delete route;
|
||||
Engine::audioEngine()->doneChangeInModel();
|
||||
}
|
||||
@@ -617,7 +630,7 @@ void Mixer::masterMix( sampleFrame * _buf )
|
||||
// also instantly add all muted channels as they don't need to care
|
||||
// about their senders, and can just increment the deps of their
|
||||
// recipients right away.
|
||||
AudioEngineWorkerThread::resetJobQueue( AudioEngineWorkerThread::JobQueue::Dynamic );
|
||||
AudioEngineWorkerThread::resetJobQueue( AudioEngineWorkerThread::JobQueue::OperationMode::Dynamic );
|
||||
for( MixerChannel * ch : m_mixerChannels )
|
||||
{
|
||||
ch->m_muted = ch->m_muteModel.value();
|
||||
@@ -714,9 +727,9 @@ void Mixer::clearChannel(mix_ch_t index)
|
||||
if( index > 0)
|
||||
{
|
||||
// delete existing sends
|
||||
while( ! ch->m_sends.isEmpty() )
|
||||
while (!ch->m_sends.empty())
|
||||
{
|
||||
deleteChannelSend( ch->m_sends.first() );
|
||||
deleteChannelSend(ch->m_sends.front());
|
||||
}
|
||||
|
||||
// add send to master
|
||||
@@ -724,9 +737,9 @@ void Mixer::clearChannel(mix_ch_t index)
|
||||
}
|
||||
|
||||
// delete receives
|
||||
while( ! ch->m_receives.isEmpty() )
|
||||
while (!ch->m_receives.empty())
|
||||
{
|
||||
deleteChannelSend( ch->m_receives.first() );
|
||||
deleteChannelSend(ch->m_receives.front());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -835,19 +848,22 @@ void Mixer::validateChannelName( int index, int oldIndex )
|
||||
bool Mixer::isChannelInUse(int index)
|
||||
{
|
||||
// check if the index mixer channel receives audio from any other channel
|
||||
if (!m_mixerChannels[index]->m_receives.isEmpty())
|
||||
if (!m_mixerChannels[index]->m_receives.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if the destination mixer channel on any instrument or sample track is the index mixer channel
|
||||
TrackContainer::TrackList tracks;
|
||||
tracks += Engine::getSong()->tracks();
|
||||
tracks += Engine::patternStore()->tracks();
|
||||
|
||||
auto& songTracks = Engine::getSong()->tracks();
|
||||
auto& patternStoreTracks = Engine::patternStore()->tracks();
|
||||
tracks.insert(tracks.end(), songTracks.begin(), songTracks.end());
|
||||
tracks.insert(tracks.end(), patternStoreTracks.begin(), patternStoreTracks.end());
|
||||
|
||||
for (const auto t : tracks)
|
||||
{
|
||||
if (t->type() == Track::InstrumentTrack)
|
||||
if (t->type() == Track::Type::Instrument)
|
||||
{
|
||||
auto inst = dynamic_cast<InstrumentTrack*>(t);
|
||||
if (inst->mixerChannelModel()->value() == index)
|
||||
@@ -855,7 +871,7 @@ bool Mixer::isChannelInUse(int index)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (t->type() == Track::SampleTrack)
|
||||
else if (t->type() == Track::Type::Sample)
|
||||
{
|
||||
auto strack = dynamic_cast<SampleTrack*>(t);
|
||||
if (strack->mixerChannelModel()->value() == index)
|
||||
|
||||
@@ -38,13 +38,13 @@ Note::Note( const TimePos & length, const TimePos & pos,
|
||||
int key, volume_t volume, panning_t panning,
|
||||
DetuningHelper * detuning ) :
|
||||
m_selected( false ),
|
||||
m_oldKey( qBound( 0, key, NumKeys ) ),
|
||||
m_oldKey(std::clamp(key, 0, NumKeys)),
|
||||
m_oldPos( pos ),
|
||||
m_oldLength( length ),
|
||||
m_isPlaying( false ),
|
||||
m_key( qBound( 0, key, NumKeys ) ),
|
||||
m_volume( qBound( MinVolume, volume, MaxVolume ) ),
|
||||
m_panning( qBound( PanningLeft, panning, PanningRight ) ),
|
||||
m_key(std::clamp(key, 0, NumKeys)),
|
||||
m_volume(std::clamp(volume, MinVolume, MaxVolume)),
|
||||
m_panning(std::clamp(panning, PanningLeft, PanningRight)),
|
||||
m_length( length ),
|
||||
m_pos( pos ),
|
||||
m_detuning( nullptr )
|
||||
@@ -114,7 +114,7 @@ void Note::setPos( const TimePos & pos )
|
||||
|
||||
void Note::setKey( const int key )
|
||||
{
|
||||
const int k = qBound( 0, key, NumKeys - 1 );
|
||||
const int k = std::clamp(key, 0, NumKeys - 1);
|
||||
m_key = k;
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ void Note::setKey( const int key )
|
||||
|
||||
void Note::setVolume( volume_t volume )
|
||||
{
|
||||
const volume_t v = qBound( MinVolume, volume, MaxVolume );
|
||||
const volume_t v = std::clamp(volume, MinVolume, MaxVolume);
|
||||
m_volume = v;
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ void Note::setVolume( volume_t volume )
|
||||
|
||||
void Note::setPanning( panning_t panning )
|
||||
{
|
||||
const panning_t p = qBound( PanningLeft, panning, PanningRight );
|
||||
const panning_t p = std::clamp(panning, PanningLeft, PanningRight);
|
||||
m_panning = p;
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ void Note::saveSettings( QDomDocument & doc, QDomElement & parent )
|
||||
void Note::loadSettings( const QDomElement & _this )
|
||||
{
|
||||
const int oldKey = _this.attribute( "tone" ).toInt() + _this.attribute( "oct" ).toInt() * KeysPerOctave;
|
||||
m_key = qMax( oldKey, _this.attribute( "key" ).toInt() );
|
||||
m_key = std::max(oldKey, _this.attribute("key").toInt());
|
||||
m_volume = _this.attribute( "vol" ).toInt();
|
||||
m_panning = _this.attribute( "pan" ).toInt();
|
||||
m_length = _this.attribute( "len" ).toInt();
|
||||
@@ -216,7 +216,7 @@ void Note::createDetuning()
|
||||
m_detuning = new DetuningHelper;
|
||||
(void) m_detuning->automationClip();
|
||||
m_detuning->setRange( -MaxDetuning, MaxDetuning, 0.5f );
|
||||
m_detuning->automationClip()->setProgressionType( AutomationClip::LinearProgression );
|
||||
m_detuning->automationClip()->setProgressionType( AutomationClip::ProgressionType::Linear );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
|
||||
NotePlayHandle *parent,
|
||||
int midiEventChannel,
|
||||
Origin origin ) :
|
||||
PlayHandle( TypeNotePlayHandle, _offset ),
|
||||
PlayHandle( Type::NotePlayHandle, _offset ),
|
||||
Note( n.length(), n.pos(), n.key(), n.getVolume(), n.getPanning(), n.detuning() ),
|
||||
m_pluginData( nullptr ),
|
||||
m_instrumentTrack( instrumentTrack ),
|
||||
@@ -104,12 +104,12 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack,
|
||||
setFrames( _frames );
|
||||
|
||||
// inform attached components about new MIDI note (used for recording in Piano Roll)
|
||||
if( m_origin == OriginMidiInput )
|
||||
if( m_origin == Origin::MidiInput )
|
||||
{
|
||||
m_instrumentTrack->midiNoteOn( *this );
|
||||
}
|
||||
|
||||
if(m_instrumentTrack->instrument() && m_instrumentTrack->instrument()->flags() & Instrument::IsSingleStreamed )
|
||||
if(m_instrumentTrack->instrument() && m_instrumentTrack->instrument()->flags() & Instrument::Flag::IsSingleStreamed )
|
||||
{
|
||||
setUsesBuffer( false );
|
||||
}
|
||||
@@ -350,9 +350,9 @@ fpp_t NotePlayHandle::framesLeftForCurrentPeriod() const
|
||||
{
|
||||
if( m_totalFramesPlayed == 0 )
|
||||
{
|
||||
return (fpp_t) qMin<f_cnt_t>( framesLeft(), Engine::audioEngine()->framesPerPeriod() - offset() );
|
||||
return static_cast<fpp_t>(std::min<f_cnt_t>(framesLeft(), Engine::audioEngine()->framesPerPeriod() - offset()));
|
||||
}
|
||||
return (fpp_t) qMin<f_cnt_t>( framesLeft(), Engine::audioEngine()->framesPerPeriod() );
|
||||
return static_cast<fpp_t>(std::min<f_cnt_t>(framesLeft(), Engine::audioEngine()->framesPerPeriod()));
|
||||
}
|
||||
|
||||
|
||||
@@ -384,7 +384,7 @@ void NotePlayHandle::noteOff( const f_cnt_t _s )
|
||||
|
||||
// then set some variables indicating release-state
|
||||
m_framesBeforeRelease = _s;
|
||||
m_releaseFramesToDo = qMax<f_cnt_t>( 0, actualReleaseFramesToDo() );
|
||||
m_releaseFramesToDo = std::max<f_cnt_t>(0, actualReleaseFramesToDo());
|
||||
|
||||
if( m_hasMidiNote )
|
||||
{
|
||||
@@ -400,7 +400,7 @@ void NotePlayHandle::noteOff( const f_cnt_t _s )
|
||||
// inform attached components about MIDI finished (used for recording in Piano Roll)
|
||||
if (!instrumentTrack()->isSustainPedalPressed())
|
||||
{
|
||||
if( m_origin == OriginMidiInput )
|
||||
if( m_origin == Origin::MidiInput )
|
||||
{
|
||||
setLength( TimePos( static_cast<f_cnt_t>( totalFramesPlayed() / Engine::framesPerTick() ) ) );
|
||||
m_instrumentTrack->midiNoteOff( *this );
|
||||
@@ -557,14 +557,20 @@ void NotePlayHandle::updateFrequency()
|
||||
|
||||
|
||||
|
||||
void NotePlayHandle::processTimePos( const TimePos& time )
|
||||
void NotePlayHandle::processTimePos(const TimePos& time, float pitchValue, bool isRecording)
|
||||
{
|
||||
if( detuning() && time >= songGlobalParentOffset()+pos() )
|
||||
if (!detuning() || time < songGlobalParentOffset() + pos()) { return; }
|
||||
|
||||
if (isRecording && m_origin == Origin::MidiInput)
|
||||
{
|
||||
const float v = detuning()->automationClip()->valueAt( time - songGlobalParentOffset() - pos() );
|
||||
if( !typeInfo<float>::isEqual( v, m_baseDetuning->value() ) )
|
||||
detuning()->automationClip()->recordValue(time - songGlobalParentOffset() - pos(), pitchValue / 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
const float v = detuning()->automationClip()->valueAt(time - songGlobalParentOffset() - pos());
|
||||
if (!typeInfo<float>::isEqual(v, m_baseDetuning->value()))
|
||||
{
|
||||
m_baseDetuning->setValue( v );
|
||||
m_baseDetuning->setValue(v);
|
||||
updateFrequency();
|
||||
}
|
||||
}
|
||||
@@ -575,8 +581,8 @@ void NotePlayHandle::processTimePos( const TimePos& time )
|
||||
|
||||
void NotePlayHandle::resize( const bpm_t _new_tempo )
|
||||
{
|
||||
if (origin() == OriginMidiInput ||
|
||||
(origin() == OriginNoteStacking && m_parent->origin() == OriginMidiInput))
|
||||
if (origin() == Origin::MidiInput ||
|
||||
(origin() == Origin::NoteStacking && m_parent->origin() == Origin::MidiInput))
|
||||
{
|
||||
// Don't resize notes from MIDI input - they should continue to play
|
||||
// until the key is released, and their large duration can cause
|
||||
|
||||
@@ -90,21 +90,22 @@ void Oscillator::update(sampleFrame* ab, const fpp_t frames, const ch_cnt_t chnl
|
||||
m_isModulator = modulator;
|
||||
if (m_subOsc != nullptr)
|
||||
{
|
||||
switch (m_modulationAlgoModel->value())
|
||||
switch (static_cast<ModulationAlgo>(m_modulationAlgoModel->value()))
|
||||
{
|
||||
case PhaseModulation:
|
||||
case ModulationAlgo::PhaseModulation:
|
||||
updatePM(ab, frames, chnl);
|
||||
break;
|
||||
case AmplitudeModulation:
|
||||
case ModulationAlgo::AmplitudeModulation:
|
||||
updateAM(ab, frames, chnl);
|
||||
break;
|
||||
case SignalMix:
|
||||
case ModulationAlgo::SignalMix:
|
||||
default:
|
||||
updateMix(ab, frames, chnl);
|
||||
break;
|
||||
case SynchronizedBySubOsc:
|
||||
case ModulationAlgo::SynchronizedBySubOsc:
|
||||
updateSync(ab, frames, chnl);
|
||||
break;
|
||||
case FrequencyModulation:
|
||||
case ModulationAlgo::FrequencyModulation:
|
||||
updateFM(ab, frames, chnl);
|
||||
}
|
||||
}
|
||||
@@ -199,7 +200,7 @@ void Oscillator::generateAntiAliasUserWaveTable(SampleBuffer *sampleBuffer)
|
||||
|
||||
|
||||
sample_t Oscillator::s_waveTables
|
||||
[Oscillator::WaveShapes::NumWaveShapeTables]
|
||||
[Oscillator::NumWaveShapeTables]
|
||||
[OscillatorConstants::WAVE_TABLES_PER_WAVEFORM_COUNT]
|
||||
[OscillatorConstants::WAVETABLE_LENGTH];
|
||||
fftwf_plan Oscillator::s_fftPlan;
|
||||
@@ -235,9 +236,9 @@ void Oscillator::generateWaveTables()
|
||||
// Start from the table that contains the least number of bands, and re-use each table in the following
|
||||
// iteration, adding more bands in each step and avoiding repeated computation of earlier bands.
|
||||
using generator_t = void (*)(int, sample_t*, int);
|
||||
auto simpleGen = [](WaveShapes shape, generator_t generator)
|
||||
auto simpleGen = [](WaveShape shape, generator_t generator)
|
||||
{
|
||||
const int shapeID = shape - FirstWaveShapeTable;
|
||||
const int shapeID = static_cast<std::size_t>(shape) - FirstWaveShapeTable;
|
||||
int lastBands = 0;
|
||||
|
||||
// Clear the first wave table
|
||||
@@ -273,7 +274,7 @@ void Oscillator::generateWaveTables()
|
||||
Oscillator::s_sampleBuffer[i] = moogSawSample((float)i / (float)OscillatorConstants::WAVETABLE_LENGTH);
|
||||
}
|
||||
fftwf_execute(s_fftPlan);
|
||||
generateFromFFT(OscillatorConstants::MAX_FREQ / freqFromWaveTableBand(i), s_waveTables[WaveShapes::MoogSawWave - FirstWaveShapeTable][i]);
|
||||
generateFromFFT(OscillatorConstants::MAX_FREQ / freqFromWaveTableBand(i), s_waveTables[static_cast<std::size_t>(WaveShape::MoogSaw) - FirstWaveShapeTable][i]);
|
||||
}
|
||||
|
||||
// Generate exponential tables
|
||||
@@ -284,7 +285,7 @@ void Oscillator::generateWaveTables()
|
||||
s_sampleBuffer[i] = expSample((float)i / (float)OscillatorConstants::WAVETABLE_LENGTH);
|
||||
}
|
||||
fftwf_execute(s_fftPlan);
|
||||
generateFromFFT(OscillatorConstants::MAX_FREQ / freqFromWaveTableBand(i), s_waveTables[WaveShapes::ExponentialWave - FirstWaveShapeTable][i]);
|
||||
generateFromFFT(OscillatorConstants::MAX_FREQ / freqFromWaveTableBand(i), s_waveTables[static_cast<std::size_t>(WaveShape::Exponential) - FirstWaveShapeTable][i]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -292,18 +293,18 @@ void Oscillator::generateWaveTables()
|
||||
// but since threading is not essential in this case, it is easier and more reliable to simply generate
|
||||
// the wavetables serially. Remove the the check and #else branch once std::thread is well supported.
|
||||
#if !defined(__MINGW32__) && !defined(__MINGW64__)
|
||||
std::thread sawThread(simpleGen, WaveShapes::SawWave, generateSawWaveTable);
|
||||
std::thread squareThread(simpleGen, WaveShapes::SquareWave, generateSquareWaveTable);
|
||||
std::thread triangleThread(simpleGen, WaveShapes::TriangleWave, generateTriangleWaveTable);
|
||||
std::thread sawThread(simpleGen, WaveShape::Saw, generateSawWaveTable);
|
||||
std::thread squareThread(simpleGen, WaveShape::Square, generateSquareWaveTable);
|
||||
std::thread triangleThread(simpleGen, WaveShape::Triangle, generateTriangleWaveTable);
|
||||
std::thread fftThread(fftGen);
|
||||
sawThread.join();
|
||||
squareThread.join();
|
||||
triangleThread.join();
|
||||
fftThread.join();
|
||||
#else
|
||||
simpleGen(WaveShapes::SawWave, generateSawWaveTable);
|
||||
simpleGen(WaveShapes::SquareWave, generateSquareWaveTable);
|
||||
simpleGen(WaveShapes::TriangleWave, generateTriangleWaveTable);
|
||||
simpleGen(WaveShape::Saw, generateSawWaveTable);
|
||||
simpleGen(WaveShape::Square, generateSquareWaveTable);
|
||||
simpleGen(WaveShape::Triangle, generateTriangleWaveTable);
|
||||
fftGen();
|
||||
#endif
|
||||
}
|
||||
@@ -314,32 +315,32 @@ void Oscillator::generateWaveTables()
|
||||
void Oscillator::updateNoSub( sampleFrame * _ab, const fpp_t _frames,
|
||||
const ch_cnt_t _chnl )
|
||||
{
|
||||
switch( m_waveShapeModel->value() )
|
||||
switch( static_cast<WaveShape>(m_waveShapeModel->value()) )
|
||||
{
|
||||
case SineWave:
|
||||
case WaveShape::Sine:
|
||||
default:
|
||||
updateNoSub<SineWave>( _ab, _frames, _chnl );
|
||||
updateNoSub<WaveShape::Sine>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case TriangleWave:
|
||||
updateNoSub<TriangleWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Triangle:
|
||||
updateNoSub<WaveShape::Triangle>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case SawWave:
|
||||
updateNoSub<SawWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Saw:
|
||||
updateNoSub<WaveShape::Saw>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case SquareWave:
|
||||
updateNoSub<SquareWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Square:
|
||||
updateNoSub<WaveShape::Square>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case MoogSawWave:
|
||||
updateNoSub<MoogSawWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::MoogSaw:
|
||||
updateNoSub<WaveShape::MoogSaw>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case ExponentialWave:
|
||||
updateNoSub<ExponentialWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Exponential:
|
||||
updateNoSub<WaveShape::Exponential>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case WhiteNoise:
|
||||
updateNoSub<WhiteNoise>( _ab, _frames, _chnl );
|
||||
case WaveShape::WhiteNoise:
|
||||
updateNoSub<WaveShape::WhiteNoise>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case UserDefinedWave:
|
||||
updateNoSub<UserDefinedWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::UserDefined:
|
||||
updateNoSub<WaveShape::UserDefined>( _ab, _frames, _chnl );
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -350,32 +351,32 @@ void Oscillator::updateNoSub( sampleFrame * _ab, const fpp_t _frames,
|
||||
void Oscillator::updatePM( sampleFrame * _ab, const fpp_t _frames,
|
||||
const ch_cnt_t _chnl )
|
||||
{
|
||||
switch( m_waveShapeModel->value() )
|
||||
switch( static_cast<WaveShape>(m_waveShapeModel->value()) )
|
||||
{
|
||||
case SineWave:
|
||||
case WaveShape::Sine:
|
||||
default:
|
||||
updatePM<SineWave>( _ab, _frames, _chnl );
|
||||
updatePM<WaveShape::Sine>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case TriangleWave:
|
||||
updatePM<TriangleWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Triangle:
|
||||
updatePM<WaveShape::Triangle>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case SawWave:
|
||||
updatePM<SawWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Saw:
|
||||
updatePM<WaveShape::Saw>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case SquareWave:
|
||||
updatePM<SquareWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Square:
|
||||
updatePM<WaveShape::Square>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case MoogSawWave:
|
||||
updatePM<MoogSawWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::MoogSaw:
|
||||
updatePM<WaveShape::MoogSaw>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case ExponentialWave:
|
||||
updatePM<ExponentialWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Exponential:
|
||||
updatePM<WaveShape::Exponential>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case WhiteNoise:
|
||||
updatePM<WhiteNoise>( _ab, _frames, _chnl );
|
||||
case WaveShape::WhiteNoise:
|
||||
updatePM<WaveShape::WhiteNoise>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case UserDefinedWave:
|
||||
updatePM<UserDefinedWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::UserDefined:
|
||||
updatePM<WaveShape::UserDefined>( _ab, _frames, _chnl );
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -386,32 +387,32 @@ void Oscillator::updatePM( sampleFrame * _ab, const fpp_t _frames,
|
||||
void Oscillator::updateAM( sampleFrame * _ab, const fpp_t _frames,
|
||||
const ch_cnt_t _chnl )
|
||||
{
|
||||
switch( m_waveShapeModel->value() )
|
||||
switch( static_cast<WaveShape>(m_waveShapeModel->value()) )
|
||||
{
|
||||
case SineWave:
|
||||
case WaveShape::Sine:
|
||||
default:
|
||||
updateAM<SineWave>( _ab, _frames, _chnl );
|
||||
updateAM<WaveShape::Sine>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case TriangleWave:
|
||||
updateAM<TriangleWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Triangle:
|
||||
updateAM<WaveShape::Triangle>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case SawWave:
|
||||
updateAM<SawWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Saw:
|
||||
updateAM<WaveShape::Saw>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case SquareWave:
|
||||
updateAM<SquareWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Square:
|
||||
updateAM<WaveShape::Square>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case MoogSawWave:
|
||||
updateAM<MoogSawWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::MoogSaw:
|
||||
updateAM<WaveShape::MoogSaw>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case ExponentialWave:
|
||||
updateAM<ExponentialWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Exponential:
|
||||
updateAM<WaveShape::Exponential>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case WhiteNoise:
|
||||
updateAM<WhiteNoise>( _ab, _frames, _chnl );
|
||||
case WaveShape::WhiteNoise:
|
||||
updateAM<WaveShape::WhiteNoise>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case UserDefinedWave:
|
||||
updateAM<UserDefinedWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::UserDefined:
|
||||
updateAM<WaveShape::UserDefined>( _ab, _frames, _chnl );
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -422,32 +423,32 @@ void Oscillator::updateAM( sampleFrame * _ab, const fpp_t _frames,
|
||||
void Oscillator::updateMix( sampleFrame * _ab, const fpp_t _frames,
|
||||
const ch_cnt_t _chnl )
|
||||
{
|
||||
switch( m_waveShapeModel->value() )
|
||||
switch( static_cast<WaveShape>(m_waveShapeModel->value()) )
|
||||
{
|
||||
case SineWave:
|
||||
case WaveShape::Sine:
|
||||
default:
|
||||
updateMix<SineWave>( _ab, _frames, _chnl );
|
||||
updateMix<WaveShape::Sine>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case TriangleWave:
|
||||
updateMix<TriangleWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Triangle:
|
||||
updateMix<WaveShape::Triangle>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case SawWave:
|
||||
updateMix<SawWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Saw:
|
||||
updateMix<WaveShape::Saw>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case SquareWave:
|
||||
updateMix<SquareWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Square:
|
||||
updateMix<WaveShape::Square>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case MoogSawWave:
|
||||
updateMix<MoogSawWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::MoogSaw:
|
||||
updateMix<WaveShape::MoogSaw>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case ExponentialWave:
|
||||
updateMix<ExponentialWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Exponential:
|
||||
updateMix<WaveShape::Exponential>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case WhiteNoise:
|
||||
updateMix<WhiteNoise>( _ab, _frames, _chnl );
|
||||
case WaveShape::WhiteNoise:
|
||||
updateMix<WaveShape::WhiteNoise>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case UserDefinedWave:
|
||||
updateMix<UserDefinedWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::UserDefined:
|
||||
updateMix<WaveShape::UserDefined>( _ab, _frames, _chnl );
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -458,32 +459,32 @@ void Oscillator::updateMix( sampleFrame * _ab, const fpp_t _frames,
|
||||
void Oscillator::updateSync( sampleFrame * _ab, const fpp_t _frames,
|
||||
const ch_cnt_t _chnl )
|
||||
{
|
||||
switch( m_waveShapeModel->value() )
|
||||
switch( static_cast<WaveShape>(m_waveShapeModel->value()) )
|
||||
{
|
||||
case SineWave:
|
||||
case WaveShape::Sine:
|
||||
default:
|
||||
updateSync<SineWave>( _ab, _frames, _chnl );
|
||||
updateSync<WaveShape::Sine>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case TriangleWave:
|
||||
updateSync<TriangleWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Triangle:
|
||||
updateSync<WaveShape::Triangle>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case SawWave:
|
||||
updateSync<SawWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Saw:
|
||||
updateSync<WaveShape::Saw>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case SquareWave:
|
||||
updateSync<SquareWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Square:
|
||||
updateSync<WaveShape::Square>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case MoogSawWave:
|
||||
updateSync<MoogSawWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::MoogSaw:
|
||||
updateSync<WaveShape::MoogSaw>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case ExponentialWave:
|
||||
updateSync<ExponentialWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Exponential:
|
||||
updateSync<WaveShape::Exponential>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case WhiteNoise:
|
||||
updateSync<WhiteNoise>( _ab, _frames, _chnl );
|
||||
case WaveShape::WhiteNoise:
|
||||
updateSync<WaveShape::WhiteNoise>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case UserDefinedWave:
|
||||
updateSync<UserDefinedWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::UserDefined:
|
||||
updateSync<WaveShape::UserDefined>( _ab, _frames, _chnl );
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -494,32 +495,32 @@ void Oscillator::updateSync( sampleFrame * _ab, const fpp_t _frames,
|
||||
void Oscillator::updateFM( sampleFrame * _ab, const fpp_t _frames,
|
||||
const ch_cnt_t _chnl )
|
||||
{
|
||||
switch( m_waveShapeModel->value() )
|
||||
switch( static_cast<WaveShape>(m_waveShapeModel->value()) )
|
||||
{
|
||||
case SineWave:
|
||||
case WaveShape::Sine:
|
||||
default:
|
||||
updateFM<SineWave>( _ab, _frames, _chnl );
|
||||
updateFM<WaveShape::Sine>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case TriangleWave:
|
||||
updateFM<TriangleWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Triangle:
|
||||
updateFM<WaveShape::Triangle>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case SawWave:
|
||||
updateFM<SawWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Saw:
|
||||
updateFM<WaveShape::Saw>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case SquareWave:
|
||||
updateFM<SquareWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Square:
|
||||
updateFM<WaveShape::Square>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case MoogSawWave:
|
||||
updateFM<MoogSawWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::MoogSaw:
|
||||
updateFM<WaveShape::MoogSaw>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case ExponentialWave:
|
||||
updateFM<ExponentialWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::Exponential:
|
||||
updateFM<WaveShape::Exponential>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case WhiteNoise:
|
||||
updateFM<WhiteNoise>( _ab, _frames, _chnl );
|
||||
case WaveShape::WhiteNoise:
|
||||
updateFM<WaveShape::WhiteNoise>( _ab, _frames, _chnl );
|
||||
break;
|
||||
case UserDefinedWave:
|
||||
updateFM<UserDefinedWave>( _ab, _frames, _chnl );
|
||||
case WaveShape::UserDefined:
|
||||
updateFM<WaveShape::UserDefined>( _ab, _frames, _chnl );
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -568,7 +569,7 @@ float Oscillator::syncInit( sampleFrame * _ab, const fpp_t _frames,
|
||||
|
||||
|
||||
// if we have no sub-osc, we can't do any modulation... just get our samples
|
||||
template<Oscillator::WaveShapes W>
|
||||
template<Oscillator::WaveShape W>
|
||||
void Oscillator::updateNoSub( sampleFrame * _ab, const fpp_t _frames,
|
||||
const ch_cnt_t _chnl )
|
||||
{
|
||||
@@ -586,7 +587,7 @@ void Oscillator::updateNoSub( sampleFrame * _ab, const fpp_t _frames,
|
||||
|
||||
|
||||
// do pm by using sub-osc as modulator
|
||||
template<Oscillator::WaveShapes W>
|
||||
template<Oscillator::WaveShape W>
|
||||
void Oscillator::updatePM( sampleFrame * _ab, const fpp_t _frames,
|
||||
const ch_cnt_t _chnl )
|
||||
{
|
||||
@@ -607,7 +608,7 @@ void Oscillator::updatePM( sampleFrame * _ab, const fpp_t _frames,
|
||||
|
||||
|
||||
// do am by using sub-osc as modulator
|
||||
template<Oscillator::WaveShapes W>
|
||||
template<Oscillator::WaveShape W>
|
||||
void Oscillator::updateAM( sampleFrame * _ab, const fpp_t _frames,
|
||||
const ch_cnt_t _chnl )
|
||||
{
|
||||
@@ -626,7 +627,7 @@ void Oscillator::updateAM( sampleFrame * _ab, const fpp_t _frames,
|
||||
|
||||
|
||||
// do mix by using sub-osc as mix-sample
|
||||
template<Oscillator::WaveShapes W>
|
||||
template<Oscillator::WaveShape W>
|
||||
void Oscillator::updateMix( sampleFrame * _ab, const fpp_t _frames,
|
||||
const ch_cnt_t _chnl )
|
||||
{
|
||||
@@ -646,7 +647,7 @@ void Oscillator::updateMix( sampleFrame * _ab, const fpp_t _frames,
|
||||
|
||||
// sync with sub-osc (every time sub-osc starts new period, we also start new
|
||||
// period)
|
||||
template<Oscillator::WaveShapes W>
|
||||
template<Oscillator::WaveShape W>
|
||||
void Oscillator::updateSync( sampleFrame * _ab, const fpp_t _frames,
|
||||
const ch_cnt_t _chnl )
|
||||
{
|
||||
@@ -669,7 +670,7 @@ void Oscillator::updateSync( sampleFrame * _ab, const fpp_t _frames,
|
||||
|
||||
|
||||
// do fm by using sub-osc as modulator
|
||||
template<Oscillator::WaveShapes W>
|
||||
template<Oscillator::WaveShape W>
|
||||
void Oscillator::updateFM( sampleFrame * _ab, const fpp_t _frames,
|
||||
const ch_cnt_t _chnl )
|
||||
{
|
||||
@@ -690,7 +691,7 @@ void Oscillator::updateFM( sampleFrame * _ab, const fpp_t _frames,
|
||||
|
||||
|
||||
template<>
|
||||
inline sample_t Oscillator::getSample<Oscillator::SineWave>(const float sample)
|
||||
inline sample_t Oscillator::getSample<Oscillator::WaveShape::Sine>(const float sample)
|
||||
{
|
||||
const float current_freq = m_freq * m_detuning_div_samplerate * Engine::audioEngine()->processingSampleRate();
|
||||
|
||||
@@ -708,12 +709,12 @@ inline sample_t Oscillator::getSample<Oscillator::SineWave>(const float sample)
|
||||
|
||||
|
||||
template<>
|
||||
inline sample_t Oscillator::getSample<Oscillator::TriangleWave>(
|
||||
inline sample_t Oscillator::getSample<Oscillator::WaveShape::Triangle>(
|
||||
const float _sample )
|
||||
{
|
||||
if (m_useWaveTable && !m_isModulator)
|
||||
{
|
||||
return wtSample(s_waveTables[WaveShapes::TriangleWave - FirstWaveShapeTable],_sample);
|
||||
return wtSample(s_waveTables[static_cast<std::size_t>(WaveShape::Triangle) - FirstWaveShapeTable],_sample);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -725,12 +726,12 @@ inline sample_t Oscillator::getSample<Oscillator::TriangleWave>(
|
||||
|
||||
|
||||
template<>
|
||||
inline sample_t Oscillator::getSample<Oscillator::SawWave>(
|
||||
inline sample_t Oscillator::getSample<Oscillator::WaveShape::Saw>(
|
||||
const float _sample )
|
||||
{
|
||||
if (m_useWaveTable && !m_isModulator)
|
||||
{
|
||||
return wtSample(s_waveTables[WaveShapes::SawWave - FirstWaveShapeTable], _sample);
|
||||
return wtSample(s_waveTables[static_cast<std::size_t>(WaveShape::Saw) - FirstWaveShapeTable], _sample);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -742,12 +743,12 @@ inline sample_t Oscillator::getSample<Oscillator::SawWave>(
|
||||
|
||||
|
||||
template<>
|
||||
inline sample_t Oscillator::getSample<Oscillator::SquareWave>(
|
||||
inline sample_t Oscillator::getSample<Oscillator::WaveShape::Square>(
|
||||
const float _sample )
|
||||
{
|
||||
if (m_useWaveTable && !m_isModulator)
|
||||
{
|
||||
return wtSample(s_waveTables[WaveShapes::SquareWave - FirstWaveShapeTable], _sample);
|
||||
return wtSample(s_waveTables[static_cast<std::size_t>(WaveShape::Square) - FirstWaveShapeTable], _sample);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -759,12 +760,12 @@ inline sample_t Oscillator::getSample<Oscillator::SquareWave>(
|
||||
|
||||
|
||||
template<>
|
||||
inline sample_t Oscillator::getSample<Oscillator::MoogSawWave>(
|
||||
inline sample_t Oscillator::getSample<Oscillator::WaveShape::MoogSaw>(
|
||||
const float _sample )
|
||||
{
|
||||
if (m_useWaveTable && !m_isModulator)
|
||||
{
|
||||
return wtSample(s_waveTables[WaveShapes::MoogSawWave - FirstWaveShapeTable], _sample);
|
||||
return wtSample(s_waveTables[static_cast<std::size_t>(WaveShape::MoogSaw) - FirstWaveShapeTable], _sample);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -776,12 +777,12 @@ inline sample_t Oscillator::getSample<Oscillator::MoogSawWave>(
|
||||
|
||||
|
||||
template<>
|
||||
inline sample_t Oscillator::getSample<Oscillator::ExponentialWave>(
|
||||
inline sample_t Oscillator::getSample<Oscillator::WaveShape::Exponential>(
|
||||
const float _sample )
|
||||
{
|
||||
if (m_useWaveTable && !m_isModulator)
|
||||
{
|
||||
return wtSample(s_waveTables[WaveShapes::ExponentialWave - FirstWaveShapeTable], _sample);
|
||||
return wtSample(s_waveTables[static_cast<std::size_t>(WaveShape::Exponential) - FirstWaveShapeTable], _sample);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -793,7 +794,7 @@ inline sample_t Oscillator::getSample<Oscillator::ExponentialWave>(
|
||||
|
||||
|
||||
template<>
|
||||
inline sample_t Oscillator::getSample<Oscillator::WhiteNoise>(
|
||||
inline sample_t Oscillator::getSample<Oscillator::WaveShape::WhiteNoise>(
|
||||
const float _sample )
|
||||
{
|
||||
return( noiseSample( _sample ) );
|
||||
@@ -803,7 +804,7 @@ inline sample_t Oscillator::getSample<Oscillator::WhiteNoise>(
|
||||
|
||||
|
||||
template<>
|
||||
inline sample_t Oscillator::getSample<Oscillator::UserDefinedWave>(
|
||||
inline sample_t Oscillator::getSample<Oscillator::WaveShape::UserDefined>(
|
||||
const float _sample )
|
||||
{
|
||||
if (m_useWaveTable && !m_isModulator)
|
||||
|
||||
@@ -44,7 +44,7 @@ PatternStore::PatternStore() :
|
||||
// not change upon setCurrentPattern()-call
|
||||
connect(&m_patternComboBoxModel, SIGNAL(dataUnchanged()),
|
||||
this, SLOT(currentPatternChanged()));
|
||||
setType(PatternContainer);
|
||||
setType(Type::Pattern);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ bool PatternStore::play(TimePos start, fpp_t frames, f_cnt_t offset, int clipNum
|
||||
|
||||
start = start % (lengthOfPattern(clipNum) * TimePos::ticksPerBar());
|
||||
|
||||
TrackList tl = tracks();
|
||||
const TrackList& tl = tracks();
|
||||
for (Track * t : tl)
|
||||
{
|
||||
if (t->play(start, frames, offset, clipNum))
|
||||
@@ -97,7 +97,7 @@ bar_t PatternStore::lengthOfPattern(int pattern) const
|
||||
// Don't create Clips here if they don't exist
|
||||
if (pattern < t->numOfClips())
|
||||
{
|
||||
maxLength = qMax(maxLength, t->getClip(pattern)->length());
|
||||
maxLength = std::max(maxLength, t->getClip(pattern)->length());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ bar_t PatternStore::lengthOfPattern(int pattern) const
|
||||
|
||||
int PatternStore::numOfPatterns() const
|
||||
{
|
||||
return Engine::getSong()->countTracks(Track::PatternTrack);
|
||||
return Engine::getSong()->countTracks(Track::Type::Pattern);
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ int PatternStore::numOfPatterns() const
|
||||
|
||||
void PatternStore::removePattern(int pattern)
|
||||
{
|
||||
TrackList tl = tracks();
|
||||
const TrackList& tl = tracks();
|
||||
for (Track * t : tl)
|
||||
{
|
||||
delete t->getClip(pattern);
|
||||
@@ -125,7 +125,7 @@ void PatternStore::removePattern(int pattern)
|
||||
}
|
||||
if (pattern <= currentPattern())
|
||||
{
|
||||
setCurrentPattern(qMax(currentPattern() - 1, 0));
|
||||
setCurrentPattern(std::max(currentPattern() - 1, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ void PatternStore::removePattern(int pattern)
|
||||
|
||||
void PatternStore::swapPattern(int pattern1, int pattern2)
|
||||
{
|
||||
TrackList tl = tracks();
|
||||
const TrackList& tl = tracks();
|
||||
for (Track * t : tl)
|
||||
{
|
||||
t->swapPositionOfClips(pattern1, pattern2);
|
||||
@@ -159,7 +159,7 @@ void PatternStore::updatePatternTrack(Clip* clip)
|
||||
|
||||
void PatternStore::fixIncorrectPositions()
|
||||
{
|
||||
TrackList tl = tracks();
|
||||
const TrackList& tl = tracks();
|
||||
for (Track * t : tl)
|
||||
{
|
||||
for (int i = 0; i < numOfPatterns(); ++i)
|
||||
@@ -174,7 +174,7 @@ void PatternStore::fixIncorrectPositions()
|
||||
|
||||
void PatternStore::play()
|
||||
{
|
||||
if (Engine::getSong()->playMode() != Song::Mode_PlayPattern)
|
||||
if (Engine::getSong()->playMode() != Song::PlayMode::Pattern)
|
||||
{
|
||||
Engine::getSong()->playPattern();
|
||||
}
|
||||
@@ -215,10 +215,10 @@ void PatternStore::updateComboBox()
|
||||
void PatternStore::currentPatternChanged()
|
||||
{
|
||||
// now update all track-labels (the current one has to become white, the others gray)
|
||||
TrackList tl = Engine::getSong()->tracks();
|
||||
const TrackList& tl = Engine::getSong()->tracks();
|
||||
for (Track * t : tl)
|
||||
{
|
||||
if (t->type() == Track::PatternTrack)
|
||||
if (t->type() == Track::Type::Pattern)
|
||||
{
|
||||
t->dataChanged();
|
||||
}
|
||||
@@ -230,7 +230,7 @@ void PatternStore::currentPatternChanged()
|
||||
|
||||
void PatternStore::createClipsForPattern(int pattern)
|
||||
{
|
||||
TrackList tl = tracks();
|
||||
const TrackList& tl = tracks();
|
||||
for (Track * t : tl)
|
||||
{
|
||||
t->createClipsForPattern(pattern);
|
||||
|
||||
@@ -46,7 +46,7 @@ bool PeakController::m_buggedFile;
|
||||
|
||||
PeakController::PeakController( Model * _parent,
|
||||
PeakControllerEffect * _peak_effect ) :
|
||||
Controller( Controller::PeakController, _parent, tr( "Peak Controller" ) ),
|
||||
Controller( ControllerType::Peak, _parent, tr( "Peak Controller" ) ),
|
||||
m_peakEffect( _peak_effect ),
|
||||
m_currentSample( 0.0f )
|
||||
{
|
||||
@@ -161,12 +161,11 @@ void PeakController::loadSettings( const QDomElement & _this )
|
||||
effectId = m_loadCount++;
|
||||
}
|
||||
|
||||
PeakControllerEffectVector::Iterator i;
|
||||
for( i = s_effects.begin(); i != s_effects.end(); ++i )
|
||||
for (const auto& effect : s_effects)
|
||||
{
|
||||
if( (*i)->m_effectId == effectId )
|
||||
if (effect->m_effectId == effectId)
|
||||
{
|
||||
m_peakEffect = *i;
|
||||
m_peakEffect = effect;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -190,16 +189,14 @@ PeakController * PeakController::getControllerBySetting(const QDomElement & _thi
|
||||
{
|
||||
int effectId = _this.attribute( "effectId" ).toInt();
|
||||
|
||||
PeakControllerEffectVector::Iterator i;
|
||||
|
||||
//Backward compatibility for bug in <= 0.4.15 . For >= 1.0.0 ,
|
||||
//foundCount should always be 1 because m_effectId is initialized with rand()
|
||||
int foundCount = 0;
|
||||
if( m_buggedFile == false )
|
||||
{
|
||||
for( i = s_effects.begin(); i != s_effects.end(); ++i )
|
||||
for (const auto& effect : s_effects)
|
||||
{
|
||||
if( (*i)->m_effectId == effectId )
|
||||
if (effect->m_effectId == effectId)
|
||||
{
|
||||
foundCount++;
|
||||
}
|
||||
@@ -208,9 +205,9 @@ PeakController * PeakController::getControllerBySetting(const QDomElement & _thi
|
||||
{
|
||||
m_buggedFile = true;
|
||||
int newEffectId = 0;
|
||||
for( i = s_effects.begin(); i != s_effects.end(); ++i )
|
||||
for (const auto& effect : s_effects)
|
||||
{
|
||||
(*i)->m_effectId = newEffectId++;
|
||||
effect->m_effectId = newEffectId++;
|
||||
}
|
||||
QMessageBox msgBox;
|
||||
msgBox.setIcon( QMessageBox::Information );
|
||||
@@ -231,11 +228,11 @@ PeakController * PeakController::getControllerBySetting(const QDomElement & _thi
|
||||
}
|
||||
m_getCount++; //NB: m_getCount should be increased even m_buggedFile is false
|
||||
|
||||
for( i = s_effects.begin(); i != s_effects.end(); ++i )
|
||||
for (const auto& effect : s_effects)
|
||||
{
|
||||
if( (*i)->m_effectId == effectId )
|
||||
if (effect->m_effectId == effectId)
|
||||
{
|
||||
return (*i)->controller();
|
||||
return effect->controller();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,12 +49,12 @@ namespace lmms
|
||||
*/
|
||||
static const auto KEY_ORDER = std::array
|
||||
{
|
||||
// C CIS D DIS
|
||||
Piano::WhiteKey, Piano::BlackKey, Piano::WhiteKey, Piano::BlackKey,
|
||||
// E F FIS G
|
||||
Piano::WhiteKey, Piano::WhiteKey, Piano::BlackKey, Piano::WhiteKey,
|
||||
// GIS A AIS B
|
||||
Piano::BlackKey, Piano::WhiteKey, Piano::BlackKey, Piano::WhiteKey
|
||||
// C CIS D DIS
|
||||
Piano::KeyType::White, Piano::KeyType::Black, Piano::KeyType::White, Piano::KeyType::Black,
|
||||
// E F FIS G
|
||||
Piano::KeyType::White, Piano::KeyType::White, Piano::KeyType::Black, Piano::KeyType::White,
|
||||
// GIS A AIS B
|
||||
Piano::KeyType::Black, Piano::KeyType::White, Piano::KeyType::Black, Piano::KeyType::White
|
||||
} ;
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ bool Piano::isBlackKey(int key)
|
||||
{
|
||||
int keyCode = key % KeysPerOctave;
|
||||
|
||||
return KEY_ORDER[keyCode] == Piano::BlackKey;
|
||||
return KEY_ORDER[keyCode] == Piano::KeyType::Black;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ static Plugin::Descriptor dummyPluginDescriptor =
|
||||
QT_TRANSLATE_NOOP( "PluginBrowser", "no description" ),
|
||||
"Tobias Doerffel <tobydox/at/users.sf.net>",
|
||||
0x0100,
|
||||
Plugin::Undefined,
|
||||
Plugin::Type::Undefined,
|
||||
&dummyLoader,
|
||||
nullptr
|
||||
} ;
|
||||
|
||||
@@ -85,8 +85,7 @@ void PluginFactory::setupSearchPaths()
|
||||
addRelativeIfExists(PLUGIN_DIR);
|
||||
#endif
|
||||
// Or via an environment variable:
|
||||
QString env_path;
|
||||
if (!(env_path = qgetenv("LMMS_PLUGIN_DIR")).isEmpty())
|
||||
if (const char* env_path = std::getenv("LMMS_PLUGIN_DIR"))
|
||||
QDir::addSearchPath("plugins", env_path);
|
||||
|
||||
QDir::addSearchPath("plugins", ConfigManager::inst()->workingDir() + "plugins");
|
||||
@@ -110,7 +109,7 @@ Plugin::DescriptorList PluginFactory::descriptors() const
|
||||
return m_descriptors.values();
|
||||
}
|
||||
|
||||
Plugin::DescriptorList PluginFactory::descriptors(Plugin::PluginTypes type) const
|
||||
Plugin::DescriptorList PluginFactory::descriptors(Plugin::Type type) const
|
||||
{
|
||||
return m_descriptors.values(type);
|
||||
}
|
||||
|
||||
@@ -34,43 +34,43 @@ const char *PluginIssue::msgFor(const PluginIssueType &it)
|
||||
{
|
||||
switch (it)
|
||||
{
|
||||
case unknownPortFlow:
|
||||
case PluginIssueType::UnknownPortFlow:
|
||||
return "unknown port flow for mandatory port";
|
||||
case unknownPortType:
|
||||
case PluginIssueType::UnknownPortType:
|
||||
return "unknown port type for mandatory port";
|
||||
case tooManyInputChannels:
|
||||
case PluginIssueType::TooManyInputChannels:
|
||||
return "too many audio input channels";
|
||||
case tooManyOutputChannels:
|
||||
case PluginIssueType::TooManyOutputChannels:
|
||||
return "too many audio output channels";
|
||||
case tooManyMidiInputChannels:
|
||||
case PluginIssueType::TooManyMidiInputChannels:
|
||||
return "too many MIDI input channels";
|
||||
case tooManyMidiOutputChannels:
|
||||
case PluginIssueType::TooManyMidiOutputChannels:
|
||||
return "too many MIDI output channels";
|
||||
case noOutputChannel:
|
||||
case PluginIssueType::NoOutputChannel:
|
||||
return "no audio output channel";
|
||||
case portHasNoDef:
|
||||
case PluginIssueType::PortHasNoDef:
|
||||
return "port is missing default value";
|
||||
case portHasNoMin:
|
||||
case PluginIssueType::PortHasNoMin:
|
||||
return "port is missing min value";
|
||||
case portHasNoMax:
|
||||
case PluginIssueType::PortHasNoMax:
|
||||
return "port is missing max value";
|
||||
case minGreaterMax:
|
||||
case PluginIssueType::MinGreaterMax:
|
||||
return "port minimum is greater than maximum";
|
||||
case defaultValueNotInRange:
|
||||
case PluginIssueType::DefaultValueNotInRange:
|
||||
return "default value is not in range [min, max]";
|
||||
case logScaleMinMissing:
|
||||
case PluginIssueType::LogScaleMinMissing:
|
||||
return "logscale requires minimum value";
|
||||
case logScaleMaxMissing:
|
||||
case PluginIssueType::LogScaleMaxMissing:
|
||||
return "logscale requires maximum value";
|
||||
case logScaleMinMaxDifferentSigns:
|
||||
case PluginIssueType::LogScaleMinMaxDifferentSigns:
|
||||
return "logscale with min < 0 < max";
|
||||
case featureNotSupported:
|
||||
case PluginIssueType::FeatureNotSupported:
|
||||
return "required feature not supported";
|
||||
case badPortType:
|
||||
case PluginIssueType::BadPortType:
|
||||
return "unsupported port type";
|
||||
case blacklisted:
|
||||
case PluginIssueType::Blacklisted:
|
||||
return "blacklisted plugin";
|
||||
case noIssue:
|
||||
case PluginIssueType::NoIssue:
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
m_dataMutex()
|
||||
{
|
||||
setJournalling( false );
|
||||
m_previewInstrumentTrack = dynamic_cast<InstrumentTrack *>( Track::create( Track::InstrumentTrack, this ) );
|
||||
m_previewInstrumentTrack = dynamic_cast<InstrumentTrack *>( Track::create( Track::Type::Instrument, this ) );
|
||||
m_previewInstrumentTrack->setJournalling( false );
|
||||
m_previewInstrumentTrack->setPreviewMode( true );
|
||||
}
|
||||
@@ -116,7 +116,7 @@ PreviewTrackContainer * PresetPreviewPlayHandle::s_previewTC;
|
||||
|
||||
|
||||
PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, bool _load_by_plugin, DataFile *dataFile ) :
|
||||
PlayHandle( TypePresetPreviewHandle ),
|
||||
PlayHandle( Type::PresetPreviewHandle ),
|
||||
m_previewNote(nullptr)
|
||||
{
|
||||
setUsesBuffer( false );
|
||||
@@ -169,7 +169,7 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file,
|
||||
// make sure, our preset-preview-track does not appear in any MIDI-
|
||||
// devices list, so just disable receiving/sending MIDI-events at all
|
||||
s_previewTC->previewInstrumentTrack()->
|
||||
midiPort()->setMode( MidiPort::Disabled );
|
||||
midiPort()->setMode( MidiPort::Mode::Disabled );
|
||||
|
||||
Engine::audioEngine()->requestChangeInModel();
|
||||
// create note-play-handle for it
|
||||
|
||||
@@ -58,7 +58,7 @@ void ProjectJournal::undo()
|
||||
|
||||
if( jo )
|
||||
{
|
||||
DataFile curState( DataFile::JournalData );
|
||||
DataFile curState( DataFile::Type::JournalData );
|
||||
jo->saveState( curState, curState.content() );
|
||||
m_redoCheckPoints.push( CheckPoint( c.joID, curState ) );
|
||||
|
||||
@@ -83,7 +83,7 @@ void ProjectJournal::redo()
|
||||
|
||||
if( jo )
|
||||
{
|
||||
DataFile curState( DataFile::JournalData );
|
||||
DataFile curState( DataFile::Type::JournalData );
|
||||
jo->saveState( curState, curState.content() );
|
||||
m_undoCheckPoints.push( CheckPoint( c.joID, curState ) );
|
||||
|
||||
@@ -115,7 +115,7 @@ void ProjectJournal::addJournalCheckPoint( JournallingObject *jo )
|
||||
{
|
||||
m_redoCheckPoints.clear();
|
||||
|
||||
DataFile dataFile( DataFile::JournalData );
|
||||
DataFile dataFile( DataFile::Type::JournalData );
|
||||
jo->saveState( dataFile, dataFile.content() );
|
||||
|
||||
m_undoCheckPoints.push( CheckPoint( jo->id(), dataFile ) );
|
||||
|
||||
@@ -42,15 +42,15 @@ namespace lmms
|
||||
const std::array<ProjectRenderer::FileEncodeDevice, 5> ProjectRenderer::fileEncodeDevices
|
||||
{
|
||||
|
||||
FileEncodeDevice{ ProjectRenderer::WaveFile,
|
||||
FileEncodeDevice{ ProjectRenderer::ExportFileFormat::Wave,
|
||||
QT_TRANSLATE_NOOP( "ProjectRenderer", "WAV (*.wav)" ),
|
||||
".wav", &AudioFileWave::getInst },
|
||||
FileEncodeDevice{ ProjectRenderer::FlacFile,
|
||||
FileEncodeDevice{ ProjectRenderer::ExportFileFormat::Flac,
|
||||
QT_TRANSLATE_NOOP("ProjectRenderer", "FLAC (*.flac)"),
|
||||
".flac",
|
||||
&AudioFileFlac::getInst
|
||||
},
|
||||
FileEncodeDevice{ ProjectRenderer::OggFile,
|
||||
FileEncodeDevice{ ProjectRenderer::ExportFileFormat::Ogg,
|
||||
QT_TRANSLATE_NOOP( "ProjectRenderer", "OGG (*.ogg)" ),
|
||||
".ogg",
|
||||
#ifdef LMMS_HAVE_OGGVORBIS
|
||||
@@ -59,7 +59,7 @@ const std::array<ProjectRenderer::FileEncodeDevice, 5> ProjectRenderer::fileEnco
|
||||
nullptr
|
||||
#endif
|
||||
},
|
||||
FileEncodeDevice{ ProjectRenderer::MP3File,
|
||||
FileEncodeDevice{ ProjectRenderer::ExportFileFormat::MP3,
|
||||
QT_TRANSLATE_NOOP( "ProjectRenderer", "MP3 (*.mp3)" ),
|
||||
".mp3",
|
||||
#ifdef LMMS_HAVE_MP3LAME
|
||||
@@ -71,7 +71,7 @@ const std::array<ProjectRenderer::FileEncodeDevice, 5> ProjectRenderer::fileEnco
|
||||
// Insert your own file-encoder infos here.
|
||||
// Maybe one day the user can add own encoders inside the program.
|
||||
|
||||
FileEncodeDevice{ ProjectRenderer::NumFileFormats, nullptr, nullptr, nullptr }
|
||||
FileEncodeDevice{ ProjectRenderer::ExportFileFormat::Count, nullptr, nullptr, nullptr }
|
||||
|
||||
} ;
|
||||
|
||||
@@ -80,7 +80,7 @@ const std::array<ProjectRenderer::FileEncodeDevice, 5> ProjectRenderer::fileEnco
|
||||
|
||||
ProjectRenderer::ProjectRenderer( const AudioEngine::qualitySettings & qualitySettings,
|
||||
const OutputSettings & outputSettings,
|
||||
ExportFileFormats exportFileFormat,
|
||||
ExportFileFormat exportFileFormat,
|
||||
const QString & outputFilename ) :
|
||||
QThread( Engine::audioEngine() ),
|
||||
m_fileDev( nullptr ),
|
||||
@@ -88,7 +88,7 @@ ProjectRenderer::ProjectRenderer( const AudioEngine::qualitySettings & qualitySe
|
||||
m_progress( 0 ),
|
||||
m_abort( false )
|
||||
{
|
||||
AudioFileDeviceInstantiaton audioEncoderFactory = fileEncodeDevices[exportFileFormat].m_getDevInst;
|
||||
AudioFileDeviceInstantiaton audioEncoderFactory = fileEncodeDevices[static_cast<std::size_t>(exportFileFormat)].m_getDevInst;
|
||||
|
||||
if (audioEncoderFactory)
|
||||
{
|
||||
@@ -110,11 +110,11 @@ ProjectRenderer::ProjectRenderer( const AudioEngine::qualitySettings & qualitySe
|
||||
|
||||
// Little help function for getting file format from a file extension
|
||||
// (only for registered file-encoders).
|
||||
ProjectRenderer::ExportFileFormats ProjectRenderer::getFileFormatFromExtension(
|
||||
ProjectRenderer::ExportFileFormat ProjectRenderer::getFileFormatFromExtension(
|
||||
const QString & _ext )
|
||||
{
|
||||
int idx = 0;
|
||||
while( fileEncodeDevices[idx].m_fileFormat != NumFileFormats )
|
||||
while( fileEncodeDevices[idx].m_fileFormat != ExportFileFormat::Count )
|
||||
{
|
||||
if( QString( fileEncodeDevices[idx].m_extension ) == _ext )
|
||||
{
|
||||
@@ -123,16 +123,16 @@ ProjectRenderer::ExportFileFormats ProjectRenderer::getFileFormatFromExtension(
|
||||
++idx;
|
||||
}
|
||||
|
||||
return( WaveFile ); // Default.
|
||||
return( ExportFileFormat::Wave ); // Default.
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
QString ProjectRenderer::getFileExtensionFromFormat(
|
||||
ExportFileFormats fmt )
|
||||
ExportFileFormat fmt )
|
||||
{
|
||||
return fileEncodeDevices[fmt].m_extension;
|
||||
return fileEncodeDevices[static_cast<std::size_t>(fmt)].m_extension;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -95,12 +95,12 @@ int ProjectVersion::compare(const ProjectVersion & a, const ProjectVersion & b,
|
||||
if(aPat != bPat){ return aPat - bPat; }
|
||||
|
||||
// Decide how many optional identifiers we care about
|
||||
const int maxLabels = qMax(0, limit - 3);
|
||||
const int maxLabels = std::max(0, limit - 3);
|
||||
const auto aLabels = a.getLabels().mid(0, maxLabels);
|
||||
const auto bLabels = b.getLabels().mid(0, maxLabels);
|
||||
|
||||
// We can only compare identifiers if both versions have them
|
||||
const int commonLabels = qMin(aLabels.size(), bLabels.size());
|
||||
const int commonLabels = std::min(aLabels.size(), bLabels.size());
|
||||
// If one version has optional labels and the other doesn't,
|
||||
// the one without them is bigger
|
||||
if (commonLabels == 0){ return bLabels.size() - aLabels.size(); }
|
||||
|
||||
@@ -359,7 +359,7 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf
|
||||
|
||||
memset( m_audioBuffer.get(), 0, m_audioBufferSize );
|
||||
|
||||
ch_cnt_t inputs = qMin<ch_cnt_t>( m_inputCount, DEFAULT_CHANNELS );
|
||||
ch_cnt_t inputs = std::min<ch_cnt_t>(m_inputCount, DEFAULT_CHANNELS);
|
||||
|
||||
if( _in_buf != nullptr && inputs > 0 )
|
||||
{
|
||||
@@ -403,8 +403,8 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf
|
||||
waitForMessage( IdProcessingDone );
|
||||
unlock();
|
||||
|
||||
const ch_cnt_t outputs = qMin<ch_cnt_t>( m_outputCount,
|
||||
DEFAULT_CHANNELS );
|
||||
const ch_cnt_t outputs = std::min<ch_cnt_t>(m_outputCount,
|
||||
DEFAULT_CHANNELS);
|
||||
if( m_splitChannels )
|
||||
{
|
||||
for( ch_cnt_t ch = 0; ch < outputs; ++ch )
|
||||
@@ -427,8 +427,8 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf
|
||||
// clear buffer, if plugin didn't fill up both channels
|
||||
BufferManager::clear( _out_buf, frames );
|
||||
|
||||
for( ch_cnt_t ch = 0; ch <
|
||||
qMin<int>( DEFAULT_CHANNELS, outputs ); ++ch )
|
||||
for (ch_cnt_t ch = 0; ch <
|
||||
std::min<int>(DEFAULT_CHANNELS, outputs); ++ch)
|
||||
{
|
||||
for( fpp_t frame = 0; frame < frames; ++frame )
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace lmms
|
||||
RenderManager::RenderManager(
|
||||
const AudioEngine::qualitySettings & qualitySettings,
|
||||
const OutputSettings & outputSettings,
|
||||
ProjectRenderer::ExportFileFormats fmt,
|
||||
ProjectRenderer::ExportFileFormat fmt,
|
||||
QString outputPath) :
|
||||
m_qualitySettings(qualitySettings),
|
||||
m_oldQualitySettings( Engine::audioEngine()->currentQualitySettings() ),
|
||||
@@ -69,7 +69,7 @@ void RenderManager::renderNextTrack()
|
||||
{
|
||||
m_activeRenderer.reset();
|
||||
|
||||
if( m_tracksToRender.isEmpty() )
|
||||
if (m_tracksToRender.empty())
|
||||
{
|
||||
// nothing left to render
|
||||
restoreMutedState();
|
||||
@@ -97,29 +97,29 @@ void RenderManager::renderNextTrack()
|
||||
// Render the song into individual tracks
|
||||
void RenderManager::renderTracks()
|
||||
{
|
||||
const TrackContainer::TrackList & tl = Engine::getSong()->tracks();
|
||||
const TrackContainer::TrackList& tl = Engine::getSong()->tracks();
|
||||
|
||||
// find all currently unnmuted tracks -- we want to render these.
|
||||
for (const auto& tk : tl)
|
||||
{
|
||||
Track::TrackTypes type = tk->type();
|
||||
Track::Type type = tk->type();
|
||||
|
||||
// Don't render automation tracks
|
||||
if ( tk->isMuted() == false &&
|
||||
( type == Track::InstrumentTrack || type == Track::SampleTrack ) )
|
||||
( type == Track::Type::Instrument || type == Track::Type::Sample ) )
|
||||
{
|
||||
m_unmuted.push_back(tk);
|
||||
}
|
||||
}
|
||||
|
||||
const TrackContainer::TrackList t2 = Engine::patternStore()->tracks();
|
||||
const TrackContainer::TrackList& t2 = Engine::patternStore()->tracks();
|
||||
for (const auto& tk : t2)
|
||||
{
|
||||
Track::TrackTypes type = tk->type();
|
||||
Track::Type type = tk->type();
|
||||
|
||||
// Don't render automation tracks
|
||||
if ( tk->isMuted() == false &&
|
||||
( type == Track::InstrumentTrack || type == Track::SampleTrack ) )
|
||||
( type == Track::Type::Instrument || type == Track::Type::Sample ) )
|
||||
{
|
||||
m_unmuted.push_back(tk);
|
||||
}
|
||||
@@ -169,7 +169,7 @@ void RenderManager::render(QString outputPath)
|
||||
// Unmute all tracks that were muted while rendering tracks
|
||||
void RenderManager::restoreMutedState()
|
||||
{
|
||||
while( !m_unmuted.isEmpty() )
|
||||
while (!m_unmuted.empty())
|
||||
{
|
||||
Track* restoreTrack = m_unmuted.back();
|
||||
m_unmuted.pop_back();
|
||||
|
||||
@@ -247,7 +247,15 @@ void SampleBuffer::update(bool keepSettings)
|
||||
const int fileSizeMax = 300; // MB
|
||||
const int sampleLengthMax = 90; // Minutes
|
||||
|
||||
bool fileLoadError = false;
|
||||
enum class FileLoadError
|
||||
{
|
||||
None,
|
||||
ReadPermissionDenied,
|
||||
TooLarge,
|
||||
Invalid
|
||||
};
|
||||
FileLoadError fileLoadError = FileLoadError::None;
|
||||
|
||||
if (m_audioFile.isEmpty() && m_origData != nullptr && m_origFrames > 0)
|
||||
{
|
||||
// TODO: reverse- and amplification-property is not covered
|
||||
@@ -271,31 +279,40 @@ void SampleBuffer::update(bool keepSettings)
|
||||
m_frames = 0;
|
||||
|
||||
const QFileInfo fileInfo(file);
|
||||
if (fileInfo.size() > fileSizeMax * 1024 * 1024)
|
||||
if (!fileInfo.isReadable())
|
||||
{
|
||||
fileLoadError = true;
|
||||
fileLoadError = FileLoadError::ReadPermissionDenied;
|
||||
}
|
||||
else if (fileInfo.size() > fileSizeMax * 1024 * 1024)
|
||||
{
|
||||
fileLoadError = FileLoadError::TooLarge;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use QFile to handle unicode file names on Windows
|
||||
QFile f(file);
|
||||
SNDFILE * sndFile;
|
||||
SNDFILE * sndFile = nullptr;
|
||||
SF_INFO sfInfo;
|
||||
sfInfo.format = 0;
|
||||
|
||||
if (f.open(QIODevice::ReadOnly) && (sndFile = sf_open_fd(f.handle(), SFM_READ, &sfInfo, false)))
|
||||
{
|
||||
f_cnt_t frames = sfInfo.frames;
|
||||
int rate = sfInfo.samplerate;
|
||||
if (frames / rate > sampleLengthMax * 60)
|
||||
{
|
||||
fileLoadError = true;
|
||||
fileLoadError = FileLoadError::TooLarge;
|
||||
}
|
||||
sf_close(sndFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileLoadError = FileLoadError::Invalid;
|
||||
}
|
||||
f.close();
|
||||
}
|
||||
|
||||
if (!fileLoadError)
|
||||
if (fileLoadError == FileLoadError::None)
|
||||
{
|
||||
#ifdef LMMS_HAVE_OGGVORBIS
|
||||
// workaround for a bug in libsndfile or our libsndfile decoder
|
||||
@@ -322,7 +339,7 @@ void SampleBuffer::update(bool keepSettings)
|
||||
}
|
||||
}
|
||||
|
||||
if (m_frames == 0 || fileLoadError) // if still no frames, bail
|
||||
if (m_frames == 0 || fileLoadError != FileLoadError::None) // if still no frames, bail
|
||||
{
|
||||
// sample couldn't be decoded, create buffer containing
|
||||
// one sample-frame
|
||||
@@ -363,16 +380,35 @@ void SampleBuffer::update(bool keepSettings)
|
||||
}
|
||||
Oscillator::generateAntiAliasUserWaveTable(this);
|
||||
|
||||
if (fileLoadError)
|
||||
if (fileLoadError != FileLoadError::None)
|
||||
{
|
||||
QString title = tr("Fail to open file");
|
||||
QString message = tr("Audio files are limited to %1 MB "
|
||||
"in size and %2 minutes of playing time"
|
||||
).arg(fileSizeMax).arg(sampleLengthMax);
|
||||
QString message;
|
||||
|
||||
switch (fileLoadError)
|
||||
{
|
||||
case FileLoadError::None:
|
||||
// present just to avoid a compiler warning
|
||||
break;
|
||||
|
||||
case FileLoadError::ReadPermissionDenied:
|
||||
message = tr("Read permission denied");
|
||||
break;
|
||||
|
||||
case FileLoadError::TooLarge:
|
||||
message = tr("Audio files are limited to %1 MB "
|
||||
"in size and %2 minutes of playing time"
|
||||
).arg(fileSizeMax).arg(sampleLengthMax);
|
||||
break;
|
||||
|
||||
case FileLoadError::Invalid:
|
||||
message = tr("Invalid audio file");
|
||||
break;
|
||||
}
|
||||
|
||||
if (gui::getGUI() != nullptr)
|
||||
{
|
||||
QMessageBox::information(nullptr,
|
||||
title, message, QMessageBox::Ok);
|
||||
QMessageBox::information(nullptr, title, message, QMessageBox::Ok);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -456,10 +492,10 @@ void SampleBuffer::normalizeSampleRate(const sample_rate_t srcSR, bool keepSetti
|
||||
{
|
||||
auto oldRateToNewRateRatio = static_cast<float>(audioEngineSampleRate()) / oldRate;
|
||||
|
||||
m_startFrame = qBound(0, f_cnt_t(m_startFrame * oldRateToNewRateRatio), m_frames);
|
||||
m_endFrame = qBound(m_startFrame, f_cnt_t(m_endFrame * oldRateToNewRateRatio), m_frames);
|
||||
m_loopStartFrame = qBound(0, f_cnt_t(m_loopStartFrame * oldRateToNewRateRatio), m_frames);
|
||||
m_loopEndFrame = qBound(m_loopStartFrame, f_cnt_t(m_loopEndFrame * oldRateToNewRateRatio), m_frames);
|
||||
m_startFrame = std::clamp(f_cnt_t(m_startFrame * oldRateToNewRateRatio), 0, m_frames);
|
||||
m_endFrame = std::clamp(f_cnt_t(m_endFrame * oldRateToNewRateRatio), m_startFrame, m_frames);
|
||||
m_loopStartFrame = std::clamp(f_cnt_t(m_loopStartFrame * oldRateToNewRateRatio), 0, m_frames);
|
||||
m_loopEndFrame = std::clamp(f_cnt_t(m_loopEndFrame * oldRateToNewRateRatio), m_loopStartFrame, m_frames);
|
||||
m_sampleRate = audioEngineSampleRate();
|
||||
}
|
||||
}
|
||||
@@ -736,9 +772,9 @@ bool SampleBuffer::play(
|
||||
|
||||
|
||||
// this holds the index of the first frame to play
|
||||
f_cnt_t playFrame = qMax(state->m_frameIndex, startFrame);
|
||||
f_cnt_t playFrame = std::max(state->m_frameIndex, startFrame);
|
||||
|
||||
if (loopMode == LoopOff)
|
||||
if (loopMode == LoopMode::Off)
|
||||
{
|
||||
if (playFrame >= endFrame || (endFrame - playFrame) / freqFactor == 0)
|
||||
{
|
||||
@@ -746,7 +782,7 @@ bool SampleBuffer::play(
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (loopMode == LoopOn)
|
||||
else if (loopMode == LoopMode::On)
|
||||
{
|
||||
playFrame = getLoopedIndex(playFrame, loopStartFrame, loopEndFrame);
|
||||
}
|
||||
@@ -786,14 +822,14 @@ bool SampleBuffer::play(
|
||||
// Advance
|
||||
switch (loopMode)
|
||||
{
|
||||
case LoopOff:
|
||||
case LoopMode::Off:
|
||||
playFrame += srcData.input_frames_used;
|
||||
break;
|
||||
case LoopOn:
|
||||
case LoopMode::On:
|
||||
playFrame += srcData.input_frames_used;
|
||||
playFrame = getLoopedIndex(playFrame, loopStartFrame, loopEndFrame);
|
||||
break;
|
||||
case LoopPingPong:
|
||||
case LoopMode::PingPong:
|
||||
{
|
||||
f_cnt_t left = srcData.input_frames_used;
|
||||
if (state->isBackwards())
|
||||
@@ -825,14 +861,14 @@ bool SampleBuffer::play(
|
||||
// Advance
|
||||
switch (loopMode)
|
||||
{
|
||||
case LoopOff:
|
||||
case LoopMode::Off:
|
||||
playFrame += frames;
|
||||
break;
|
||||
case LoopOn:
|
||||
case LoopMode::On:
|
||||
playFrame += frames;
|
||||
playFrame = getLoopedIndex(playFrame, loopStartFrame, loopEndFrame);
|
||||
break;
|
||||
case LoopPingPong:
|
||||
case LoopMode::PingPong:
|
||||
{
|
||||
f_cnt_t left = frames;
|
||||
if (state->isBackwards())
|
||||
@@ -883,14 +919,14 @@ sampleFrame * SampleBuffer::getSampleFragment(
|
||||
f_cnt_t end
|
||||
) const
|
||||
{
|
||||
if (loopMode == LoopOff)
|
||||
if (loopMode == LoopMode::Off)
|
||||
{
|
||||
if (index + frames <= end)
|
||||
{
|
||||
return m_data + index;
|
||||
}
|
||||
}
|
||||
else if (loopMode == LoopOn)
|
||||
else if (loopMode == LoopMode::On)
|
||||
{
|
||||
if (index + frames <= loopEnd)
|
||||
{
|
||||
@@ -907,20 +943,20 @@ sampleFrame * SampleBuffer::getSampleFragment(
|
||||
|
||||
*tmp = MM_ALLOC<sampleFrame>( frames);
|
||||
|
||||
if (loopMode == LoopOff)
|
||||
if (loopMode == LoopMode::Off)
|
||||
{
|
||||
f_cnt_t available = end - index;
|
||||
memcpy(*tmp, m_data + index, available * BYTES_PER_FRAME);
|
||||
memset(*tmp + available, 0, (frames - available) * BYTES_PER_FRAME);
|
||||
}
|
||||
else if (loopMode == LoopOn)
|
||||
else if (loopMode == LoopMode::On)
|
||||
{
|
||||
f_cnt_t copied = qMin(frames, loopEnd - index);
|
||||
f_cnt_t copied = std::min(frames, loopEnd - index);
|
||||
memcpy(*tmp, m_data + index, copied * BYTES_PER_FRAME);
|
||||
f_cnt_t loopFrames = loopEnd - loopStart;
|
||||
while (copied < frames)
|
||||
{
|
||||
f_cnt_t todo = qMin(frames - copied, loopFrames);
|
||||
f_cnt_t todo = std::min(frames - copied, loopFrames);
|
||||
memcpy(*tmp + copied, m_data + loopStart, todo * BYTES_PER_FRAME);
|
||||
copied += todo;
|
||||
}
|
||||
@@ -936,7 +972,7 @@ sampleFrame * SampleBuffer::getSampleFragment(
|
||||
|
||||
if (currentBackwards)
|
||||
{
|
||||
copied = qMin(frames, pos - loopStart);
|
||||
copied = std::min(frames, pos - loopStart);
|
||||
for (int i = 0; i < copied; i++)
|
||||
{
|
||||
(*tmp)[i][0] = m_data[pos - i][0];
|
||||
@@ -947,7 +983,7 @@ sampleFrame * SampleBuffer::getSampleFragment(
|
||||
}
|
||||
else
|
||||
{
|
||||
copied = qMin(frames, loopEnd - pos);
|
||||
copied = std::min(frames, loopEnd - pos);
|
||||
memcpy(*tmp, m_data + pos, copied * BYTES_PER_FRAME);
|
||||
pos += copied;
|
||||
if (pos == loopEnd) { currentBackwards = true; }
|
||||
@@ -957,7 +993,7 @@ sampleFrame * SampleBuffer::getSampleFragment(
|
||||
{
|
||||
if (currentBackwards)
|
||||
{
|
||||
f_cnt_t todo = qMin(frames - copied, pos - loopStart);
|
||||
f_cnt_t todo = std::min(frames - copied, pos - loopStart);
|
||||
for (int i = 0; i < todo; i++)
|
||||
{
|
||||
(*tmp)[copied + i][0] = m_data[pos - i][0];
|
||||
@@ -969,7 +1005,7 @@ sampleFrame * SampleBuffer::getSampleFragment(
|
||||
}
|
||||
else
|
||||
{
|
||||
f_cnt_t todo = qMin(frames - copied, loopEnd - pos);
|
||||
f_cnt_t todo = std::min(frames - copied, loopEnd - pos);
|
||||
memcpy(*tmp + copied, m_data + pos, todo * BYTES_PER_FRAME);
|
||||
pos += todo;
|
||||
copied += todo;
|
||||
@@ -1085,8 +1121,8 @@ void SampleBuffer::visualize(
|
||||
|
||||
const float trueRmsData = (rmsData[0] + rmsData[1]) / 2 / fpp;
|
||||
const float sqrtRmsData = sqrt(trueRmsData);
|
||||
const float maxRmsData = qBound(minData, sqrtRmsData, maxData);
|
||||
const float minRmsData = qBound(minData, -sqrtRmsData, maxData);
|
||||
const float maxRmsData = std::clamp(sqrtRmsData, minData, maxData);
|
||||
const float minRmsData = std::clamp(-sqrtRmsData, minData, maxData);
|
||||
|
||||
// If nbFrames >= w, we can use curPixel to calculate X
|
||||
// but if nbFrames < w, we need to calculate it proportionally
|
||||
@@ -1291,7 +1327,7 @@ QString & SampleBuffer::toBase64(QString & dst) const
|
||||
|
||||
while (frameCnt < m_frames)
|
||||
{
|
||||
f_cnt_t remaining = qMin<f_cnt_t>(FRAMES_PER_BUF, m_frames - frameCnt);
|
||||
f_cnt_t remaining = std::min<f_cnt_t>(FRAMES_PER_BUF, m_frames - frameCnt);
|
||||
FLAC__int32 buf[FRAMES_PER_BUF * DEFAULT_CHANNELS];
|
||||
for (f_cnt_t f = 0; f < remaining; ++f)
|
||||
{
|
||||
@@ -1353,7 +1389,7 @@ SampleBuffer * SampleBuffer::resample(const sample_rate_t srcSR, const sample_ra
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error: src_new() failed in sample_buffer.cpp!\n");
|
||||
printf("Error: src_new() failed in SampleBuffer.cpp!\n");
|
||||
}
|
||||
dstSB->update();
|
||||
return dstSB;
|
||||
@@ -1576,7 +1612,7 @@ SampleBuffer::handleState::handleState(bool varyingPitch, int interpolationMode)
|
||||
|
||||
if ((m_resamplingData = src_new(interpolationMode, DEFAULT_CHANNELS, &error)) == nullptr)
|
||||
{
|
||||
qDebug("Error: src_new() failed in sample_buffer.cpp!\n");
|
||||
qDebug("Error: src_new() failed in SampleBuffer.cpp!\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ SampleClip::SampleClip( Track * _track ) :
|
||||
this, SLOT(updateLength()));
|
||||
|
||||
//care about positionmarker
|
||||
gui::TimeLineWidget* timeLine = Engine::getSong()->getPlayPos( Engine::getSong()->Mode_PlaySong ).m_timeLine;
|
||||
gui::TimeLineWidget* timeLine = Engine::getSong()->getPlayPos( Song::PlayMode::Song ).m_timeLine;
|
||||
if( timeLine )
|
||||
{
|
||||
connect( timeLine, SIGNAL(positionMarkerMoved()), this, SLOT(playbackPositionChanged()));
|
||||
@@ -74,11 +74,11 @@ SampleClip::SampleClip( Track * _track ) :
|
||||
|
||||
switch( getTrack()->trackContainer()->type() )
|
||||
{
|
||||
case TrackContainer::PatternContainer:
|
||||
case TrackContainer::Type::Pattern:
|
||||
setAutoResize( true );
|
||||
break;
|
||||
|
||||
case TrackContainer::SongContainer:
|
||||
case TrackContainer::Type::Song:
|
||||
// move down
|
||||
default:
|
||||
setAutoResize( false );
|
||||
@@ -117,7 +117,7 @@ SampleClip::~SampleClip()
|
||||
|
||||
void SampleClip::changeLength( const TimePos & _length )
|
||||
{
|
||||
Clip::changeLength( qMax( static_cast<int>( _length ), 1 ) );
|
||||
Clip::changeLength(std::max(static_cast<int>(_length), 1));
|
||||
}
|
||||
|
||||
|
||||
@@ -143,23 +143,26 @@ void SampleClip::setSampleBuffer( SampleBuffer* sb )
|
||||
|
||||
|
||||
|
||||
void SampleClip::setSampleFile( const QString & _sf )
|
||||
void SampleClip::setSampleFile(const QString & sf)
|
||||
{
|
||||
int length;
|
||||
if ( _sf.isEmpty() )
|
||||
{ //When creating an empty sample clip make it a bar long
|
||||
float nom = Engine::getSong()->getTimeSigModel().getNumerator();
|
||||
float den = Engine::getSong()->getTimeSigModel().getDenominator();
|
||||
length = DefaultTicksPerBar * ( nom / den );
|
||||
}
|
||||
else
|
||||
{ //Otherwise set it to the sample's length
|
||||
m_sampleBuffer->setAudioFile( _sf );
|
||||
int length = 0;
|
||||
|
||||
if (!sf.isEmpty())
|
||||
{
|
||||
m_sampleBuffer->setAudioFile(sf);
|
||||
length = sampleLength();
|
||||
}
|
||||
changeLength(length);
|
||||
|
||||
setStartTimeOffset( 0 );
|
||||
if (length == 0)
|
||||
{
|
||||
//If there is no sample, make the clip a bar long
|
||||
float nom = Engine::getSong()->getTimeSigModel().getNumerator();
|
||||
float den = Engine::getSong()->getTimeSigModel().getDenominator();
|
||||
length = DefaultTicksPerBar * (nom / den);
|
||||
}
|
||||
|
||||
changeLength(length);
|
||||
setStartTimeOffset(0);
|
||||
|
||||
emit sampleChanged();
|
||||
emit playbackPositionChanged();
|
||||
@@ -179,7 +182,7 @@ void SampleClip::toggleRecord()
|
||||
|
||||
void SampleClip::playbackPositionChanged()
|
||||
{
|
||||
Engine::audioEngine()->removePlayHandlesOfTypes( getTrack(), PlayHandle::TypeSamplePlayHandle );
|
||||
Engine::audioEngine()->removePlayHandlesOfTypes( getTrack(), PlayHandle::Type::SamplePlayHandle );
|
||||
auto st = dynamic_cast<SampleTrack*>(getTrack());
|
||||
st->setPlayingClips( false );
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace lmms
|
||||
|
||||
|
||||
SamplePlayHandle::SamplePlayHandle( SampleBuffer* sampleBuffer , bool ownAudioPort ) :
|
||||
PlayHandle( TypeSamplePlayHandle ),
|
||||
PlayHandle( Type::SamplePlayHandle ),
|
||||
m_sampleBuffer( sharedObject::ref( sampleBuffer ) ),
|
||||
m_doneMayReturnTrue( true ),
|
||||
m_frame( 0 ),
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace lmms
|
||||
|
||||
|
||||
SampleRecordHandle::SampleRecordHandle( SampleClip* clip ) :
|
||||
PlayHandle( TypeSamplePlayHandle ),
|
||||
PlayHandle( Type::SamplePlayHandle ),
|
||||
m_framesRecorded( 0 ),
|
||||
m_minLength( clip->length() ),
|
||||
m_track( clip->getTrack() ),
|
||||
|
||||
@@ -68,7 +68,7 @@ tick_t TimePos::s_ticksPerBar = DefaultTicksPerBar;
|
||||
Song::Song() :
|
||||
TrackContainer(),
|
||||
m_globalAutomationTrack( dynamic_cast<AutomationTrack *>(
|
||||
Track::create( Track::HiddenAutomationTrack,
|
||||
Track::create( Track::Type::HiddenAutomation,
|
||||
this ) ) ),
|
||||
m_tempoModel( DefaultTempo, MinTempo, MaxTempo, this, tr( "Tempo" ) ),
|
||||
m_timeSigModel( this ),
|
||||
@@ -89,7 +89,7 @@ Song::Song() :
|
||||
m_savingProject( false ),
|
||||
m_loadingProject( false ),
|
||||
m_isCancelled( false ),
|
||||
m_playMode( Mode_None ),
|
||||
m_playMode( PlayMode::None ),
|
||||
m_length( 0 ),
|
||||
m_midiClipToPlay( nullptr ),
|
||||
m_loopMidiClip( false ),
|
||||
@@ -117,7 +117,7 @@ Song::Song() :
|
||||
this, SLOT(masterPitchChanged()));*/
|
||||
|
||||
qRegisterMetaType<lmms::Note>( "lmms::Note" );
|
||||
setType( SongContainer );
|
||||
setType( Type::Song );
|
||||
|
||||
for (auto& scale : m_scales) {scale = std::make_shared<Scale>();}
|
||||
for (auto& keymap : m_keymaps) {keymap = std::make_shared<Keymap>();}
|
||||
@@ -186,11 +186,11 @@ void Song::setTimeSignature()
|
||||
|
||||
void Song::savePos()
|
||||
{
|
||||
gui::TimeLineWidget* tl = m_playPos[m_playMode].m_timeLine;
|
||||
gui::TimeLineWidget* tl = getPlayPos().m_timeLine;
|
||||
|
||||
if( tl != nullptr )
|
||||
{
|
||||
tl->savePos( m_playPos[m_playMode] );
|
||||
tl->savePos( getPlayPos() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ void Song::processNextBuffer()
|
||||
if (!m_playing) { return; }
|
||||
|
||||
// At the beginning of the song, we have to reset the LFOs
|
||||
if (m_playMode == Mode_PlaySong && getPlayPos() == 0)
|
||||
if (m_playMode == PlayMode::Song && getPlayPos() == 0)
|
||||
{
|
||||
EnvelopeAndLfoParameters::instances()->reset();
|
||||
}
|
||||
@@ -216,11 +216,11 @@ void Song::processNextBuffer()
|
||||
// Determine the list of tracks to play and the clip number
|
||||
switch (m_playMode)
|
||||
{
|
||||
case Mode_PlaySong:
|
||||
case PlayMode::Song:
|
||||
trackList = tracks();
|
||||
break;
|
||||
|
||||
case Mode_PlayPattern:
|
||||
case PlayMode::Pattern:
|
||||
if (Engine::patternStore()->numOfPatterns() > 0)
|
||||
{
|
||||
clipNum = Engine::patternStore()->currentPattern();
|
||||
@@ -228,7 +228,7 @@ void Song::processNextBuffer()
|
||||
}
|
||||
break;
|
||||
|
||||
case Mode_PlayMidiClip:
|
||||
case PlayMode::MidiClip:
|
||||
if (m_midiClipToPlay)
|
||||
{
|
||||
clipNum = m_midiClipToPlay->getTrack()->getClipNum(m_midiClipToPlay);
|
||||
@@ -291,11 +291,11 @@ void Song::processNextBuffer()
|
||||
|
||||
// If we are playing a pattern track, or a MIDI clip with no loop enabled,
|
||||
// loop back to the beginning when we reach the end
|
||||
if (m_playMode == Mode_PlayPattern)
|
||||
if (m_playMode == PlayMode::Pattern)
|
||||
{
|
||||
enforceLoop(TimePos{0}, TimePos{Engine::patternStore()->lengthOfCurrentPattern(), 0});
|
||||
}
|
||||
else if (m_playMode == Mode_PlayMidiClip && m_loopMidiClip && !loopEnabled)
|
||||
else if (m_playMode == PlayMode::MidiClip && m_loopMidiClip && !loopEnabled)
|
||||
{
|
||||
enforceLoop(TimePos{0}, m_midiClipToPlay->length());
|
||||
}
|
||||
@@ -349,9 +349,9 @@ void Song::processNextBuffer()
|
||||
frameOffsetInPeriod += framesToPlay;
|
||||
frameOffsetInTick += framesToPlay;
|
||||
getPlayPos().setCurrentFrame(frameOffsetInTick);
|
||||
m_elapsedMilliSeconds[m_playMode] += TimePos::ticksToMilliseconds(framesToPlay / framesPerTick, getTempo());
|
||||
m_elapsedBars = m_playPos[Mode_PlaySong].getBar();
|
||||
m_elapsedTicks = (m_playPos[Mode_PlaySong].getTicks() % ticksPerBar()) / 48;
|
||||
m_elapsedMilliSeconds[static_cast<std::size_t>(m_playMode)] += TimePos::ticksToMilliseconds(framesToPlay / framesPerTick, getTempo());
|
||||
m_elapsedBars = getPlayPos(PlayMode::Song).getBar();
|
||||
m_elapsedTicks = (getPlayPos(PlayMode::Song).getTicks() % ticksPerBar()) / 48;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,12 +367,12 @@ void Song::processAutomations(const TrackList &tracklist, TimePos timeStart, fpp
|
||||
|
||||
switch (m_playMode)
|
||||
{
|
||||
case Mode_PlaySong:
|
||||
case PlayMode::Song:
|
||||
break;
|
||||
case Mode_PlayPattern:
|
||||
case PlayMode::Pattern:
|
||||
{
|
||||
Q_ASSERT(tracklist.size() == 1);
|
||||
Q_ASSERT(tracklist.at(0)->type() == Track::PatternTrack);
|
||||
Q_ASSERT(tracklist.at(0)->type() == Track::Type::Pattern);
|
||||
auto patternTrack = dynamic_cast<PatternTrack*>(tracklist.at(0));
|
||||
container = Engine::patternStore();
|
||||
clipNum = patternTrack->patternIndex();
|
||||
@@ -383,12 +383,12 @@ void Song::processAutomations(const TrackList &tracklist, TimePos timeStart, fpp
|
||||
}
|
||||
|
||||
values = container->automatedValuesAt(timeStart, clipNum);
|
||||
TrackList tracks = container->tracks();
|
||||
const TrackList& tracks = container->tracks();
|
||||
|
||||
Track::clipVector clips;
|
||||
for (Track* track : tracks)
|
||||
{
|
||||
if (track->type() == Track::AutomationTrack) {
|
||||
if (track->type() == Track::Type::Automation) {
|
||||
track->getClipsInRange(clips, 0, timeStart);
|
||||
}
|
||||
}
|
||||
@@ -444,12 +444,12 @@ void Song::setModified(bool value)
|
||||
|
||||
bool Song::isExportDone() const
|
||||
{
|
||||
return !isExporting() || m_playPos[m_playMode] >= m_exportSongEnd;
|
||||
return !isExporting() || getPlayPos() >= m_exportSongEnd;
|
||||
}
|
||||
|
||||
int Song::getExportProgress() const
|
||||
{
|
||||
TimePos pos = m_playPos[m_playMode];
|
||||
TimePos pos = getPlayPos();
|
||||
|
||||
if (pos >= m_exportSongEnd)
|
||||
{
|
||||
@@ -486,7 +486,7 @@ void Song::playSong()
|
||||
stop();
|
||||
}
|
||||
|
||||
m_playMode = Mode_PlaySong;
|
||||
m_playMode = PlayMode::Song;
|
||||
m_playing = true;
|
||||
m_paused = false;
|
||||
|
||||
@@ -525,7 +525,7 @@ void Song::playPattern()
|
||||
stop();
|
||||
}
|
||||
|
||||
m_playMode = Mode_PlayPattern;
|
||||
m_playMode = PlayMode::Pattern;
|
||||
m_playing = true;
|
||||
m_paused = false;
|
||||
|
||||
@@ -551,7 +551,7 @@ void Song::playMidiClip( const MidiClip* midiClipToPlay, bool loop )
|
||||
|
||||
if( m_midiClipToPlay != nullptr )
|
||||
{
|
||||
m_playMode = Mode_PlayMidiClip;
|
||||
m_playMode = PlayMode::MidiClip;
|
||||
m_playing = true;
|
||||
m_paused = false;
|
||||
}
|
||||
@@ -589,14 +589,14 @@ void Song::updateLength()
|
||||
|
||||
|
||||
|
||||
void Song::setPlayPos( tick_t ticks, PlayModes playMode )
|
||||
void Song::setPlayPos( tick_t ticks, PlayMode playMode )
|
||||
{
|
||||
tick_t ticksFromPlayMode = m_playPos[playMode].getTicks();
|
||||
tick_t ticksFromPlayMode = getPlayPos(playMode).getTicks();
|
||||
m_elapsedTicks += ticksFromPlayMode - ticks;
|
||||
m_elapsedMilliSeconds[playMode] += TimePos::ticksToMilliseconds( ticks - ticksFromPlayMode, getTempo() );
|
||||
m_playPos[playMode].setTicks( ticks );
|
||||
m_playPos[playMode].setCurrentFrame( 0.0f );
|
||||
m_playPos[playMode].setJumped( true );
|
||||
m_elapsedMilliSeconds[static_cast<std::size_t>(playMode)] += TimePos::ticksToMilliseconds( ticks - ticksFromPlayMode, getTempo() );
|
||||
getPlayPos(playMode).setTicks( ticks );
|
||||
getPlayPos(playMode).setCurrentFrame( 0.0f );
|
||||
getPlayPos(playMode).setJumped( true );
|
||||
|
||||
// send a signal if playposition changes during playback
|
||||
if( isPlaying() )
|
||||
@@ -634,7 +634,7 @@ void Song::togglePause()
|
||||
void Song::stop()
|
||||
{
|
||||
// do not stop/reset things again if we're stopped already
|
||||
if( m_playMode == Mode_None )
|
||||
if( m_playMode == PlayMode::None )
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -644,7 +644,7 @@ void Song::stop()
|
||||
// To avoid race conditions with the processing threads
|
||||
Engine::audioEngine()->requestChangeInModel();
|
||||
|
||||
TimeLineWidget * tl = m_playPos[m_playMode].m_timeLine;
|
||||
TimeLineWidget * tl = getPlayPos().m_timeLine;
|
||||
m_paused = false;
|
||||
m_recording = true;
|
||||
|
||||
@@ -652,41 +652,41 @@ void Song::stop()
|
||||
{
|
||||
switch( tl->behaviourAtStop() )
|
||||
{
|
||||
case TimeLineWidget::BackToZero:
|
||||
m_playPos[m_playMode].setTicks(0);
|
||||
m_elapsedMilliSeconds[m_playMode] = 0;
|
||||
case TimeLineWidget::BehaviourAtStopState::BackToZero:
|
||||
getPlayPos().setTicks(0);
|
||||
m_elapsedMilliSeconds[static_cast<std::size_t>(m_playMode)] = 0;
|
||||
break;
|
||||
|
||||
case TimeLineWidget::BackToStart:
|
||||
case TimeLineWidget::BehaviourAtStopState::BackToStart:
|
||||
if( tl->savedPos() >= 0 )
|
||||
{
|
||||
m_playPos[m_playMode].setTicks(tl->savedPos().getTicks());
|
||||
getPlayPos().setTicks(tl->savedPos().getTicks());
|
||||
setToTime(tl->savedPos());
|
||||
|
||||
tl->savePos( -1 );
|
||||
}
|
||||
break;
|
||||
|
||||
case TimeLineWidget::KeepStopPosition:
|
||||
case TimeLineWidget::BehaviourAtStopState::KeepStopPosition:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_playPos[m_playMode].setTicks( 0 );
|
||||
m_elapsedMilliSeconds[m_playMode] = 0;
|
||||
getPlayPos().setTicks( 0 );
|
||||
m_elapsedMilliSeconds[static_cast<std::size_t>(m_playMode)] = 0;
|
||||
}
|
||||
m_playing = false;
|
||||
|
||||
m_elapsedMilliSeconds[Mode_None] = m_elapsedMilliSeconds[m_playMode];
|
||||
m_playPos[Mode_None].setTicks(m_playPos[m_playMode].getTicks());
|
||||
m_elapsedMilliSeconds[static_cast<std::size_t>(PlayMode::None)] = m_elapsedMilliSeconds[static_cast<std::size_t>(m_playMode)];
|
||||
getPlayPos(PlayMode::None).setTicks(getPlayPos().getTicks());
|
||||
|
||||
m_playPos[m_playMode].setCurrentFrame( 0 );
|
||||
getPlayPos().setCurrentFrame( 0 );
|
||||
|
||||
m_vstSyncController.setPlaybackState( m_exporting );
|
||||
m_vstSyncController.setAbsolutePosition(
|
||||
m_playPos[m_playMode].getTicks()
|
||||
+ m_playPos[m_playMode].currentFrame()
|
||||
getPlayPos().getTicks()
|
||||
+ getPlayPos().currentFrame()
|
||||
/ (double) Engine::framesPerTick() );
|
||||
|
||||
// remove all note-play-handles that are active
|
||||
@@ -701,7 +701,7 @@ void Song::stop()
|
||||
}
|
||||
m_oldAutomatedValues.clear();
|
||||
|
||||
m_playMode = Mode_None;
|
||||
m_playMode = PlayMode::None;
|
||||
|
||||
Engine::audioEngine()->doneChangeInModel();
|
||||
|
||||
@@ -721,19 +721,19 @@ void Song::startExport()
|
||||
|
||||
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_exportSongBegin = m_exportLoopBegin = getPlayPos(PlayMode::Song).m_timeLine->loopBegin();
|
||||
m_exportSongEnd = m_exportLoopEnd = getPlayPos(PlayMode::Song).m_timeLine->loopEnd();
|
||||
|
||||
m_playPos[Mode_PlaySong].setTicks( m_playPos[Mode_PlaySong].m_timeLine->loopBegin().getTicks() );
|
||||
getPlayPos(PlayMode::Song).setTicks( getPlayPos(PlayMode::Song).m_timeLine->loopBegin().getTicks() );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_exportSongEnd = TimePos(m_length, 0);
|
||||
|
||||
// Handle potentially ridiculous loop points gracefully.
|
||||
if (m_loopRenderCount > 1 && m_playPos[Mode_PlaySong].m_timeLine->loopEnd() > m_exportSongEnd)
|
||||
if (m_loopRenderCount > 1 && getPlayPos(PlayMode::Song).m_timeLine->loopEnd() > m_exportSongEnd)
|
||||
{
|
||||
m_exportSongEnd = m_playPos[Mode_PlaySong].m_timeLine->loopEnd();
|
||||
m_exportSongEnd = getPlayPos(PlayMode::Song).m_timeLine->loopEnd();
|
||||
}
|
||||
|
||||
if (!m_exportLoop)
|
||||
@@ -741,17 +741,17 @@ void Song::startExport()
|
||||
|
||||
m_exportSongBegin = TimePos(0,0);
|
||||
// FIXME: remove this check once we load timeline in headless mode
|
||||
if (m_playPos[Mode_PlaySong].m_timeLine)
|
||||
if (getPlayPos(PlayMode::Song).m_timeLine)
|
||||
{
|
||||
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() : TimePos(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() : TimePos(0,0);
|
||||
m_exportLoopBegin = getPlayPos(PlayMode::Song).m_timeLine->loopBegin() < m_exportSongEnd &&
|
||||
getPlayPos(PlayMode::Song).m_timeLine->loopEnd() <= m_exportSongEnd ?
|
||||
getPlayPos(PlayMode::Song).m_timeLine->loopBegin() : TimePos(0,0);
|
||||
m_exportLoopEnd = getPlayPos(PlayMode::Song).m_timeLine->loopBegin() < m_exportSongEnd &&
|
||||
getPlayPos(PlayMode::Song).m_timeLine->loopEnd() <= m_exportSongEnd ?
|
||||
getPlayPos(PlayMode::Song).m_timeLine->loopEnd() : TimePos(0,0);
|
||||
}
|
||||
|
||||
m_playPos[Mode_PlaySong].setTicks( 0 );
|
||||
getPlayPos(PlayMode::Song).setTicks( 0 );
|
||||
}
|
||||
|
||||
m_exportEffectiveLength = (m_exportLoopBegin - m_exportSongBegin) + (m_exportLoopEnd - m_exportLoopBegin)
|
||||
@@ -784,7 +784,7 @@ void Song::insertBar()
|
||||
{
|
||||
// FIXME journal batch of tracks instead of each track individually
|
||||
if (track->numOfClips() > 0) { track->addJournalCheckPoint(); }
|
||||
track->insertBar(m_playPos[Mode_PlaySong]);
|
||||
track->insertBar(getPlayPos(PlayMode::Song));
|
||||
}
|
||||
m_tracksMutex.unlock();
|
||||
}
|
||||
@@ -799,7 +799,7 @@ void Song::removeBar()
|
||||
{
|
||||
// FIXME journal batch of tracks instead of each track individually
|
||||
if (track->numOfClips() > 0) { track->addJournalCheckPoint(); }
|
||||
track->removeBar(m_playPos[Mode_PlaySong]);
|
||||
track->removeBar(getPlayPos(PlayMode::Song));
|
||||
}
|
||||
m_tracksMutex.unlock();
|
||||
}
|
||||
@@ -809,7 +809,7 @@ void Song::removeBar()
|
||||
|
||||
void Song::addPatternTrack()
|
||||
{
|
||||
Track * t = Track::create(Track::PatternTrack, this);
|
||||
Track * t = Track::create(Track::Type::Pattern, this);
|
||||
Engine::patternStore()->setCurrentPattern(dynamic_cast<PatternTrack*>(t)->patternIndex());
|
||||
}
|
||||
|
||||
@@ -818,7 +818,7 @@ void Song::addPatternTrack()
|
||||
|
||||
void Song::addSampleTrack()
|
||||
{
|
||||
( void )Track::create( Track::SampleTrack, this );
|
||||
( void )Track::create( Track::Type::Sample, this );
|
||||
}
|
||||
|
||||
|
||||
@@ -826,7 +826,7 @@ void Song::addSampleTrack()
|
||||
|
||||
void Song::addAutomationTrack()
|
||||
{
|
||||
( void )Track::create( Track::AutomationTrack, this );
|
||||
( void )Track::create( Track::Type::Automation, this );
|
||||
}
|
||||
|
||||
|
||||
@@ -840,7 +840,9 @@ bpm_t Song::getTempo()
|
||||
|
||||
AutomatedValueMap Song::automatedValuesAt(TimePos time, int clipNum) const
|
||||
{
|
||||
return TrackContainer::automatedValuesFromTracks(TrackList{m_globalAutomationTrack} << tracks(), time, clipNum);
|
||||
auto trackList = TrackList{m_globalAutomationTrack};
|
||||
trackList.insert(trackList.end(), tracks().begin(), tracks().end());
|
||||
return TrackContainer::automatedValuesFromTracks(trackList, time, clipNum);
|
||||
}
|
||||
|
||||
|
||||
@@ -857,9 +859,9 @@ void Song::clearProject()
|
||||
stop();
|
||||
}
|
||||
|
||||
for( int i = 0; i < Mode_Count; i++ )
|
||||
for( int i = 0; i < PlayModeCount; i++ )
|
||||
{
|
||||
setPlayPos( 0, ( PlayModes )i );
|
||||
setPlayPos( 0, ( PlayMode )i );
|
||||
}
|
||||
|
||||
|
||||
@@ -958,15 +960,15 @@ void Song::createNewProject()
|
||||
setProjectFileName("");
|
||||
|
||||
Track * t;
|
||||
t = Track::create( Track::InstrumentTrack, this );
|
||||
t = Track::create( Track::Type::Instrument, this );
|
||||
dynamic_cast<InstrumentTrack * >( t )->loadInstrument(
|
||||
"tripleoscillator" );
|
||||
t = Track::create(Track::InstrumentTrack, Engine::patternStore());
|
||||
t = Track::create(Track::Type::Instrument, Engine::patternStore());
|
||||
dynamic_cast<InstrumentTrack * >( t )->loadInstrument(
|
||||
"kicker" );
|
||||
Track::create( Track::SampleTrack, this );
|
||||
Track::create( Track::PatternTrack, this );
|
||||
Track::create( Track::AutomationTrack, this );
|
||||
Track::create( Track::Type::Sample, this );
|
||||
Track::create( Track::Type::Pattern, this );
|
||||
Track::create( Track::Type::Automation, this );
|
||||
|
||||
m_tempoModel.setInitValue( DefaultTempo );
|
||||
m_timeSigModel.reset();
|
||||
@@ -1078,10 +1080,10 @@ void Song::loadProject( const QString & fileName )
|
||||
m_masterVolumeModel.loadSettings( dataFile.head(), "mastervol" );
|
||||
m_masterPitchModel.loadSettings( dataFile.head(), "masterpitch" );
|
||||
|
||||
if( m_playPos[Mode_PlaySong].m_timeLine )
|
||||
if( getPlayPos(PlayMode::Song).m_timeLine )
|
||||
{
|
||||
// reset loop-point-state
|
||||
m_playPos[Mode_PlaySong].m_timeLine->toggleLoopPoints( 0 );
|
||||
getPlayPos(PlayMode::Song).m_timeLine->toggleLoopPoints( 0 );
|
||||
}
|
||||
|
||||
if( !dataFile.content().firstChildElement( "track" ).isNull() )
|
||||
@@ -1117,7 +1119,7 @@ void Song::loadProject( const QString & fileName )
|
||||
if( nd.isElement() && nd.nodeName() == "track" )
|
||||
{
|
||||
++m_nLoadingTrack;
|
||||
if (nd.toElement().attribute("type").toInt() == Track::PatternTrack)
|
||||
if (static_cast<Track::Type>(nd.toElement().attribute("type").toInt()) == Track::Type::Pattern)
|
||||
{
|
||||
n += nd.toElement().elementsByTagName("patterntrack").at(0)
|
||||
.toElement().firstChildElement().childNodes().count();
|
||||
@@ -1165,9 +1167,9 @@ void Song::loadProject( const QString & fileName )
|
||||
{
|
||||
getGUI()->getProjectNotes()->SerializingObject::restoreState( node.toElement() );
|
||||
}
|
||||
else if( node.nodeName() == m_playPos[Mode_PlaySong].m_timeLine->nodeName() )
|
||||
else if( node.nodeName() == getPlayPos(PlayMode::Song).m_timeLine->nodeName() )
|
||||
{
|
||||
m_playPos[Mode_PlaySong].m_timeLine->restoreState( node.toElement() );
|
||||
getPlayPos(PlayMode::Song).m_timeLine->restoreState( node.toElement() );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1183,7 +1185,7 @@ void Song::loadProject( const QString & fileName )
|
||||
|
||||
// Remove dummy controllers that was added for correct connections
|
||||
m_controllers.erase(std::remove_if(m_controllers.begin(), m_controllers.end(),
|
||||
[](Controller* c){return c->type() == Controller::DummyController;}),
|
||||
[](Controller* c){return c->type() == Controller::ControllerType::Dummy;}),
|
||||
m_controllers.end());
|
||||
|
||||
// resolve all IDs so that autoModels are automated
|
||||
@@ -1233,7 +1235,7 @@ bool Song::saveProjectFile(const QString & filename, bool withResources)
|
||||
{
|
||||
using gui::getGUI;
|
||||
|
||||
DataFile dataFile( DataFile::SongProject );
|
||||
DataFile dataFile( DataFile::Type::SongProject );
|
||||
m_savingProject = true;
|
||||
|
||||
m_tempoModel.saveSettings( dataFile, dataFile.head(), "bpm" );
|
||||
@@ -1251,7 +1253,7 @@ bool Song::saveProjectFile(const QString & filename, bool withResources)
|
||||
getGUI()->pianoRoll()->saveState( dataFile, dataFile.content() );
|
||||
getGUI()->automationEditor()->m_editor->saveState( dataFile, dataFile.content() );
|
||||
getGUI()->getProjectNotes()->SerializingObject::saveState( dataFile, dataFile.content() );
|
||||
m_playPos[Mode_PlaySong].m_timeLine->saveState( dataFile, dataFile.content() );
|
||||
getPlayPos(PlayMode::Song).m_timeLine->saveState( dataFile, dataFile.content() );
|
||||
}
|
||||
|
||||
saveControllerStates( dataFile, dataFile.content() );
|
||||
@@ -1278,7 +1280,7 @@ bool Song::guiSaveProject()
|
||||
// Save the current song with the given filename
|
||||
bool Song::guiSaveProjectAs(const QString & filename)
|
||||
{
|
||||
DataFile dataFile(DataFile::SongProject);
|
||||
DataFile dataFile(DataFile::Type::SongProject);
|
||||
QString fileNameWithExtension = dataFile.nameWithExtension(filename);
|
||||
|
||||
bool withResources = m_saveOptions.saveAsProjectBundle.value();
|
||||
@@ -1326,8 +1328,7 @@ void Song::restoreControllerStates( const QDomElement & element )
|
||||
else
|
||||
{
|
||||
// Fix indices to ensure correct connections
|
||||
m_controllers.append(Controller::create(
|
||||
Controller::DummyController, this));
|
||||
m_controllers.push_back(Controller::create(Controller::ControllerType::Dummy, this));
|
||||
}
|
||||
|
||||
node = node.nextSibling();
|
||||
@@ -1445,9 +1446,10 @@ void Song::setProjectFileName(QString const & projectFileName)
|
||||
|
||||
void Song::addController( Controller * controller )
|
||||
{
|
||||
if( controller && !m_controllers.contains( controller ) )
|
||||
bool containsController = std::find(m_controllers.begin(), m_controllers.end(), controller) != m_controllers.end();
|
||||
if (controller && !containsController)
|
||||
{
|
||||
m_controllers.append( controller );
|
||||
m_controllers.push_back(controller);
|
||||
emit controllerAdded( controller );
|
||||
|
||||
this->setModified();
|
||||
@@ -1459,10 +1461,10 @@ void Song::addController( Controller * controller )
|
||||
|
||||
void Song::removeController( Controller * controller )
|
||||
{
|
||||
int index = m_controllers.indexOf( controller );
|
||||
if( index != -1 )
|
||||
auto it = std::find(m_controllers.begin(), m_controllers.end(), controller);
|
||||
if (it != m_controllers.end())
|
||||
{
|
||||
m_controllers.remove( index );
|
||||
m_controllers.erase(it);
|
||||
|
||||
emit controllerRemoved( controller );
|
||||
delete controller;
|
||||
|
||||
@@ -91,7 +91,7 @@ void StepRecorder::notePressed(const Note & n)
|
||||
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_curStepNotes.push_back(new StepNote(Note(m_curStepLength, m_curStepStartPos, n.key(), n.getVolume(), n.getPanning())));
|
||||
m_pianoRoll.update();
|
||||
}
|
||||
else if (stepNote->isReleased())
|
||||
@@ -175,15 +175,15 @@ void StepRecorder::setStepsLength(const TimePos& newLength)
|
||||
updateWidget();
|
||||
}
|
||||
|
||||
QVector<Note*> StepRecorder::getCurStepNotes()
|
||||
std::vector<Note*> StepRecorder::getCurStepNotes()
|
||||
{
|
||||
QVector<Note*> notes;
|
||||
std::vector<Note*> notes;
|
||||
|
||||
if(m_isStepInProgress)
|
||||
{
|
||||
for(StepNote* stepNote: m_curStepNotes)
|
||||
for (StepNote* stepNote: m_curStepNotes)
|
||||
{
|
||||
notes.append(&stepNote->m_note);
|
||||
notes.push_back(&stepNote->m_note);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,18 +288,13 @@ void StepRecorder::removeNotesReleasedForTooLong()
|
||||
int nextTimout = std::numeric_limits<int>::max();
|
||||
bool notesRemoved = false;
|
||||
|
||||
QMutableVectorIterator<StepNote*> itr(m_curStepNotes);
|
||||
while (itr.hasNext())
|
||||
for (const auto& stepNote : m_curStepNotes)
|
||||
{
|
||||
StepNote* stepNote = itr.next();
|
||||
|
||||
if(stepNote->isReleased())
|
||||
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
|
||||
@@ -309,6 +304,17 @@ void StepRecorder::removeNotesReleasedForTooLong()
|
||||
}
|
||||
}
|
||||
|
||||
m_curStepNotes.erase(std::remove_if(m_curStepNotes.begin(), m_curStepNotes.end(), [](auto stepNote)
|
||||
{
|
||||
bool shouldRemove = stepNote->isReleased() && stepNote->timeSinceReleased() >= REMOVE_RELEASED_NOTE_TIME_THRESHOLD_MS;
|
||||
if (shouldRemove)
|
||||
{
|
||||
delete stepNote;
|
||||
}
|
||||
|
||||
return shouldRemove;
|
||||
}), m_curStepNotes.end());
|
||||
|
||||
if(notesRemoved)
|
||||
{
|
||||
m_pianoRoll.update();
|
||||
|
||||
@@ -40,8 +40,8 @@ TempoSyncKnobModel::TempoSyncKnobModel( const float _val, const float _min,
|
||||
const float _scale, Model * _parent,
|
||||
const QString & _display_name ) :
|
||||
FloatModel( _val, _min, _max, _step, _parent, _display_name ),
|
||||
m_tempoSyncMode( SyncNone ),
|
||||
m_tempoLastSyncMode( SyncNone ),
|
||||
m_tempoSyncMode( SyncMode::None ),
|
||||
m_tempoLastSyncMode( SyncMode::None ),
|
||||
m_scale( _scale ),
|
||||
m_custom( _parent )
|
||||
{
|
||||
@@ -55,15 +55,15 @@ TempoSyncKnobModel::TempoSyncKnobModel( const float _val, const float _min,
|
||||
|
||||
void TempoSyncKnobModel::setTempoSync( QAction * _item )
|
||||
{
|
||||
setTempoSync( _item->data().toInt() );
|
||||
setTempoSync( static_cast<SyncMode>(_item->data().toInt()) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TempoSyncKnobModel::setTempoSync( int _note_type )
|
||||
void TempoSyncKnobModel::setTempoSync( SyncMode _note_type )
|
||||
{
|
||||
setSyncMode( ( TempoSyncMode ) _note_type );
|
||||
setSyncMode( _note_type );
|
||||
Engine::getSong()->setModified();
|
||||
}
|
||||
|
||||
@@ -74,34 +74,34 @@ void TempoSyncKnobModel::calculateTempoSyncTime( bpm_t _bpm )
|
||||
{
|
||||
float conversionFactor = 1.0;
|
||||
|
||||
if( m_tempoSyncMode )
|
||||
if( m_tempoSyncMode != SyncMode::None )
|
||||
{
|
||||
switch( m_tempoSyncMode )
|
||||
{
|
||||
case SyncCustom:
|
||||
case SyncMode::Custom:
|
||||
conversionFactor =
|
||||
static_cast<float>( m_custom.getDenominator() ) /
|
||||
static_cast<float>( m_custom.getNumerator() );
|
||||
break;
|
||||
case SyncDoubleWholeNote:
|
||||
case SyncMode::DoubleWholeNote:
|
||||
conversionFactor = 0.125;
|
||||
break;
|
||||
case SyncWholeNote:
|
||||
case SyncMode::WholeNote:
|
||||
conversionFactor = 0.25;
|
||||
break;
|
||||
case SyncHalfNote:
|
||||
case SyncMode::HalfNote:
|
||||
conversionFactor = 0.5;
|
||||
break;
|
||||
case SyncQuarterNote:
|
||||
case SyncMode::QuarterNote:
|
||||
conversionFactor = 1.0;
|
||||
break;
|
||||
case SyncEighthNote:
|
||||
case SyncMode::EighthNote:
|
||||
conversionFactor = 2.0;
|
||||
break;
|
||||
case SyncSixteenthNote:
|
||||
case SyncMode::SixteenthNote:
|
||||
conversionFactor = 4.0;
|
||||
break;
|
||||
case SyncThirtysecondNote:
|
||||
case SyncMode::ThirtysecondNote:
|
||||
conversionFactor = 8.0;
|
||||
break;
|
||||
default: ;
|
||||
@@ -117,6 +117,10 @@ void TempoSyncKnobModel::calculateTempoSyncTime( bpm_t _bpm )
|
||||
emit syncModeChanged( m_tempoSyncMode );
|
||||
m_tempoLastSyncMode = m_tempoSyncMode;
|
||||
}
|
||||
else if (m_tempoSyncMode == SyncMode::Custom)
|
||||
{
|
||||
emit syncModeChanged(m_tempoSyncMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,18 +142,18 @@ void TempoSyncKnobModel::loadSettings( const QDomElement & _this,
|
||||
{
|
||||
FloatModel::loadSettings( _this, _name );
|
||||
m_custom.loadSettings( _this, _name );
|
||||
setSyncMode( ( TempoSyncMode ) _this.attribute( _name + "_syncmode" ).toInt() );
|
||||
setSyncMode( ( SyncMode ) _this.attribute( _name + "_syncmode" ).toInt() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void TempoSyncKnobModel::setSyncMode( TempoSyncMode _new_mode )
|
||||
void TempoSyncKnobModel::setSyncMode( SyncMode _new_mode )
|
||||
{
|
||||
if( m_tempoSyncMode != _new_mode )
|
||||
{
|
||||
m_tempoSyncMode = _new_mode;
|
||||
if( _new_mode == SyncCustom )
|
||||
if( _new_mode == SyncMode::Custom )
|
||||
{
|
||||
connect( &m_custom, SIGNAL(dataChanged()),
|
||||
this, SLOT(updateCustom()),
|
||||
@@ -174,7 +178,7 @@ void TempoSyncKnobModel::setScale( float _new_scale )
|
||||
|
||||
void TempoSyncKnobModel::updateCustom()
|
||||
{
|
||||
setSyncMode( SyncCustom );
|
||||
setSyncMode( SyncMode::Custom );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ tick_t TimePos::ticksPerBar( const TimeSig &sig )
|
||||
int TimePos::stepsPerBar()
|
||||
{
|
||||
int steps = ticksPerBar() / DefaultBeatsPerBar;
|
||||
return qMax( 1, steps );
|
||||
return std::max(1, steps);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ ToolPlugin * ToolPlugin::instantiate( const QString & _plugin_name, Model * _par
|
||||
{
|
||||
Plugin * p = Plugin::instantiate( _plugin_name, _parent, nullptr );
|
||||
// check whether instantiated plugin is a tool
|
||||
if( p->type() == Plugin::Tool )
|
||||
if( p->type() == Plugin::Type::Tool )
|
||||
{
|
||||
// everything ok, so return pointer
|
||||
return dynamic_cast<ToolPlugin *>( p );
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace lmms
|
||||
*
|
||||
* \todo check the definitions of all the properties - are they OK?
|
||||
*/
|
||||
Track::Track( TrackTypes type, TrackContainer * tc ) :
|
||||
Track::Track( Type type, TrackContainer * tc ) :
|
||||
Model( tc ), /*!< The track Model */
|
||||
m_trackContainer( tc ), /*!< The track container object */
|
||||
m_type( type ), /*!< The track type */
|
||||
@@ -84,9 +84,9 @@ Track::~Track()
|
||||
lock();
|
||||
emit destroyedTrack();
|
||||
|
||||
while( !m_clips.isEmpty() )
|
||||
while (!m_clips.empty())
|
||||
{
|
||||
delete m_clips.last();
|
||||
delete m_clips.back();
|
||||
}
|
||||
|
||||
m_trackContainer->removeTrack( this );
|
||||
@@ -101,7 +101,7 @@ Track::~Track()
|
||||
* \param tt The type of track to create
|
||||
* \param tc The track container to attach to
|
||||
*/
|
||||
Track * Track::create( TrackTypes tt, TrackContainer * tc )
|
||||
Track * Track::create( Type tt, TrackContainer * tc )
|
||||
{
|
||||
Engine::audioEngine()->requestChangeInModel();
|
||||
|
||||
@@ -109,13 +109,13 @@ Track * Track::create( TrackTypes tt, TrackContainer * tc )
|
||||
|
||||
switch( tt )
|
||||
{
|
||||
case InstrumentTrack: t = new class InstrumentTrack( tc ); break;
|
||||
case PatternTrack: t = new class PatternTrack( tc ); break;
|
||||
case SampleTrack: t = new class SampleTrack( tc ); break;
|
||||
// case EVENT_TRACK:
|
||||
// case VIDEO_TRACK:
|
||||
case AutomationTrack: t = new class AutomationTrack( tc ); break;
|
||||
case HiddenAutomationTrack:
|
||||
case Type::Instrument: t = new class InstrumentTrack( tc ); break;
|
||||
case Type::Pattern: t = new class PatternTrack( tc ); break;
|
||||
case Type::Sample: t = new class SampleTrack( tc ); break;
|
||||
// case Type::Event:
|
||||
// case Type::Video:
|
||||
case Type::Automation: t = new class AutomationTrack( tc ); break;
|
||||
case Type::HiddenAutomation:
|
||||
t = new class AutomationTrack( tc, true ); break;
|
||||
default: break;
|
||||
}
|
||||
@@ -145,7 +145,7 @@ Track * Track::create( const QDomElement & element, TrackContainer * tc )
|
||||
Engine::audioEngine()->requestChangeInModel();
|
||||
|
||||
Track * t = create(
|
||||
static_cast<TrackTypes>( element.attribute( "type" ).toInt() ),
|
||||
static_cast<Type>( element.attribute( "type" ).toInt() ),
|
||||
tc );
|
||||
if( t != nullptr )
|
||||
{
|
||||
@@ -197,7 +197,7 @@ void Track::saveSettings( QDomDocument & doc, QDomElement & element )
|
||||
{
|
||||
element.setTagName( "track" );
|
||||
}
|
||||
element.setAttribute( "type", type() );
|
||||
element.setAttribute( "type", static_cast<int>(type()) );
|
||||
element.setAttribute( "name", name() );
|
||||
m_mutedModel.saveSettings( doc, element, "muted" );
|
||||
m_soloModel.saveSettings( doc, element, "solo" );
|
||||
@@ -249,7 +249,7 @@ void Track::saveSettings( QDomDocument & doc, QDomElement & element )
|
||||
*/
|
||||
void Track::loadSettings( const QDomElement & element )
|
||||
{
|
||||
if( element.attribute( "type" ).toInt() != type() )
|
||||
if( static_cast<Type>(element.attribute( "type" ).toInt()) != type() )
|
||||
{
|
||||
qWarning( "Current track-type does not match track-type of "
|
||||
"settings-node!\n" );
|
||||
@@ -365,9 +365,9 @@ void Track::removeClip( Clip * clip )
|
||||
/*! \brief Remove all Clips from this track */
|
||||
void Track::deleteClips()
|
||||
{
|
||||
while( ! m_clips.isEmpty() )
|
||||
while (!m_clips.empty())
|
||||
{
|
||||
delete m_clips.first();
|
||||
delete m_clips.front();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,7 +613,7 @@ void Track::toggleSolo()
|
||||
{
|
||||
track->setMuted(false);
|
||||
}
|
||||
else if (soloLegacyBehavior || track->type() != AutomationTrack)
|
||||
else if (soloLegacyBehavior || track->type() != Type::Automation)
|
||||
{
|
||||
track->setMuted(true);
|
||||
}
|
||||
@@ -626,7 +626,7 @@ void Track::toggleSolo()
|
||||
{
|
||||
// Unless we are on the sololegacybehavior mode, only restores the
|
||||
// mute state if the track isn't an Automation Track
|
||||
if (soloLegacyBehavior || track->type() != AutomationTrack)
|
||||
if (soloLegacyBehavior || track->type() != Type::Automation)
|
||||
{
|
||||
track->setMuted(track->m_mutedBeforeSolo);
|
||||
}
|
||||
|
||||
@@ -156,13 +156,13 @@ void TrackContainer::loadSettings( const QDomElement & _this )
|
||||
|
||||
|
||||
|
||||
int TrackContainer::countTracks( Track::TrackTypes _tt ) const
|
||||
int TrackContainer::countTracks( Track::Type _tt ) const
|
||||
{
|
||||
int cnt = 0;
|
||||
m_tracksMutex.lockForRead();
|
||||
for (const auto& track : m_tracks)
|
||||
{
|
||||
if (track->type() == _tt || _tt == Track::NumTrackTypes)
|
||||
if (track->type() == _tt || _tt == Track::Type::Count)
|
||||
{
|
||||
++cnt;
|
||||
}
|
||||
@@ -176,7 +176,7 @@ int TrackContainer::countTracks( Track::TrackTypes _tt ) const
|
||||
|
||||
void TrackContainer::addTrack( Track * _track )
|
||||
{
|
||||
if( _track->type() != Track::HiddenAutomationTrack )
|
||||
if( _track->type() != Track::Type::HiddenAutomation )
|
||||
{
|
||||
_track->lock();
|
||||
m_tracksMutex.lockForWrite();
|
||||
@@ -196,14 +196,14 @@ void TrackContainer::removeTrack( Track * _track )
|
||||
// After checking that index != -1, we need to upgrade the lock to a write locker before changing m_tracks.
|
||||
// But since Qt offers no function to promote a read lock to a write lock, we must start with the write locker.
|
||||
QWriteLocker lockTracksAccess(&m_tracksMutex);
|
||||
int index = m_tracks.indexOf( _track );
|
||||
if( index != -1 )
|
||||
auto it = std::find(m_tracks.begin(), m_tracks.end(), _track);
|
||||
if (it != m_tracks.end())
|
||||
{
|
||||
// If the track is solo, all other tracks are muted. Change this before removing the solo track:
|
||||
if (_track->isSolo()) {
|
||||
_track->setSolo(false);
|
||||
}
|
||||
m_tracks.remove( index );
|
||||
m_tracks.erase(it);
|
||||
lockTracksAccess.unlock();
|
||||
|
||||
if( Engine::getSong() )
|
||||
@@ -226,9 +226,9 @@ void TrackContainer::updateAfterTrackAdd()
|
||||
void TrackContainer::clearAllTracks()
|
||||
{
|
||||
//m_tracksMutex.lockForWrite();
|
||||
while( !m_tracks.isEmpty() )
|
||||
while (!m_tracks.empty())
|
||||
{
|
||||
delete m_tracks.first();
|
||||
delete m_tracks.front();
|
||||
}
|
||||
//m_tracksMutex.unlock();
|
||||
}
|
||||
@@ -240,7 +240,7 @@ bool TrackContainer::isEmpty() const
|
||||
{
|
||||
for (const auto& track : m_tracks)
|
||||
{
|
||||
if (!track->getClips().isEmpty())
|
||||
if (!track->getClips().empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -268,14 +268,14 @@ AutomatedValueMap TrackContainer::automatedValuesFromTracks(const TrackList &tra
|
||||
|
||||
switch(track->type())
|
||||
{
|
||||
case Track::AutomationTrack:
|
||||
case Track::HiddenAutomationTrack:
|
||||
case Track::PatternTrack:
|
||||
case Track::Type::Automation:
|
||||
case Track::Type::HiddenAutomation:
|
||||
case Track::Type::Pattern:
|
||||
if (clipNum < 0) {
|
||||
track->getClipsInRange(clips, 0, time);
|
||||
} else {
|
||||
Q_ASSERT(track->numOfClips() > clipNum);
|
||||
clips << track->getClip(clipNum);
|
||||
clips.push_back(track->getClip(clipNum));
|
||||
}
|
||||
default:
|
||||
break;
|
||||
@@ -299,7 +299,7 @@ AutomatedValueMap TrackContainer::automatedValuesFromTracks(const TrackList &tra
|
||||
}
|
||||
TimePos relTime = time - p->startPosition();
|
||||
if (! p->getAutoResize()) {
|
||||
relTime = qMin(relTime, p->length());
|
||||
relTime = std::min(relTime, p->length());
|
||||
}
|
||||
float value = p->valueAt(relTime);
|
||||
|
||||
|
||||
434
src/core/UpgradeExtendedNoteRange.cpp
Normal file
434
src/core/UpgradeExtendedNoteRange.cpp
Normal file
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
* UpgradeExtendedNoteRange.cpp - Upgrades the extended note range
|
||||
*
|
||||
* 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 "UpgradeExtendedNoteRange.h"
|
||||
|
||||
#include "Track.h"
|
||||
|
||||
#include <QDomElement>
|
||||
|
||||
#include <set>
|
||||
#include <cassert>
|
||||
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Used by the helper function that analyzes automation patterns.
|
||||
*/
|
||||
struct PatternAnalysisResult
|
||||
{
|
||||
PatternAnalysisResult(bool hasBaseNoteAutomations, bool hasNonBaseNoteAutomations)
|
||||
{
|
||||
this->hasBaseNoteAutomations = hasBaseNoteAutomations;
|
||||
this->hasNonBaseNoteAutomations = hasNonBaseNoteAutomations;
|
||||
}
|
||||
bool hasBaseNoteAutomations;
|
||||
bool hasNonBaseNoteAutomations;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper function that checks for an automation pattern if it contains automation for
|
||||
* targets that are base notes and/or other targets.
|
||||
* @param automationPattern The automation pattern to be checked.
|
||||
* @param automatedBaseNoteIds A set of id of automated base notes that are used in the check.
|
||||
* @return A struct that contains the results.
|
||||
*/
|
||||
static PatternAnalysisResult analyzeAutomationPattern(QDomElement const & automationPattern, std::set<unsigned int> const & automatedBaseNoteIds)
|
||||
{
|
||||
bool hasBaseNoteAutomations = false;
|
||||
bool hasNonBaseNoteAutomations = false;
|
||||
|
||||
// Iterate the objects. These contain the ids of the automated objects.
|
||||
QDomElement object = automationPattern.firstChildElement("object");
|
||||
while (!object.isNull())
|
||||
{
|
||||
unsigned int const id = object.attribute("id").toUInt();
|
||||
|
||||
// Check if the automated object is a base note.
|
||||
if (automatedBaseNoteIds.find(id) != automatedBaseNoteIds.end())
|
||||
{
|
||||
hasBaseNoteAutomations = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasNonBaseNoteAutomations = true;
|
||||
}
|
||||
|
||||
object = object.nextSiblingElement("object");
|
||||
}
|
||||
|
||||
return PatternAnalysisResult(hasBaseNoteAutomations, hasNonBaseNoteAutomations);
|
||||
}
|
||||
|
||||
static void fixNotePatterns(QDomNodeList & patterns)
|
||||
{
|
||||
for (int i = 0; i < patterns.size(); ++i)
|
||||
{
|
||||
QDomNodeList notes = patterns.item(i).toElement().elementsByTagName("note");
|
||||
for (int j = 0; j < notes.size(); ++j)
|
||||
{
|
||||
QDomElement note = notes.item(j).toElement();
|
||||
if (note.hasAttribute("key"))
|
||||
{
|
||||
int const currentKey = note.attribute("key").toInt();
|
||||
note.setAttribute("key", currentKey + 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fixInstrumentBaseNoteAndCollectIds(QDomElement & instrument, std::set<unsigned int> & automatedBaseNoteIds)
|
||||
{
|
||||
// Raise the base note of every instrument by 12 to compensate for the change
|
||||
// of A4 key code from 57 to 69. This ensures that notes are labeled correctly.
|
||||
QDomElement instrumentParent = instrument.parentNode().toElement();
|
||||
|
||||
// Correct the base note of the instrument. Base notes which are automated are
|
||||
// stored as elements. Non-automated base notes are stored as attributes.
|
||||
if (instrumentParent.hasAttribute("basenote"))
|
||||
{
|
||||
// TODO Base notes can have float values in the save file! This might need to be changed!
|
||||
int const currentBaseNote = instrumentParent.attribute("basenote").toInt();
|
||||
instrumentParent.setAttribute("basenote", currentBaseNote + 12);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if the instrument track has an automated base note.
|
||||
// Correct the value of the base note and collect their ids while doing so.
|
||||
// The ids are used later to find the automations that automate these corrected base
|
||||
// notes so that we can correct the automation values as well.
|
||||
QDomNodeList baseNotes = instrumentParent.elementsByTagName("basenote");
|
||||
for (int j = 0; j < baseNotes.size(); ++j)
|
||||
{
|
||||
QDomElement baseNote = baseNotes.item(j).toElement();
|
||||
if (!baseNote.isNull())
|
||||
{
|
||||
if (baseNote.hasAttribute("value"))
|
||||
{
|
||||
// Base notes can have float values in the save file, e.g. if the file
|
||||
// is saved after a linear automation has run on the base note. Therefore
|
||||
// it is fixed here as a float even if the nominal values of base notes
|
||||
// are integers.
|
||||
float const value = baseNote.attribute("value").toFloat();
|
||||
baseNote.setAttribute("value", value + 12);
|
||||
}
|
||||
|
||||
// The ids of base notes are of type jo_id_t which are in fact uint32_t.
|
||||
// So let's just use these here to save some casting.
|
||||
unsigned int const id = baseNote.attribute("id").toUInt();
|
||||
automatedBaseNoteIds.insert(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper method that fixes the values and out values for an automation pattern.
|
||||
* @param automationPattern The automation pattern to be fixed.
|
||||
*/
|
||||
static void fixAutomationPattern(QDomElement & automationPattern)
|
||||
{
|
||||
QDomElement time = automationPattern.firstChildElement("time");
|
||||
while (!time.isNull())
|
||||
{
|
||||
// Automation patterns can automate base notes as floats
|
||||
// so we read and correct them as floats here.
|
||||
float const value = time.attribute("value").toFloat();
|
||||
time.setAttribute("value", value + 12.);
|
||||
|
||||
// The method "upgrade_automationNodes" adds some attributes
|
||||
// with the name "outValue". We have to correct these as well.
|
||||
float const outValue = time.attribute("outValue").toFloat();
|
||||
time.setAttribute("outValue", outValue + 12.);
|
||||
|
||||
time = time.nextSiblingElement("time");
|
||||
};
|
||||
}
|
||||
|
||||
static bool affected(QDomElement & instrument)
|
||||
{
|
||||
assert(instrument.hasAttribute("name"));
|
||||
QString const name = instrument.attribute("name");
|
||||
|
||||
return name == "zynaddsubfx" ||
|
||||
name == "vestige" || name == "lv2instrument" ||
|
||||
name == "carlapatchbay" || name == "carlarack";
|
||||
}
|
||||
|
||||
static void fixTrack(QDomElement & track, std::set<unsigned int> & automatedBaseNoteIds)
|
||||
{
|
||||
if (!track.hasAttribute("type"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Track::Type const trackType = static_cast<Track::Type>(track.attribute("type").toInt());
|
||||
|
||||
// BB tracks need special handling because they contain a track container of their own
|
||||
if (trackType == Track::Type::Pattern)
|
||||
{
|
||||
// Assuming that a BB track cannot contain another BB track here...
|
||||
QDomNodeList subTracks = track.elementsByTagName("track");
|
||||
for (int i = 0; i < subTracks.size(); ++i)
|
||||
{
|
||||
QDomElement subTrack = subTracks.item(i).toElement();
|
||||
assert (static_cast<Track::Type>(subTrack.attribute("type").toInt()) != Track::Type::Pattern);
|
||||
fixTrack(subTrack, automatedBaseNoteIds);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QDomNodeList instruments = track.elementsByTagName("instrument");
|
||||
|
||||
for (int i = 0; i < instruments.size(); ++i)
|
||||
{
|
||||
QDomElement instrument = instruments.item(i).toElement();
|
||||
|
||||
fixInstrumentBaseNoteAndCollectIds(instrument, automatedBaseNoteIds);
|
||||
|
||||
// Raise the pitch of all notes in patterns assigned to instruments not affected
|
||||
// by #1857 by an octave. This negates the base note change for normal instruments,
|
||||
// but leaves the MIDI-based instruments sounding an octave lower, preserving their
|
||||
// pitch in existing projects.
|
||||
if (!affected(instrument))
|
||||
{
|
||||
QDomNodeList patterns = track.elementsByTagName("pattern");
|
||||
fixNotePatterns(patterns);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fixAutomationTracks(QDomElement & song, std::set<unsigned int> const & automatedBaseNoteIds)
|
||||
{
|
||||
// Now fix all the automation tracks.
|
||||
QDomNodeList tracks = song.elementsByTagName("track");
|
||||
|
||||
// We have to collect the tracks that we need to duplicate and cannot do this in-place
|
||||
// because if we did the iteration might never stop.
|
||||
std::vector<QDomElement> tracksToDuplicate;
|
||||
tracksToDuplicate.reserve(tracks.size());
|
||||
|
||||
// Iterate the tracks again. This time work on all automation tracks.
|
||||
for (int i = 0; i < tracks.size(); ++i)
|
||||
{
|
||||
QDomElement currentTrack = tracks.item(i).toElement();
|
||||
if (static_cast<Track::Type>(currentTrack.attribute("type").toInt()) != Track::Type::Automation)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check each track for the types of automations it contains in its patterns.
|
||||
bool containsPatternsWithBaseNoteTargets = false;
|
||||
bool containsPatternsWithNonBaseNoteTargets = false;
|
||||
|
||||
QDomElement automationPattern = currentTrack.firstChildElement("automationpattern");
|
||||
while (!automationPattern.isNull())
|
||||
{
|
||||
auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds);
|
||||
containsPatternsWithBaseNoteTargets |= analysis.hasBaseNoteAutomations;
|
||||
containsPatternsWithNonBaseNoteTargets |= analysis.hasNonBaseNoteAutomations;
|
||||
|
||||
automationPattern = automationPattern.nextSiblingElement("automationpattern");
|
||||
}
|
||||
|
||||
if (!containsPatternsWithBaseNoteTargets)
|
||||
{
|
||||
// No base notes are automated by this automation track so we have nothing to do
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!containsPatternsWithNonBaseNoteTargets)
|
||||
{
|
||||
// Only base note targets. This means we can simply keep the track and fix it.
|
||||
automationPattern = currentTrack.firstChildElement("automationpattern");
|
||||
while (!automationPattern.isNull())
|
||||
{
|
||||
fixAutomationPattern(automationPattern);
|
||||
|
||||
automationPattern = automationPattern.nextSiblingElement("automationpattern");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The automation track has automations for base notes and other targets in its patterns.
|
||||
// We will later need to duplicate/split the track.
|
||||
tracksToDuplicate.push_back(currentTrack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now fix the tracks that need duplication/splitting
|
||||
for (QDomElement & track : tracksToDuplicate)
|
||||
{
|
||||
// First clone the original track
|
||||
QDomNode cloneOfTrack = track.cloneNode();
|
||||
|
||||
// Now that we have the original and the clone we can manipulate both of them.
|
||||
// The original track will keep only patterns without base note automations.
|
||||
// Note: for the original track these might also be automation patterns without
|
||||
// any targets. We will keep these because they might have been saved by the users
|
||||
// like this.
|
||||
QDomElement automationPattern = track.firstChildElement("automationpattern");
|
||||
while (!automationPattern.isNull())
|
||||
{
|
||||
auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds);
|
||||
if (!analysis.hasBaseNoteAutomations)
|
||||
{
|
||||
// This pattern has no base note automations. Leave it alone.
|
||||
automationPattern = automationPattern.nextSiblingElement("automationpattern");
|
||||
}
|
||||
else if (!analysis.hasNonBaseNoteAutomations)
|
||||
{
|
||||
// The pattern only has base note automations. Remove it completely as it would become empty.
|
||||
QDomElement patternToRemove = automationPattern;
|
||||
automationPattern = automationPattern.nextSiblingElement("automationpattern");
|
||||
track.removeChild(patternToRemove);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The pattern itself is mixed. Remove the base note objects.
|
||||
QDomElement object = automationPattern.firstChildElement("object");
|
||||
while (!object.isNull())
|
||||
{
|
||||
unsigned int const id = object.attribute("id").toUInt();
|
||||
|
||||
if (automatedBaseNoteIds.find(id) != automatedBaseNoteIds.end())
|
||||
{
|
||||
QDomElement objectToRemove = object;
|
||||
object = object.nextSiblingElement("object");
|
||||
automationPattern.removeChild(objectToRemove);
|
||||
}
|
||||
else
|
||||
{
|
||||
object = object.nextSiblingElement("object");
|
||||
}
|
||||
}
|
||||
|
||||
automationPattern = automationPattern.nextSiblingElement("automationpattern");
|
||||
}
|
||||
}
|
||||
|
||||
// The clone will only keep non-empty patterns with base note automations
|
||||
// and the values of the patterns will be corrected.
|
||||
automationPattern = cloneOfTrack.firstChildElement("automationpattern");
|
||||
while (!automationPattern.isNull())
|
||||
{
|
||||
auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds);
|
||||
if (analysis.hasBaseNoteAutomations)
|
||||
{
|
||||
// This pattern has base note automations. Remove all other ones and fix the pattern.
|
||||
QDomElement object = automationPattern.firstChildElement("object");
|
||||
while (!object.isNull())
|
||||
{
|
||||
unsigned int const id = object.attribute("id").toUInt();
|
||||
|
||||
if (automatedBaseNoteIds.find(id) == automatedBaseNoteIds.end())
|
||||
{
|
||||
QDomElement objectToRemove = object;
|
||||
object = object.nextSiblingElement("object");
|
||||
automationPattern.removeChild(objectToRemove);
|
||||
}
|
||||
else
|
||||
{
|
||||
object = object.nextSiblingElement("object");
|
||||
}
|
||||
}
|
||||
|
||||
fixAutomationPattern(automationPattern);
|
||||
|
||||
automationPattern = automationPattern.nextSiblingElement("automationpattern");
|
||||
}
|
||||
else
|
||||
{
|
||||
// The pattern has no base note automations. Remove it completely.
|
||||
QDomElement patternToRemove = automationPattern;
|
||||
automationPattern = automationPattern.nextSiblingElement("automationpattern");
|
||||
cloneOfTrack.removeChild(patternToRemove);
|
||||
}
|
||||
}
|
||||
track.parentNode().appendChild(cloneOfTrack);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UpgradeExtendedNoteRange::UpgradeExtendedNoteRange(QDomElement & domElement) :
|
||||
m_domElement(domElement)
|
||||
{
|
||||
}
|
||||
|
||||
void UpgradeExtendedNoteRange::upgrade()
|
||||
{
|
||||
QDomElement song = m_domElement.firstChildElement("song");
|
||||
while (!song.isNull())
|
||||
{
|
||||
// This set will later contain all ids of automated base notes. They are
|
||||
// used to find out which automation patterns must to be corrected, i.e. to
|
||||
// check if an automation pattern has one or more base notes as its target.
|
||||
std::set<unsigned int> automatedBaseNoteIds;
|
||||
|
||||
QDomElement trackContainer = song.firstChildElement("trackcontainer");
|
||||
while (!trackContainer.isNull())
|
||||
{
|
||||
QDomElement track = trackContainer.firstChildElement("track");
|
||||
while (!track.isNull())
|
||||
{
|
||||
fixTrack(track, automatedBaseNoteIds);
|
||||
|
||||
track = track.nextSiblingElement("track");
|
||||
}
|
||||
|
||||
trackContainer = trackContainer.nextSiblingElement("trackcontainer");
|
||||
}
|
||||
|
||||
fixAutomationTracks(song, automatedBaseNoteIds);
|
||||
|
||||
song = song.nextSiblingElement("song");
|
||||
};
|
||||
|
||||
if (m_domElement.elementsByTagName("song").item(0).isNull())
|
||||
{
|
||||
// Dealing with a preset, not a song
|
||||
QDomNodeList presets = m_domElement.elementsByTagName("instrumenttrack");
|
||||
if (presets.item(0).isNull()) { return; }
|
||||
QDomElement preset = presets.item(0).toElement();
|
||||
// Common correction for all instrument presets (make base notes match the new MIDI range).
|
||||
// NOTE: Many older presets do not have any basenote defined, assume they were "made for 57".
|
||||
// (Specifying a default like this also happens to fix a FileBrowser bug where previews of presets
|
||||
// with undefined basenote always play with the basenote inherited from previously played preview.)
|
||||
int oldBase = preset.attribute("basenote", "57").toInt();
|
||||
preset.setAttribute("basenote", oldBase + 12);
|
||||
// Extra correction for Zyn, VeSTige, LV2 and Carla (to preserve the original buggy behavior).
|
||||
QDomNodeList instruments = presets.item(0).toElement().elementsByTagName("instrument");
|
||||
if (instruments.isEmpty()) { return; }
|
||||
QDomElement instrument = instruments.item(0).toElement();
|
||||
if (affected(instrument))
|
||||
{
|
||||
preset.setAttribute("basenote", preset.attribute("basenote").toInt() + 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace lmms
|
||||
47
src/core/UpgradeExtendedNoteRange.h
Normal file
47
src/core/UpgradeExtendedNoteRange.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* UpgradeExtendedNoteRange.h - Upgrades the extended note range
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef LMMS_UPGRADEEXTENDEDNOTERANGE_H
|
||||
#define LMMS_UPGRADEEXTENDEDNOTERANGE_H
|
||||
|
||||
|
||||
class QDomElement;
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
class UpgradeExtendedNoteRange
|
||||
{
|
||||
public:
|
||||
UpgradeExtendedNoteRange(QDomElement & domElement);
|
||||
|
||||
void upgrade();
|
||||
|
||||
private:
|
||||
QDomElement & m_domElement;
|
||||
};
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // LMMS_UPGRADEEXTENDEDNOTERANGE_H
|
||||
@@ -36,10 +36,10 @@ namespace lmms
|
||||
{
|
||||
|
||||
AudioAlsa::AudioAlsa( bool & _success_ful, AudioEngine* _audioEngine ) :
|
||||
AudioDevice( qBound<ch_cnt_t>(
|
||||
AudioDevice(std::clamp<ch_cnt_t>(
|
||||
ConfigManager::inst()->value("audioalsa", "channels").toInt(),
|
||||
DEFAULT_CHANNELS,
|
||||
ConfigManager::inst()->value( "audioalsa", "channels" ).toInt(),
|
||||
SURROUND_CHANNELS ), _audioEngine ),
|
||||
SURROUND_CHANNELS), _audioEngine),
|
||||
m_handle( nullptr ),
|
||||
m_hwParams( nullptr ),
|
||||
m_swParams( nullptr ),
|
||||
@@ -87,7 +87,7 @@ AudioAlsa::AudioAlsa( bool & _success_ful, AudioEngine* _audioEngine ) :
|
||||
int count = snd_pcm_poll_descriptors_count( m_handle );
|
||||
ufds = new pollfd[count];
|
||||
snd_pcm_poll_descriptors( m_handle, ufds, count );
|
||||
for( int i = 0; i < qMax( 3, count ); ++i )
|
||||
for (int i = 0; i < std::max(3, count); ++i)
|
||||
{
|
||||
const int fd = ( i >= count ) ? ufds[0].fd+i : ufds[i].fd;
|
||||
int oldflags = fcntl( fd, F_GETFD, 0 );
|
||||
@@ -328,7 +328,7 @@ void AudioAlsa::run()
|
||||
outbuf,
|
||||
m_convertEndian );
|
||||
}
|
||||
int min_len = qMin( len, outbuf_size - outbuf_pos );
|
||||
int min_len = std::min(len, outbuf_size - outbuf_pos);
|
||||
memcpy( ptr, outbuf + outbuf_pos,
|
||||
min_len * sizeof( int_sample_t ) );
|
||||
ptr += min_len;
|
||||
|
||||
@@ -58,8 +58,8 @@ bool AudioFileFlac::startEncoding()
|
||||
|
||||
switch (getOutputSettings().getBitDepth())
|
||||
{
|
||||
case OutputSettings::Depth_24Bit:
|
||||
case OutputSettings::Depth_32Bit:
|
||||
case OutputSettings::BitDepth::Depth24Bit:
|
||||
case OutputSettings::BitDepth::Depth32Bit:
|
||||
// FLAC does not support 32bit sampling, so take it as 24.
|
||||
m_sfinfo.format |= SF_FORMAT_PCM_24;
|
||||
break;
|
||||
@@ -94,7 +94,7 @@ void AudioFileFlac::writeBuffer(surroundSampleFrame const* _ab, fpp_t const fram
|
||||
OutputSettings::BitDepth depth = getOutputSettings().getBitDepth();
|
||||
float clipvalue = std::nextafterf( -1.0f, 0.0f );
|
||||
|
||||
if (depth == OutputSettings::Depth_24Bit || depth == OutputSettings::Depth_32Bit) // Float encoding
|
||||
if (depth == OutputSettings::BitDepth::Depth24Bit || depth == OutputSettings::BitDepth::Depth32Bit) // Float encoding
|
||||
{
|
||||
auto buf = std::vector<sample_t>(frames * channels());
|
||||
for(fpp_t frame = 0; frame < frames; ++frame)
|
||||
@@ -104,7 +104,7 @@ void AudioFileFlac::writeBuffer(surroundSampleFrame const* _ab, fpp_t const fram
|
||||
// Clip the negative side to just above -1.0 in order to prevent it from changing sign
|
||||
// Upstream issue: https://github.com/erikd/libsndfile/issues/309
|
||||
// When this commit is reverted libsndfile-1.0.29 must be made a requirement for FLAC
|
||||
buf[frame*channels() + channel] = qMax( clipvalue, _ab[frame][channel] * master_gain );
|
||||
buf[frame*channels() + channel] = std::max(clipvalue, _ab[frame][channel] * master_gain);
|
||||
}
|
||||
}
|
||||
sf_writef_float(m_sf, static_cast<float*>(buf.data()), frames);
|
||||
|
||||
@@ -94,11 +94,11 @@ MPEG_mode mapToMPEG_mode(OutputSettings::StereoMode stereoMode)
|
||||
{
|
||||
switch (stereoMode)
|
||||
{
|
||||
case OutputSettings::StereoMode_Stereo:
|
||||
case OutputSettings::StereoMode::Stereo:
|
||||
return STEREO;
|
||||
case OutputSettings::StereoMode_JointStereo:
|
||||
case OutputSettings::StereoMode::JointStereo:
|
||||
return JOINT_STEREO;
|
||||
case OutputSettings::StereoMode_Mono:
|
||||
case OutputSettings::StereoMode::Mono:
|
||||
return MONO;
|
||||
default:
|
||||
return NOT_SET;
|
||||
|
||||
@@ -64,13 +64,13 @@ bool AudioFileWave::startEncoding()
|
||||
|
||||
switch( getOutputSettings().getBitDepth() )
|
||||
{
|
||||
case OutputSettings::Depth_32Bit:
|
||||
case OutputSettings::BitDepth::Depth32Bit:
|
||||
m_si.format |= SF_FORMAT_FLOAT;
|
||||
break;
|
||||
case OutputSettings::Depth_24Bit:
|
||||
case OutputSettings::BitDepth::Depth24Bit:
|
||||
m_si.format |= SF_FORMAT_PCM_24;
|
||||
break;
|
||||
case OutputSettings::Depth_16Bit:
|
||||
case OutputSettings::BitDepth::Depth16Bit:
|
||||
default:
|
||||
m_si.format |= SF_FORMAT_PCM_16;
|
||||
break;
|
||||
@@ -102,7 +102,7 @@ void AudioFileWave::writeBuffer( const surroundSampleFrame * _ab,
|
||||
{
|
||||
OutputSettings::BitDepth bitDepth = getOutputSettings().getBitDepth();
|
||||
|
||||
if( bitDepth == OutputSettings::Depth_32Bit || bitDepth == OutputSettings::Depth_24Bit )
|
||||
if( bitDepth == OutputSettings::BitDepth::Depth32Bit || bitDepth == OutputSettings::BitDepth::Depth24Bit )
|
||||
{
|
||||
auto buf = new float[_frames * channels()];
|
||||
for( fpp_t frame = 0; frame < _frames; ++frame )
|
||||
|
||||
@@ -44,10 +44,10 @@ namespace lmms
|
||||
{
|
||||
|
||||
AudioJack::AudioJack( bool & _success_ful, AudioEngine* _audioEngine ) :
|
||||
AudioDevice( qBound<int>(
|
||||
AudioDevice(std::clamp<int>(
|
||||
ConfigManager::inst()->value("audiojack", "channels").toInt(),
|
||||
DEFAULT_CHANNELS,
|
||||
ConfigManager::inst()->value( "audiojack", "channels" ).toInt(),
|
||||
SURROUND_CHANNELS ), _audioEngine ),
|
||||
SURROUND_CHANNELS), _audioEngine),
|
||||
m_client( nullptr ),
|
||||
m_active( false ),
|
||||
m_midiClient( nullptr ),
|
||||
@@ -364,7 +364,7 @@ int AudioJack::processCallback( jack_nframes_t _nframes, void * _udata )
|
||||
}
|
||||
|
||||
#ifdef AUDIO_PORT_SUPPORT
|
||||
const int frames = qMin<int>( _nframes, audioEngine()->framesPerPeriod() );
|
||||
const int frames = std::min<int>(_nframes, audioEngine()->framesPerPeriod());
|
||||
for( JackPortMap::iterator it = m_portMap.begin();
|
||||
it != m_portMap.end(); ++it )
|
||||
{
|
||||
@@ -389,10 +389,10 @@ int AudioJack::processCallback( jack_nframes_t _nframes, void * _udata )
|
||||
jack_nframes_t done = 0;
|
||||
while( done < _nframes && m_stopped == false )
|
||||
{
|
||||
jack_nframes_t todo = qMin<jack_nframes_t>(
|
||||
jack_nframes_t todo = std::min<jack_nframes_t>(
|
||||
_nframes,
|
||||
m_framesToDoInCurBuf -
|
||||
m_framesDoneInCurBuf );
|
||||
m_framesDoneInCurBuf);
|
||||
const float gain = audioEngine()->masterGain();
|
||||
for( int c = 0; c < channels(); ++c )
|
||||
{
|
||||
|
||||
@@ -70,10 +70,10 @@ static const QString PATH_DEV_DSP =
|
||||
|
||||
|
||||
AudioOss::AudioOss( bool & _success_ful, AudioEngine* _audioEngine ) :
|
||||
AudioDevice( qBound<ch_cnt_t>(
|
||||
AudioDevice(std::clamp<ch_cnt_t>(
|
||||
ConfigManager::inst()->value("audiooss", "channels").toInt(),
|
||||
DEFAULT_CHANNELS,
|
||||
ConfigManager::inst()->value( "audiooss", "channels" ).toInt(),
|
||||
SURROUND_CHANNELS ), _audioEngine ),
|
||||
SURROUND_CHANNELS), _audioEngine),
|
||||
m_convertEndian( false )
|
||||
{
|
||||
_success_ful = false;
|
||||
|
||||
@@ -121,7 +121,7 @@ void AudioPort::doProcessing()
|
||||
if( ph->buffer() )
|
||||
{
|
||||
if( ph->usesBuffer()
|
||||
&& ( ph->type() == PlayHandle::TypeNotePlayHandle
|
||||
&& ( ph->type() == PlayHandle::Type::NotePlayHandle
|
||||
|| !MixHelpers::isSilent( ph->buffer(), fpp ) ) )
|
||||
{
|
||||
m_bufferUsage = true;
|
||||
|
||||
@@ -62,10 +62,10 @@ namespace lmms
|
||||
|
||||
|
||||
AudioPortAudio::AudioPortAudio( bool & _success_ful, AudioEngine * _audioEngine ) :
|
||||
AudioDevice( qBound<ch_cnt_t>(
|
||||
AudioDevice(std::clamp<ch_cnt_t>(
|
||||
ConfigManager::inst()->value("audioportaudio", "channels").toInt(),
|
||||
DEFAULT_CHANNELS,
|
||||
ConfigManager::inst()->value( "audioportaudio", "channels" ).toInt(),
|
||||
SURROUND_CHANNELS ), _audioEngine ),
|
||||
SURROUND_CHANNELS), _audioEngine),
|
||||
m_paStream( nullptr ),
|
||||
m_wasPAInitError( false ),
|
||||
m_outBuf( new surroundSampleFrame[audioEngine()->framesPerPeriod()] ),
|
||||
@@ -295,8 +295,8 @@ int AudioPortAudio::process_callback(
|
||||
}
|
||||
m_outBufSize = frames;
|
||||
}
|
||||
const int min_len = qMin( (int)_framesPerBuffer,
|
||||
m_outBufSize - m_outBufPos );
|
||||
const int min_len = std::min(static_cast<int>(_framesPerBuffer),
|
||||
m_outBufSize - m_outBufPos);
|
||||
|
||||
float master_gain = audioEngine()->masterGain();
|
||||
|
||||
@@ -490,12 +490,12 @@ void AudioPortAudio::setupWidget::show()
|
||||
const QString& device = ConfigManager::inst()->value(
|
||||
"audioportaudio", "device" );
|
||||
|
||||
int i = qMax( 0, m_setupUtil.m_backendModel.findText( backend ) );
|
||||
int i = std::max(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 ) );
|
||||
i = std::max(0, m_setupUtil.m_deviceModel.findText(device));
|
||||
m_setupUtil.m_deviceModel.setValue( i );
|
||||
}
|
||||
|
||||
|
||||
@@ -47,10 +47,10 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata)
|
||||
|
||||
|
||||
AudioPulseAudio::AudioPulseAudio( bool & _success_ful, AudioEngine* _audioEngine ) :
|
||||
AudioDevice( qBound<ch_cnt_t>(
|
||||
AudioDevice(std::clamp<ch_cnt_t>(
|
||||
ConfigManager::inst()->value("audiopa", "channels").toInt(),
|
||||
DEFAULT_CHANNELS,
|
||||
ConfigManager::inst()->value( "audiopa", "channels" ).toInt(),
|
||||
SURROUND_CHANNELS ), _audioEngine ),
|
||||
SURROUND_CHANNELS), _audioEngine),
|
||||
m_s( nullptr ),
|
||||
m_quit( false ),
|
||||
m_convertEndian( false )
|
||||
|
||||
@@ -70,7 +70,7 @@ AudioSdl::AudioSdl( bool & _success_ful, AudioEngine* _audioEngine ) :
|
||||
// to convert the buffers
|
||||
#endif
|
||||
m_audioHandle.channels = channels();
|
||||
m_audioHandle.samples = qMax( 1024, audioEngine()->framesPerPeriod()*2 );
|
||||
m_audioHandle.samples = std::max(1024, audioEngine()->framesPerPeriod() * 2);
|
||||
|
||||
m_audioHandle.callback = sdlAudioCallback;
|
||||
m_audioHandle.userdata = this;
|
||||
@@ -257,9 +257,9 @@ void AudioSdl::sdlAudioCallback( Uint8 * _buf, int _len )
|
||||
m_currentBufferFramesCount = frames;
|
||||
|
||||
}
|
||||
const uint min_frames_count = qMin( _len/sizeof(sampleFrame),
|
||||
const uint min_frames_count = std::min(_len/sizeof(sampleFrame),
|
||||
m_currentBufferFramesCount
|
||||
- m_currentBufferFramePos );
|
||||
- m_currentBufferFramePos);
|
||||
|
||||
const float gain = audioEngine()->masterGain();
|
||||
for (uint f = 0; f < min_frames_count; f++)
|
||||
@@ -296,8 +296,8 @@ void AudioSdl::sdlAudioCallback( Uint8 * _buf, int _len )
|
||||
(int_sample_t *)m_convertedBuf,
|
||||
m_outConvertEndian );
|
||||
}
|
||||
const int min_len = qMin( _len, m_convertedBufSize
|
||||
- m_convertedBufPos );
|
||||
const int min_len = std::min(_len, m_convertedBufSize
|
||||
- m_convertedBufPos);
|
||||
memcpy( _buf, m_convertedBuf + m_convertedBufPos, min_len );
|
||||
_buf += min_len;
|
||||
_len -= min_len;
|
||||
|
||||
@@ -44,10 +44,10 @@ namespace lmms
|
||||
{
|
||||
|
||||
AudioSndio::AudioSndio(bool & _success_ful, AudioEngine * _audioEngine) :
|
||||
AudioDevice( qBound<ch_cnt_t>(
|
||||
AudioDevice(std::clamp<ch_cnt_t>(
|
||||
ConfigManager::inst()->value("audiosndio", "channels").toInt(),
|
||||
DEFAULT_CHANNELS,
|
||||
ConfigManager::inst()->value( "audiosndio", "channels" ).toInt(),
|
||||
SURROUND_CHANNELS ), _audioEngine ),
|
||||
SURROUND_CHANNELS), _audioEngine),
|
||||
m_convertEndian ( false )
|
||||
{
|
||||
_success_ful = false;
|
||||
|
||||
@@ -40,10 +40,10 @@ namespace lmms
|
||||
{
|
||||
|
||||
AudioSoundIo::AudioSoundIo( bool & outSuccessful, AudioEngine * _audioEngine ) :
|
||||
AudioDevice( qBound<ch_cnt_t>(
|
||||
AudioDevice(std::clamp<ch_cnt_t>(
|
||||
ConfigManager::inst()->value("audiosoundio", "channels").toInt(),
|
||||
DEFAULT_CHANNELS,
|
||||
ConfigManager::inst()->value( "audiosoundio", "channels" ).toInt(),
|
||||
SURROUND_CHANNELS ), _audioEngine )
|
||||
SURROUND_CHANNELS), _audioEngine)
|
||||
{
|
||||
outSuccessful = false;
|
||||
m_soundio = nullptr;
|
||||
|
||||
@@ -102,7 +102,7 @@ int notEmpty(const std::vector<float> &spectrum)
|
||||
*
|
||||
* return -1 on error
|
||||
*/
|
||||
int precomputeWindow(float *window, unsigned int length, FFT_WINDOWS type, bool normalized)
|
||||
int precomputeWindow(float *window, unsigned int length, FFTWindow type, bool normalized)
|
||||
{
|
||||
if (window == nullptr) {return -1;}
|
||||
|
||||
@@ -117,23 +117,23 @@ int precomputeWindow(float *window, unsigned int length, FFT_WINDOWS type, bool
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
case RECTANGULAR:
|
||||
case FFTWindow::Rectangular:
|
||||
for (unsigned int i = 0; i < length; i++) {window[i] = 1.0;}
|
||||
gain = 1;
|
||||
return 0;
|
||||
case BLACKMAN_HARRIS:
|
||||
case FFTWindow::BlackmanHarris:
|
||||
a0 = 0.35875;
|
||||
a1 = 0.48829;
|
||||
a2 = 0.14128;
|
||||
a3 = 0.01168;
|
||||
break;
|
||||
case HAMMING:
|
||||
case FFTWindow::Hamming:
|
||||
a0 = 0.54;
|
||||
a1 = 1.0 - a0;
|
||||
a2 = 0;
|
||||
a3 = 0;
|
||||
break;
|
||||
case HANNING:
|
||||
case FFTWindow::Hanning:
|
||||
a0 = 0.5;
|
||||
a1 = 1.0 - a0;
|
||||
a2 = 0;
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace lmms
|
||||
{
|
||||
|
||||
|
||||
Plugin::PluginTypes Lv2ControlBase::check(const LilvPlugin *plugin,
|
||||
Plugin::Type Lv2ControlBase::check(const LilvPlugin *plugin,
|
||||
std::vector<PluginIssue> &issues)
|
||||
{
|
||||
// for some reason, all checks can be done by one processor...
|
||||
|
||||
@@ -48,7 +48,7 @@ Lv2Features::Lv2Features()
|
||||
{
|
||||
const Lv2Manager* man = Engine::getLv2Manager();
|
||||
// create (yet empty) map feature URI -> feature
|
||||
for(const char* uri : man->supportedFeatureURIs())
|
||||
for(auto uri : man->supportedFeatureURIs())
|
||||
{
|
||||
m_featureByUri.emplace(uri, nullptr);
|
||||
}
|
||||
@@ -71,7 +71,7 @@ void Lv2Features::initCommon()
|
||||
void Lv2Features::createFeatureVectors()
|
||||
{
|
||||
// create vector of features
|
||||
for(std::pair<const char* const, void*>& pr : m_featureByUri)
|
||||
for(const auto& [uri, feature] : m_featureByUri)
|
||||
{
|
||||
/*
|
||||
If pr.second is nullptr here, this means that the LV2_feature
|
||||
@@ -82,7 +82,7 @@ void Lv2Features::createFeatureVectors()
|
||||
vector creation (This can be done in
|
||||
Lv2Proc::initPluginSpecificFeatures or in Lv2Features::initCommon)
|
||||
*/
|
||||
m_features.push_back(LV2_Feature { pr.first, pr.second });
|
||||
m_features.push_back(LV2_Feature{(const char*)uri.data(), (void*)feature});
|
||||
}
|
||||
|
||||
// create pointer vector (for lilv_plugin_instantiate)
|
||||
|
||||
@@ -28,13 +28,14 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <lilv/lilv.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/buf-size/buf-size.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/options/options.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
|
||||
#include <QDebug>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include "AudioEngine.h"
|
||||
#include "Engine.h"
|
||||
#include "Plugin.h"
|
||||
#include "Lv2ControlBase.h"
|
||||
@@ -46,7 +47,7 @@ namespace lmms
|
||||
{
|
||||
|
||||
|
||||
const std::set<const char*, Lv2Manager::CmpStr> Lv2Manager::pluginBlacklist =
|
||||
const std::set<std::string_view> Lv2Manager::pluginBlacklist =
|
||||
{
|
||||
// github.com/calf-studio-gear/calf, #278
|
||||
"http://calf.sourceforge.net/plugins/Analyzer",
|
||||
@@ -137,6 +138,26 @@ const std::set<const char*, Lv2Manager::CmpStr> Lv2Manager::pluginBlacklist =
|
||||
"urn:juced:DrumSynth"
|
||||
};
|
||||
|
||||
const std::set<std::string_view> Lv2Manager::pluginBlacklistBuffersizeLessThan32 =
|
||||
{
|
||||
"http://moddevices.com/plugins/mod-devel/2Voices",
|
||||
"http://moddevices.com/plugins/mod-devel/Capo",
|
||||
"http://moddevices.com/plugins/mod-devel/Drop",
|
||||
"http://moddevices.com/plugins/mod-devel/Harmonizer",
|
||||
"http://moddevices.com/plugins/mod-devel/Harmonizer2",
|
||||
"http://moddevices.com/plugins/mod-devel/HarmonizerCS",
|
||||
"http://moddevices.com/plugins/mod-devel/SuperCapo",
|
||||
"http://moddevices.com/plugins/mod-devel/SuperWhammy",
|
||||
"http://moddevices.com/plugins/mod-devel/Gx2Voices",
|
||||
"http://moddevices.com/plugins/mod-devel/GxCapo",
|
||||
"http://moddevices.com/plugins/mod-devel/GxDrop",
|
||||
"http://moddevices.com/plugins/mod-devel/GxHarmonizer",
|
||||
"http://moddevices.com/plugins/mod-devel/GxHarmonizer2",
|
||||
"http://moddevices.com/plugins/mod-devel/GxHarmonizerCS",
|
||||
"http://moddevices.com/plugins/mod-devel/GxSuperCapo",
|
||||
"http://moddevices.com/plugins/mod-devel/GxSuperWhammy"
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -152,10 +173,15 @@ Lv2Manager::Lv2Manager() :
|
||||
m_supportedFeatureURIs.insert(LV2_URID__map);
|
||||
m_supportedFeatureURIs.insert(LV2_URID__unmap);
|
||||
m_supportedFeatureURIs.insert(LV2_OPTIONS__options);
|
||||
m_supportedFeatureURIs.insert(LV2_WORKER__schedule);
|
||||
// min/max is always passed in the options
|
||||
m_supportedFeatureURIs.insert(LV2_BUF_SIZE__boundedBlockLength);
|
||||
// block length is only changed initially in AudioEngine CTOR
|
||||
m_supportedFeatureURIs.insert(LV2_BUF_SIZE__fixedBlockLength);
|
||||
if (const auto fpp = Engine::audioEngine()->framesPerPeriod(); (fpp & (fpp - 1)) == 0) // <=> ffp is power of 2 (for ffp > 0)
|
||||
{
|
||||
m_supportedFeatureURIs.insert(LV2_BUF_SIZE__powerOf2BlockLength);
|
||||
}
|
||||
|
||||
auto supportOpt = [this](Lv2UridCache::Id id)
|
||||
{
|
||||
@@ -217,7 +243,7 @@ void Lv2Manager::initPlugins()
|
||||
const LilvPlugin* curPlug = lilv_plugins_get(plugins, itr);
|
||||
|
||||
std::vector<PluginIssue> issues;
|
||||
Plugin::PluginTypes type = Lv2ControlBase::check(curPlug, issues);
|
||||
Plugin::Type type = Lv2ControlBase::check(curPlug, issues);
|
||||
std::sort(issues.begin(), issues.end());
|
||||
auto last = std::unique(issues.begin(), issues.end());
|
||||
issues.erase(last, issues.end());
|
||||
@@ -240,7 +266,7 @@ void Lv2Manager::initPlugins()
|
||||
{
|
||||
if(std::any_of(issues.begin(), issues.end(),
|
||||
[](const PluginIssue& iss) {
|
||||
return iss.type() == PluginIssueType::blacklisted; }))
|
||||
return iss.type() == PluginIssueType::Blacklisted; }))
|
||||
{
|
||||
++blacklisted;
|
||||
}
|
||||
@@ -288,14 +314,6 @@ void Lv2Manager::initPlugins()
|
||||
|
||||
|
||||
|
||||
bool Lv2Manager::CmpStr::operator()(const char *a, const char *b) const
|
||||
{
|
||||
return std::strcmp(a, b) < 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool Lv2Manager::isFeatureSupported(const char *featName) const
|
||||
{
|
||||
return m_supportedFeatureURIs.find(featName) != m_supportedFeatureURIs.end();
|
||||
|
||||
@@ -114,19 +114,19 @@ std::vector<PluginIssue> Meta::get(const LilvPlugin *plugin,
|
||||
|
||||
m_optional = hasProperty(LV2_CORE__connectionOptional);
|
||||
|
||||
m_vis = hasProperty(LV2_CORE__integer)
|
||||
? Vis::Integer // WARNING: this may still be changed below
|
||||
m_vis = hasProperty(LV2_CORE__toggled)
|
||||
? Vis::Toggled
|
||||
: hasProperty(LV2_CORE__enumeration)
|
||||
? Vis::Enumeration
|
||||
: hasProperty(LV2_CORE__toggled)
|
||||
? Vis::Toggled
|
||||
: hasProperty(LV2_CORE__integer)
|
||||
? Vis::Integer // WARNING: this may still be changed below
|
||||
: Vis::Generic;
|
||||
|
||||
if (isA(LV2_CORE__InputPort)) { m_flow = Flow::Input; }
|
||||
else if (isA(LV2_CORE__OutputPort)) { m_flow = Flow::Output; }
|
||||
else {
|
||||
m_flow = Flow::Unknown;
|
||||
issue(unknownPortFlow, portName);
|
||||
issue(PluginIssueType::UnknownPortFlow, portName);
|
||||
}
|
||||
|
||||
m_def = .0f;
|
||||
@@ -145,7 +145,7 @@ std::vector<PluginIssue> Meta::get(const LilvPlugin *plugin,
|
||||
if (isA(LV2_CORE__CVPort))
|
||||
{
|
||||
// currently not supported, but we can still check the metadata
|
||||
issue(badPortType, "cvPort");
|
||||
issue(PluginIssueType::BadPortType, "cvPort");
|
||||
}
|
||||
|
||||
m_type = isA(LV2_CORE__CVPort) ? Type::Cv : Type::Control;
|
||||
@@ -172,21 +172,21 @@ std::vector<PluginIssue> Meta::get(const LilvPlugin *plugin,
|
||||
}
|
||||
};
|
||||
|
||||
takeRangeValue(def.get(), m_def, portHasNoDef);
|
||||
takeRangeValue(def.get(), m_def, PluginIssueType::PortHasNoDef);
|
||||
if (isToggle)
|
||||
{
|
||||
m_min = .0f;
|
||||
m_max = 1.f;
|
||||
if(def.get() && m_def != m_min && m_def != m_max)
|
||||
{
|
||||
issue(defaultValueNotInRange, portName);
|
||||
issue(PluginIssueType::DefaultValueNotInRange, portName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// take min/max
|
||||
takeRangeValue(min.get(), m_min, portHasNoMin);
|
||||
takeRangeValue(max.get(), m_max, portHasNoMax);
|
||||
takeRangeValue(min.get(), m_min, PluginIssueType::PortHasNoMin);
|
||||
takeRangeValue(max.get(), m_max, PluginIssueType::PortHasNoMax);
|
||||
if(m_type == Type::Cv)
|
||||
{
|
||||
// no range is allowed and bashed to [-1,+1],
|
||||
@@ -196,10 +196,10 @@ std::vector<PluginIssue> Meta::get(const LilvPlugin *plugin,
|
||||
m_min = -1.f;
|
||||
m_max = +1.f;
|
||||
}
|
||||
else if(!m_min_set()) { issue(portHasNoMin, portName); }
|
||||
else if(!m_max_set()) { issue(portHasNoMax, portName); }
|
||||
else if(!m_min_set()) { issue(PluginIssueType::PortHasNoMin, portName); }
|
||||
else if(!m_max_set()) { issue(PluginIssueType::PortHasNoMax, portName); }
|
||||
}
|
||||
if(m_min > m_max) { issue(minGreaterMax, portName); }
|
||||
if(m_min > m_max) { issue(PluginIssueType::MinGreaterMax, portName); }
|
||||
|
||||
// sampleRate
|
||||
if (hasProperty(LV2_CORE__sampleRate)) { m_sampleRate = true; }
|
||||
@@ -207,7 +207,7 @@ std::vector<PluginIssue> Meta::get(const LilvPlugin *plugin,
|
||||
// default value
|
||||
if (def.get())
|
||||
{
|
||||
if (m_def < m_min) { issue(defaultValueNotInRange, portName); }
|
||||
if (m_def < m_min) { issue(PluginIssueType::DefaultValueNotInRange, portName); }
|
||||
else if (m_def > m_max)
|
||||
{
|
||||
if(m_sampleRate)
|
||||
@@ -215,7 +215,7 @@ std::vector<PluginIssue> Meta::get(const LilvPlugin *plugin,
|
||||
// multiplying with sample rate will hopefully lead us
|
||||
// to a good default value
|
||||
}
|
||||
else { issue(defaultValueNotInRange, portName); }
|
||||
else { issue(PluginIssueType::DefaultValueNotInRange, portName); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ std::vector<PluginIssue> Meta::get(const LilvPlugin *plugin,
|
||||
{
|
||||
if (m_optional) { m_used = false; }
|
||||
else {
|
||||
issue(PluginIssueType::unknownPortType, portName);
|
||||
issue(PluginIssueType::UnknownPortType, portName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,16 +265,16 @@ std::vector<PluginIssue> Meta::get(const LilvPlugin *plugin,
|
||||
// be non-Lv2-conforming
|
||||
if(m_min == std::numeric_limits<decltype(m_min)>::lowest())
|
||||
{
|
||||
issue(PluginIssueType::logScaleMinMissing, portName);
|
||||
issue(PluginIssueType::LogScaleMinMissing, portName);
|
||||
}
|
||||
if(m_max == std::numeric_limits<decltype(m_max)>::max())
|
||||
{
|
||||
issue(PluginIssueType::logScaleMaxMissing, portName);
|
||||
issue(PluginIssueType::LogScaleMaxMissing, portName);
|
||||
}
|
||||
// forbid min < 0 < max
|
||||
if(m_min < 0.f && m_max > 0.f)
|
||||
{
|
||||
issue(PluginIssueType::logScaleMinMaxDifferentSigns, portName);
|
||||
issue(PluginIssueType::LogScaleMinMaxDifferentSigns, portName);
|
||||
}
|
||||
m_logarithmic = true;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Lv2Proc.cpp - Lv2 processor class
|
||||
*
|
||||
* Copyright (c) 2019-2020 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
* Copyright (c) 2019-2022 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <lv2/lv2plug.in/ns/ext/midi/midi.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/atom/atom.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/resize-port/resize-port.h>
|
||||
#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
|
||||
#include <QDebug>
|
||||
#include <QDomDocument>
|
||||
#include <QtGlobal>
|
||||
@@ -61,7 +62,7 @@ struct MidiInputEvent
|
||||
|
||||
|
||||
|
||||
Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin,
|
||||
Plugin::Type Lv2Proc::check(const LilvPlugin *plugin,
|
||||
std::vector<PluginIssue>& issues)
|
||||
{
|
||||
unsigned maxPorts = lilv_plugin_get_num_ports(plugin);
|
||||
@@ -75,11 +76,19 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin,
|
||||
// TODO: manage a global blacklist outside of the code
|
||||
// for now, this will help
|
||||
// this is only a fix for the meantime
|
||||
const auto& pluginBlacklist = Lv2Manager::getPluginBlacklist();
|
||||
if (!Engine::ignorePluginBlacklist() &&
|
||||
pluginBlacklist.find(pluginUri) != pluginBlacklist.end())
|
||||
if (!Engine::ignorePluginBlacklist())
|
||||
{
|
||||
issues.emplace_back(blacklisted);
|
||||
const auto& pluginBlacklist = Lv2Manager::getPluginBlacklist();
|
||||
const auto& pluginBlacklist32 = Lv2Manager::getPluginBlacklistBuffersizeLessThan32();
|
||||
if(pluginBlacklist.find(pluginUri) != pluginBlacklist.end())
|
||||
{
|
||||
issues.emplace_back(PluginIssueType::Blacklisted);
|
||||
}
|
||||
else if(Engine::audioEngine()->framesPerPeriod() <= 32 &&
|
||||
pluginBlacklist32.find(pluginUri) != pluginBlacklist32.end())
|
||||
{
|
||||
issues.emplace_back(PluginIssueType::Blacklisted); // currently no special blacklist category
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned portNum = 0; portNum < maxPorts; ++portNum)
|
||||
@@ -106,19 +115,19 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin,
|
||||
}
|
||||
|
||||
if (audioChannels[inCount] > 2)
|
||||
issues.emplace_back(tooManyInputChannels,
|
||||
issues.emplace_back(PluginIssueType::TooManyInputChannels,
|
||||
std::to_string(audioChannels[inCount]));
|
||||
if (audioChannels[outCount] == 0)
|
||||
issues.emplace_back(noOutputChannel);
|
||||
issues.emplace_back(PluginIssueType::NoOutputChannel);
|
||||
else if (audioChannels[outCount] > 2)
|
||||
issues.emplace_back(tooManyOutputChannels,
|
||||
issues.emplace_back(PluginIssueType::TooManyOutputChannels,
|
||||
std::to_string(audioChannels[outCount]));
|
||||
|
||||
if (midiChannels[inCount] > 1)
|
||||
issues.emplace_back(tooManyMidiInputChannels,
|
||||
issues.emplace_back(PluginIssueType::TooManyMidiInputChannels,
|
||||
std::to_string(midiChannels[inCount]));
|
||||
if (midiChannels[outCount] > 1)
|
||||
issues.emplace_back(tooManyMidiOutputChannels,
|
||||
issues.emplace_back(PluginIssueType::TooManyMidiOutputChannels,
|
||||
std::to_string(midiChannels[outCount]));
|
||||
|
||||
AutoLilvNodes reqFeats(lilv_plugin_get_required_features(plugin));
|
||||
@@ -128,7 +137,7 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin,
|
||||
lilv_nodes_get(reqFeats.get(), itr));
|
||||
if(!Lv2Features::isFeatureSupported(reqFeatName))
|
||||
{
|
||||
issues.emplace_back(featureNotSupported, reqFeatName);
|
||||
issues.emplace_back(PluginIssueType::FeatureNotSupported, reqFeatName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,16 +153,16 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin,
|
||||
{
|
||||
// yes, this is not a Lv2 feature,
|
||||
// but it's a feature in abstract sense
|
||||
issues.emplace_back(featureNotSupported, ro);
|
||||
issues.emplace_back(PluginIssueType::FeatureNotSupported, ro);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (audioChannels[inCount] > 2 || audioChannels[outCount] > 2)
|
||||
? Plugin::Undefined
|
||||
? Plugin::Type::Undefined
|
||||
: (audioChannels[inCount] > 0)
|
||||
? Plugin::Effect
|
||||
: Plugin::Instrument;
|
||||
? Plugin::Type::Effect
|
||||
: Plugin::Type::Instrument;
|
||||
}
|
||||
|
||||
|
||||
@@ -162,6 +171,7 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin,
|
||||
Lv2Proc::Lv2Proc(const LilvPlugin *plugin, Model* parent) :
|
||||
LinkedModelGroup(parent),
|
||||
m_plugin(plugin),
|
||||
m_workLock(1),
|
||||
m_midiInputBuf(m_maxMidiInputEvents),
|
||||
m_midiInputReader(m_midiInputBuf)
|
||||
{
|
||||
@@ -352,7 +362,19 @@ void Lv2Proc::copyBuffersToCore(sampleFrame* buf,
|
||||
|
||||
void Lv2Proc::run(fpp_t frames)
|
||||
{
|
||||
if (m_worker)
|
||||
{
|
||||
// Process any worker replies
|
||||
m_worker->emitResponses();
|
||||
}
|
||||
|
||||
lilv_instance_run(m_instance, static_cast<uint32_t>(frames));
|
||||
|
||||
if (m_worker)
|
||||
{
|
||||
// Notify the plugin the run() cycle is finished
|
||||
m_worker->notifyPluginThatRunFinished();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -420,6 +442,9 @@ void Lv2Proc::initPlugin()
|
||||
|
||||
if (m_instance)
|
||||
{
|
||||
if(m_worker) {
|
||||
m_worker->setHandle(lilv_instance_get_handle(m_instance));
|
||||
}
|
||||
for (std::size_t portNum = 0; portNum < m_ports.size(); ++portNum)
|
||||
connectPort(portNum);
|
||||
lilv_instance_activate(m_instance);
|
||||
@@ -496,8 +521,20 @@ void Lv2Proc::initMOptions()
|
||||
|
||||
void Lv2Proc::initPluginSpecificFeatures()
|
||||
{
|
||||
// options
|
||||
initMOptions();
|
||||
m_features[LV2_OPTIONS__options] = const_cast<LV2_Options_Option*>(m_options.feature());
|
||||
|
||||
// worker (if plugin has worker extension)
|
||||
Lv2Manager* mgr = Engine::getLv2Manager();
|
||||
if (lilv_plugin_has_extension_data(m_plugin, mgr->uri(LV2_WORKER__interface).get())) {
|
||||
const auto iface = static_cast<const LV2_Worker_Interface*>(
|
||||
lilv_instance_get_extension_data(m_instance, LV2_WORKER__interface));
|
||||
bool threaded = !Engine::audioEngine()->renderOnly();
|
||||
m_worker.emplace(iface, &m_workLock, threaded);
|
||||
m_features[LV2_WORKER__schedule] = m_worker->feature();
|
||||
// Note: m_worker::setHandle will still need to be called later
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -566,21 +603,35 @@ void Lv2Proc::createPort(std::size_t portNum)
|
||||
break;
|
||||
case Lv2Ports::Vis::Enumeration:
|
||||
{
|
||||
auto comboModel = new ComboBoxModel(nullptr, dispName);
|
||||
LilvScalePoints* sps =
|
||||
lilv_port_get_scale_points(m_plugin, lilvPort);
|
||||
LILV_FOREACH(scale_points, i, sps)
|
||||
ComboBoxModel* comboModel = new ComboBoxModel(nullptr, dispName);
|
||||
|
||||
{
|
||||
const LilvScalePoint* sp = lilv_scale_points_get(sps, i);
|
||||
ctrl->m_scalePointMap.push_back(lilv_node_as_float(
|
||||
lilv_scale_point_get_value(sp)));
|
||||
comboModel->addItem(
|
||||
lilv_node_as_string(
|
||||
lilv_scale_point_get_label(sp)));
|
||||
AutoLilvScalePoints sps (static_cast<LilvScalePoints*>(lilv_port_get_scale_points(m_plugin, lilvPort)));
|
||||
// temporary map, since lilv may return scale points in random order
|
||||
std::map<float, const char*> scalePointMap;
|
||||
LILV_FOREACH(scale_points, i, sps.get())
|
||||
{
|
||||
const LilvScalePoint* sp = lilv_scale_points_get(sps.get(), i);
|
||||
const float f = lilv_node_as_float(lilv_scale_point_get_value(sp));
|
||||
const char* s = lilv_node_as_string(lilv_scale_point_get_label(sp));
|
||||
scalePointMap[f] = s;
|
||||
}
|
||||
for (const auto& [f,s] : scalePointMap)
|
||||
{
|
||||
ctrl->m_scalePointMap.push_back(f);
|
||||
comboModel->addItem(s);
|
||||
}
|
||||
}
|
||||
for(std::size_t i = 0; i < ctrl->m_scalePointMap.size(); ++i)
|
||||
{
|
||||
if(meta.def() == ctrl->m_scalePointMap[i])
|
||||
{
|
||||
comboModel->setValue(i);
|
||||
comboModel->setInitValue(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
lilv_scale_points_free(sps);
|
||||
ctrl->m_connectedModel.reset(comboModel);
|
||||
// TODO: use default value on comboModel, too?
|
||||
break;
|
||||
}
|
||||
case Lv2Ports::Vis::Toggled:
|
||||
|
||||
@@ -59,7 +59,7 @@ QString Lv2SubPluginFeatures::pluginName(const LilvPlugin *plug)
|
||||
|
||||
|
||||
|
||||
Lv2SubPluginFeatures::Lv2SubPluginFeatures(Plugin::PluginTypes type) :
|
||||
Lv2SubPluginFeatures::Lv2SubPluginFeatures(Plugin::Type type) :
|
||||
SubPluginFeatures(type)
|
||||
{
|
||||
}
|
||||
|
||||
203
src/core/lv2/Lv2Worker.cpp
Normal file
203
src/core/lv2/Lv2Worker.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Lv2Worker.cpp - Lv2Worker implementation
|
||||
*
|
||||
* Copyright (c) 2022-2022 Johannes Lorenz <jlsf2013$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 "Lv2Worker.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef LMMS_HAVE_LV2
|
||||
|
||||
#include "Engine.h"
|
||||
|
||||
|
||||
namespace lmms
|
||||
{
|
||||
|
||||
|
||||
// static wrappers
|
||||
|
||||
static LV2_Worker_Status
|
||||
staticWorkerRespond(LV2_Worker_Respond_Handle handle,
|
||||
uint32_t size, const void* data)
|
||||
{
|
||||
Lv2Worker* worker = static_cast<Lv2Worker*>(handle);
|
||||
return worker->respond(size, data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::size_t Lv2Worker::bufferSize() const
|
||||
{
|
||||
// ardour uses this fixed size for ALSA:
|
||||
return 8192 * 4;
|
||||
// for jack, they use 4 * jack_port_type_get_buffer_size (..., JACK_DEFAULT_MIDI_TYPE)
|
||||
// (possible extension for AudioDevice)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Lv2Worker::Lv2Worker(const LV2_Worker_Interface* iface,
|
||||
Semaphore* common_work_lock,
|
||||
bool threaded) :
|
||||
m_iface(iface),
|
||||
m_threaded(threaded),
|
||||
m_response(bufferSize()),
|
||||
m_requests(bufferSize()),
|
||||
m_responses(bufferSize()),
|
||||
m_requestsReader(m_requests),
|
||||
m_responsesReader(m_responses),
|
||||
m_sem(0),
|
||||
m_workLock(common_work_lock)
|
||||
{
|
||||
assert(iface);
|
||||
m_scheduleFeature.handle = static_cast<LV2_Worker_Schedule_Handle>(this);
|
||||
m_scheduleFeature.schedule_work = [](LV2_Worker_Schedule_Handle handle,
|
||||
uint32_t size, const void* data) -> LV2_Worker_Status
|
||||
{
|
||||
Lv2Worker* worker = static_cast<Lv2Worker*>(handle);
|
||||
return worker->scheduleWork(size, data);
|
||||
};
|
||||
|
||||
if (threaded) { m_thread = std::thread(&Lv2Worker::workerFunc, this); }
|
||||
|
||||
m_requests.mlock();
|
||||
m_responses.mlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Lv2Worker::~Lv2Worker()
|
||||
{
|
||||
m_exit = true;
|
||||
if(m_threaded) {
|
||||
m_sem.post();
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Let the worker send responses to the audio thread
|
||||
LV2_Worker_Status Lv2Worker::respond(uint32_t size, const void* data)
|
||||
{
|
||||
if(m_threaded)
|
||||
{
|
||||
if(m_responses.free() < sizeof(size) + size)
|
||||
{
|
||||
return LV2_WORKER_ERR_NO_SPACE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_responses.write((const char*)&size, sizeof(size));
|
||||
if(size && data) { m_responses.write((const char*)data, size); }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iface->work_response(m_handle, size, data);
|
||||
}
|
||||
return LV2_WORKER_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Let the worker receive work from the audio thread and "work" on it
|
||||
void Lv2Worker::workerFunc()
|
||||
{
|
||||
std::vector<char> buf;
|
||||
uint32_t size;
|
||||
while (true) {
|
||||
m_sem.wait();
|
||||
if (m_exit) { break; }
|
||||
const std::size_t readSpace = m_requestsReader.read_space();
|
||||
if (readSpace <= sizeof(size)) { continue; } // (should not happen)
|
||||
|
||||
m_requestsReader.read(sizeof(size)).copy((char*)&size, sizeof(size));
|
||||
assert(size <= readSpace - sizeof(size));
|
||||
if(size > buf.size()) { buf.resize(size); }
|
||||
if(size) { m_requestsReader.read(size).copy(buf.data(), size); }
|
||||
|
||||
m_workLock->wait();
|
||||
m_iface->work(m_handle, staticWorkerRespond, this, size, buf.data());
|
||||
m_workLock->post();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Let the audio thread schedule work for the worker
|
||||
LV2_Worker_Status Lv2Worker::scheduleWork(uint32_t size, const void *data)
|
||||
{
|
||||
if (m_threaded)
|
||||
{
|
||||
if(m_requests.free() < sizeof(size) + size)
|
||||
{
|
||||
return LV2_WORKER_ERR_NO_SPACE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Schedule a request to be executed by the worker thread
|
||||
m_requests.write((const char*)&size, sizeof(size));
|
||||
if(size && data) { m_requests.write((const char*)data, size); }
|
||||
m_sem.post();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Execute work immediately in this thread
|
||||
m_workLock->wait();
|
||||
m_iface->work(m_handle, staticWorkerRespond, this, size, data);
|
||||
m_workLock->post();
|
||||
}
|
||||
|
||||
return LV2_WORKER_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Let the audio thread read incoming worker responses, and process it
|
||||
void Lv2Worker::emitResponses()
|
||||
{
|
||||
std::size_t read_space = m_responsesReader.read_space();
|
||||
uint32_t size;
|
||||
while (read_space > sizeof(size)) {
|
||||
m_responsesReader.read(sizeof(size)).copy((char*)&size, sizeof(size));
|
||||
if(size) { m_responsesReader.read(size).copy(m_response.data(), size); }
|
||||
m_iface->work_response(m_handle, size, m_response.data());
|
||||
read_space -= sizeof(size) + size;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
#endif // LMMS_HAVE_LV2
|
||||
@@ -363,9 +363,9 @@ int main( int argc, char * * argv )
|
||||
new QCoreApplication( argc, argv ) :
|
||||
new gui::MainApplication(argc, argv);
|
||||
|
||||
AudioEngine::qualitySettings qs( AudioEngine::qualitySettings::Mode_HighQuality );
|
||||
OutputSettings os( 44100, OutputSettings::BitRateSettings(160, false), OutputSettings::Depth_16Bit, OutputSettings::StereoMode_JointStereo );
|
||||
ProjectRenderer::ExportFileFormats eff = ProjectRenderer::WaveFile;
|
||||
AudioEngine::qualitySettings qs( AudioEngine::qualitySettings::Mode::HighQuality );
|
||||
OutputSettings os( 44100, OutputSettings::BitRateSettings(160, false), OutputSettings::BitDepth::Depth16Bit, OutputSettings::StereoMode::JointStereo );
|
||||
ProjectRenderer::ExportFileFormat eff = ProjectRenderer::ExportFileFormat::Wave;
|
||||
|
||||
// second of two command-line parsing stages
|
||||
for( int i = 1; i < argc; ++i )
|
||||
@@ -517,23 +517,23 @@ int main( int argc, char * * argv )
|
||||
|
||||
if( ext == "wav" )
|
||||
{
|
||||
eff = ProjectRenderer::WaveFile;
|
||||
eff = ProjectRenderer::ExportFileFormat::Wave;
|
||||
}
|
||||
#ifdef LMMS_HAVE_OGGVORBIS
|
||||
else if( ext == "ogg" )
|
||||
{
|
||||
eff = ProjectRenderer::OggFile;
|
||||
eff = ProjectRenderer::ExportFileFormat::Ogg;
|
||||
}
|
||||
#endif
|
||||
#ifdef LMMS_HAVE_MP3LAME
|
||||
else if( ext == "mp3" )
|
||||
{
|
||||
eff = ProjectRenderer::MP3File;
|
||||
eff = ProjectRenderer::ExportFileFormat::MP3;
|
||||
}
|
||||
#endif
|
||||
else if (ext == "flac")
|
||||
{
|
||||
eff = ProjectRenderer::FlacFile;
|
||||
eff = ProjectRenderer::ExportFileFormat::Flac;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -596,15 +596,15 @@ int main( int argc, char * * argv )
|
||||
|
||||
if( mode == "s" )
|
||||
{
|
||||
os.setStereoMode(OutputSettings::StereoMode_Stereo);
|
||||
os.setStereoMode(OutputSettings::StereoMode::Stereo);
|
||||
}
|
||||
else if( mode == "j" )
|
||||
{
|
||||
os.setStereoMode(OutputSettings::StereoMode_JointStereo);
|
||||
os.setStereoMode(OutputSettings::StereoMode::JointStereo);
|
||||
}
|
||||
else if( mode == "m" )
|
||||
{
|
||||
os.setStereoMode(OutputSettings::StereoMode_Mono);
|
||||
os.setStereoMode(OutputSettings::StereoMode::Mono);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -613,7 +613,7 @@ int main( int argc, char * * argv )
|
||||
}
|
||||
else if( arg =="--float" || arg == "-a" )
|
||||
{
|
||||
os.setBitDepth(OutputSettings::Depth_32Bit);
|
||||
os.setBitDepth(OutputSettings::BitDepth::Depth32Bit);
|
||||
}
|
||||
else if( arg == "--interpolation" || arg == "-i" )
|
||||
{
|
||||
@@ -629,19 +629,19 @@ int main( int argc, char * * argv )
|
||||
|
||||
if( ip == "linear" )
|
||||
{
|
||||
qs.interpolation = AudioEngine::qualitySettings::Interpolation_Linear;
|
||||
qs.interpolation = AudioEngine::qualitySettings::Interpolation::Linear;
|
||||
}
|
||||
else if( ip == "sincfastest" )
|
||||
{
|
||||
qs.interpolation = AudioEngine::qualitySettings::Interpolation_SincFastest;
|
||||
qs.interpolation = AudioEngine::qualitySettings::Interpolation::SincFastest;
|
||||
}
|
||||
else if( ip == "sincmedium" )
|
||||
{
|
||||
qs.interpolation = AudioEngine::qualitySettings::Interpolation_SincMedium;
|
||||
qs.interpolation = AudioEngine::qualitySettings::Interpolation::SincMedium;
|
||||
}
|
||||
else if( ip == "sincbest" )
|
||||
{
|
||||
qs.interpolation = AudioEngine::qualitySettings::Interpolation_SincBest;
|
||||
qs.interpolation = AudioEngine::qualitySettings::Interpolation::SincBest;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -663,16 +663,16 @@ int main( int argc, char * * argv )
|
||||
switch( o )
|
||||
{
|
||||
case 1:
|
||||
qs.oversampling = AudioEngine::qualitySettings::Oversampling_None;
|
||||
qs.oversampling = AudioEngine::qualitySettings::Oversampling::None;
|
||||
break;
|
||||
case 2:
|
||||
qs.oversampling = AudioEngine::qualitySettings::Oversampling_2x;
|
||||
qs.oversampling = AudioEngine::qualitySettings::Oversampling::X2;
|
||||
break;
|
||||
case 4:
|
||||
qs.oversampling = AudioEngine::qualitySettings::Oversampling_4x;
|
||||
qs.oversampling = AudioEngine::qualitySettings::Oversampling::X4;
|
||||
break;
|
||||
case 8:
|
||||
qs.oversampling = AudioEngine::qualitySettings::Oversampling_8x;
|
||||
qs.oversampling = AudioEngine::qualitySettings::Oversampling::X8;
|
||||
break;
|
||||
default:
|
||||
return usageError( QString( "Invalid oversampling %1" ).arg( argv[i] ) );
|
||||
|
||||
@@ -245,16 +245,16 @@ void MidiAlsaSeq::applyPortMode( MidiPort * _port )
|
||||
|
||||
switch( _port->mode() )
|
||||
{
|
||||
case MidiPort::Duplex:
|
||||
case MidiPort::Mode::Duplex:
|
||||
caps[1] |= SND_SEQ_PORT_CAP_READ |
|
||||
SND_SEQ_PORT_CAP_SUBS_READ;
|
||||
|
||||
case MidiPort::Input:
|
||||
case MidiPort::Mode::Input:
|
||||
caps[0] |= SND_SEQ_PORT_CAP_WRITE |
|
||||
SND_SEQ_PORT_CAP_SUBS_WRITE;
|
||||
break;
|
||||
|
||||
case MidiPort::Output:
|
||||
case MidiPort::Mode::Output:
|
||||
caps[1] |= SND_SEQ_PORT_CAP_READ |
|
||||
SND_SEQ_PORT_CAP_SUBS_READ;
|
||||
break;
|
||||
|
||||
@@ -259,7 +259,7 @@ void MidiApple::HandleReadCallback( const MIDIPacketList *pktlist, void *srcConn
|
||||
nBytes = packet->length;
|
||||
// Check if this is the end of a continued SysEx message
|
||||
if (continueSysEx) {
|
||||
unsigned int lengthToCopy = qMin(nBytes, SYSEX_LENGTH - sysExLength);
|
||||
unsigned int lengthToCopy = std::min(nBytes, SYSEX_LENGTH - sysExLength);
|
||||
// Copy the message into our SysEx message buffer,
|
||||
// making sure not to overrun the buffer
|
||||
memcpy(sysExMessage + sysExLength, packet->data, lengthToCopy);
|
||||
@@ -298,7 +298,7 @@ void MidiApple::HandleReadCallback( const MIDIPacketList *pktlist, void *srcConn
|
||||
{
|
||||
// MIDI SysEx then we copy the rest of the message into the SysEx message buffer
|
||||
unsigned int lengthLeftInMessage = nBytes - iByte;
|
||||
unsigned int lengthToCopy = qMin(lengthLeftInMessage, SYSEX_LENGTH);
|
||||
unsigned int lengthToCopy = std::min(lengthLeftInMessage, SYSEX_LENGTH);
|
||||
|
||||
memcpy(sysExMessage + sysExLength, packet->data, lengthToCopy);
|
||||
sysExLength += lengthToCopy;
|
||||
@@ -403,7 +403,7 @@ void MidiApple::midiInClose( MIDIEndpointRef reference )
|
||||
|
||||
|
||||
|
||||
char *getName( MIDIObjectRef &object )
|
||||
char *getName( const MIDIObjectRef &object )
|
||||
{
|
||||
// Returns the name of a given MIDIObjectRef as char *
|
||||
CFStringRef name = nullptr;
|
||||
|
||||
@@ -71,8 +71,7 @@ void MidiClient::removePort( MidiPort* port )
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<MidiPort *>::Iterator it =
|
||||
std::find( m_midiPorts.begin(), m_midiPorts.end(), port );
|
||||
auto it = std::find(m_midiPorts.begin(), m_midiPorts.end(), port);
|
||||
if( it != m_midiPorts.end() )
|
||||
{
|
||||
m_midiPorts.erase( it );
|
||||
|
||||
@@ -33,9 +33,9 @@ namespace lmms
|
||||
|
||||
|
||||
MidiController::MidiController( Model * _parent ) :
|
||||
Controller( Controller::MidiController, _parent, tr( "MIDI Controller" ) ),
|
||||
Controller( ControllerType::Midi, _parent, tr( "MIDI Controller" ) ),
|
||||
MidiEventProcessor(),
|
||||
m_midiPort( tr( "unnamed_midi_controller" ), Engine::audioEngine()->midiClient(), this, this, MidiPort::Input ),
|
||||
m_midiPort( tr( "unnamed_midi_controller" ), Engine::audioEngine()->midiClient(), this, this, MidiPort::Mode::Input ),
|
||||
m_lastValue( 0.0f ),
|
||||
m_previousValue( 0.0f )
|
||||
{
|
||||
@@ -72,21 +72,20 @@ void MidiController::updateName()
|
||||
|
||||
|
||||
|
||||
void MidiController::processInEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset )
|
||||
void MidiController::processInEvent(const MidiEvent& event, const TimePos& time, f_cnt_t offset)
|
||||
{
|
||||
unsigned char controllerNum;
|
||||
switch( event.type() )
|
||||
switch(event.type())
|
||||
{
|
||||
case MidiControlChange:
|
||||
controllerNum = event.controllerNumber();
|
||||
|
||||
if( m_midiPort.inputController() == controllerNum + 1 &&
|
||||
( m_midiPort.inputChannel() == event.channel() + 1 ||
|
||||
m_midiPort.inputChannel() == 0 ) )
|
||||
if (m_midiPort.inputController() == controllerNum &&
|
||||
(m_midiPort.inputChannel() == event.channel() + 1 || m_midiPort.inputChannel() == 0))
|
||||
{
|
||||
unsigned char val = event.controllerValue();
|
||||
m_previousValue = m_lastValue;
|
||||
m_lastValue = (float)( val ) / 127.0f;
|
||||
m_lastValue = static_cast<float>(val) / 127.0f;
|
||||
emit valueChanged();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "MidiEventProcessor.h"
|
||||
#include "Note.h"
|
||||
#include "Song.h"
|
||||
#include "MidiController.h"
|
||||
|
||||
|
||||
namespace lmms
|
||||
@@ -54,8 +55,8 @@ MidiPort::MidiPort( const QString& name,
|
||||
m_mode( mode ),
|
||||
m_inputChannelModel( 0, 0, MidiChannelCount, this, tr( "Input channel" ) ),
|
||||
m_outputChannelModel( 1, 0, MidiChannelCount, this, tr( "Output channel" ) ),
|
||||
m_inputControllerModel( 0, 0, MidiControllerCount, this, tr( "Input controller" ) ),
|
||||
m_outputControllerModel( 0, 0, MidiControllerCount, this, tr( "Output controller" ) ),
|
||||
m_inputControllerModel(MidiController::NONE, MidiController::NONE, MidiControllerCount - 1, this, tr( "Input controller" )),
|
||||
m_outputControllerModel(MidiController::NONE, MidiController::NONE, MidiControllerCount - 1, this, tr( "Output controller" )),
|
||||
m_fixedInputVelocityModel( -1, -1, MidiMaxVelocity, this, tr( "Fixed input velocity" ) ),
|
||||
m_fixedOutputVelocityModel( -1, -1, MidiMaxVelocity, this, tr( "Fixed output velocity" ) ),
|
||||
m_fixedOutputNoteModel( -1, -1, MidiMaxKey, this, tr( "Fixed output note" ) ),
|
||||
@@ -66,8 +67,8 @@ MidiPort::MidiPort( const QString& name,
|
||||
{
|
||||
m_midiClient->addPort( this );
|
||||
|
||||
m_readableModel.setValue( m_mode == Input || m_mode == Duplex );
|
||||
m_writableModel.setValue( m_mode == Output || m_mode == Duplex );
|
||||
m_readableModel.setValue( m_mode == Mode::Input || m_mode == Mode::Duplex );
|
||||
m_writableModel.setValue( m_mode == Mode::Output || m_mode == Mode::Duplex );
|
||||
|
||||
connect( &m_readableModel, SIGNAL(dataChanged()),
|
||||
this, SLOT(updateMidiPortMode()), Qt::DirectConnection );
|
||||
@@ -325,10 +326,10 @@ void MidiPort::subscribeWritablePort( const QString& port, bool subscribe )
|
||||
void MidiPort::updateMidiPortMode()
|
||||
{
|
||||
// this small lookup-table makes everything easier
|
||||
static const Modes modeTable[2][2] =
|
||||
static const Mode modeTable[2][2] =
|
||||
{
|
||||
{ Disabled, Output },
|
||||
{ Input, Duplex }
|
||||
{ Mode::Disabled, Mode::Output },
|
||||
{ Mode::Input, Mode::Duplex }
|
||||
} ;
|
||||
setMode( modeTable[m_readableModel.value()][m_writableModel.value()] );
|
||||
|
||||
@@ -436,4 +437,4 @@ void MidiPort::invalidateCilent()
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
} // namespace lmms
|
||||
|
||||
@@ -61,7 +61,7 @@ SET(LMMS_SRCS
|
||||
gui/instrument/EnvelopeAndLfoView.cpp
|
||||
gui/instrument/InstrumentFunctionViews.cpp
|
||||
gui/instrument/InstrumentMidiIOView.cpp
|
||||
gui/instrument/InstrumentMiscView.cpp
|
||||
gui/instrument/InstrumentTuningView.cpp
|
||||
gui/instrument/InstrumentSoundShapingView.cpp
|
||||
gui/instrument/InstrumentTrackWindow.cpp
|
||||
gui/instrument/InstrumentView.cpp
|
||||
|
||||
@@ -258,8 +258,8 @@ int ControlLayout::doLayout(const QRect &rect, bool testOnly) const
|
||||
if (first)
|
||||
{
|
||||
// for the search bar, only show it if there are at least
|
||||
// two control widgets (i.e. at least 3 widgets)
|
||||
if (m_itemMap.size() > 2) { wid->show(); }
|
||||
// five control widgets (i.e. at least 6 widgets)
|
||||
if (m_itemMap.size() > 5) { wid->show(); }
|
||||
else { wid->hide(); }
|
||||
}
|
||||
else { wid->show(); }
|
||||
|
||||
@@ -149,7 +149,7 @@ void ControllerView::renameController()
|
||||
if( ok && !new_name.isEmpty() )
|
||||
{
|
||||
c->setName( new_name );
|
||||
if( getController()->type() == Controller::LfoController )
|
||||
if( getController()->type() == Controller::ControllerType::Lfo )
|
||||
{
|
||||
m_controllerDlg->setWindowTitle( tr( "LFO" ) + " (" + new_name + ")" );
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ AutomatableModelView* CheckControl::modelView() { return m_checkBox; }
|
||||
|
||||
CheckControl::CheckControl(QWidget *parent) :
|
||||
m_widget(new QWidget(parent)),
|
||||
m_checkBox(new LedCheckBox(nullptr, QString(), LedCheckBox::Green)),
|
||||
m_checkBox(new LedCheckBox(nullptr, QString(), LedCheckBox::LedColor::Green)),
|
||||
m_label(new QLabel(m_widget))
|
||||
{
|
||||
auto vbox = new QVBoxLayout(m_widget);
|
||||
|
||||
@@ -56,28 +56,28 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) :
|
||||
|
||||
// Disable effects that are of type "DummyEffect"
|
||||
bool isEnabled = !dynamic_cast<DummyEffect *>( effect() );
|
||||
m_bypass = new LedCheckBox( this, "", isEnabled ? LedCheckBox::Green : LedCheckBox::Red );
|
||||
m_bypass = new LedCheckBox( this, "", isEnabled ? LedCheckBox::LedColor::Green : LedCheckBox::LedColor::Red );
|
||||
m_bypass->move( 3, 3 );
|
||||
m_bypass->setEnabled( isEnabled );
|
||||
|
||||
m_bypass->setToolTip(tr("On/Off"));
|
||||
|
||||
|
||||
m_wetDry = new Knob( knobBright_26, this );
|
||||
m_wetDry = new Knob( KnobType::Bright26, this );
|
||||
m_wetDry->setLabel( tr( "W/D" ) );
|
||||
m_wetDry->move( 40 - m_wetDry->width() / 2, 5 );
|
||||
m_wetDry->setEnabled( isEnabled );
|
||||
m_wetDry->setHintText( tr( "Wet Level:" ), "" );
|
||||
|
||||
|
||||
m_autoQuit = new TempoSyncKnob( knobBright_26, this );
|
||||
m_autoQuit = new TempoSyncKnob( KnobType::Bright26, this );
|
||||
m_autoQuit->setLabel( tr( "DECAY" ) );
|
||||
m_autoQuit->move( 78 - m_autoQuit->width() / 2, 5 );
|
||||
m_autoQuit->setEnabled( isEnabled && !effect()->m_autoQuitDisabled );
|
||||
m_autoQuit->setHintText( tr( "Time:" ), "ms" );
|
||||
|
||||
|
||||
m_gate = new Knob( knobBright_26, this );
|
||||
m_gate = new Knob( KnobType::Bright26, this );
|
||||
m_gate->setLabel( tr( "GATE" ) );
|
||||
m_gate->move( 116 - m_gate->width() / 2, 5 );
|
||||
m_gate->setEnabled( isEnabled && !effect()->m_autoQuitDisabled );
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDesktopServices>
|
||||
#include <QHBoxLayout>
|
||||
@@ -126,7 +125,7 @@ FileBrowser::FileBrowser(const QString & directories, const QString & filter,
|
||||
m_filterEdit->setPlaceholderText( tr("Search") );
|
||||
m_filterEdit->setClearButtonEnabled( true );
|
||||
connect( m_filterEdit, SIGNAL( textEdited( const QString& ) ),
|
||||
this, SLOT( filterItems( const QString& ) ) );
|
||||
this, SLOT( filterAndExpandItems( const QString& ) ) );
|
||||
|
||||
auto reload_btn = new QPushButton(embed::getIconPixmap("reload"), QString(), searchWidget);
|
||||
reload_btn->setToolTip( tr( "Refresh list" ) );
|
||||
@@ -145,53 +144,95 @@ FileBrowser::FileBrowser(const QString & directories, const QString & filter,
|
||||
auto filterFocusShortcut = new QShortcut(QKeySequence(QKeySequence::Find), this, SLOT(giveFocusToFilter()));
|
||||
filterFocusShortcut->setContext(Qt::WidgetWithChildrenShortcut);
|
||||
|
||||
m_previousFilterValue = "";
|
||||
|
||||
reloadTree();
|
||||
show();
|
||||
}
|
||||
|
||||
bool FileBrowser::filterItems( const QString & filter, QTreeWidgetItem * item )
|
||||
void FileBrowser::saveDirectoriesStates()
|
||||
{
|
||||
m_savedExpandedDirs = m_fileBrowserTreeWidget->expandedDirs();
|
||||
}
|
||||
|
||||
void FileBrowser::restoreDirectoriesStates()
|
||||
{
|
||||
// call with item=NULL to filter the entire tree
|
||||
expandItems(nullptr, m_savedExpandedDirs);
|
||||
}
|
||||
|
||||
bool FileBrowser::filterAndExpandItems(const QString & filter, QTreeWidgetItem * item)
|
||||
{
|
||||
// Call with item = nullptr to filter the entire tree
|
||||
|
||||
if (item == nullptr)
|
||||
{
|
||||
// First search character so need to save current expanded directories
|
||||
if (m_previousFilterValue.isEmpty())
|
||||
{
|
||||
saveDirectoriesStates();
|
||||
}
|
||||
|
||||
m_previousFilterValue = filter;
|
||||
}
|
||||
|
||||
if (filter.isEmpty())
|
||||
{
|
||||
// Restore previous expanded directories
|
||||
if (item == nullptr)
|
||||
{
|
||||
restoreDirectoriesStates();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool anyMatched = false;
|
||||
|
||||
int numChildren = item ? item->childCount() : m_fileBrowserTreeWidget->topLevelItemCount();
|
||||
for( int i = 0; i < numChildren; ++i )
|
||||
|
||||
for (int i = 0; i < numChildren; ++i)
|
||||
{
|
||||
QTreeWidgetItem * it = item ? item->child( i ) : m_fileBrowserTreeWidget->topLevelItem(i);
|
||||
|
||||
// is directory?
|
||||
if( it->childCount() )
|
||||
auto d = dynamic_cast<Directory*>(it);
|
||||
if (d)
|
||||
{
|
||||
// matches filter?
|
||||
if( it->text( 0 ).
|
||||
contains( filter, Qt::CaseInsensitive ) )
|
||||
if (it->text(0).contains(filter, Qt::CaseInsensitive))
|
||||
{
|
||||
// yes, then show everything below
|
||||
it->setHidden( false );
|
||||
filterItems( QString(), it );
|
||||
it->setHidden(false);
|
||||
it->setExpanded(true);
|
||||
filterAndExpandItems(QString(), it);
|
||||
anyMatched = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// only show if item below matches filter
|
||||
bool didMatch = filterItems( filter, it );
|
||||
it->setHidden( !didMatch );
|
||||
// Expanding is required when recursive to load in its contents, even if it's collapsed right afterward
|
||||
it->setExpanded(true);
|
||||
|
||||
bool didMatch = filterAndExpandItems(filter, it);
|
||||
it->setHidden(!didMatch);
|
||||
it->setExpanded(didMatch);
|
||||
anyMatched = anyMatched || didMatch;
|
||||
}
|
||||
}
|
||||
// a standard item (i.e. no file or directory item?)
|
||||
else if( it->type() == QTreeWidgetItem::Type )
|
||||
{
|
||||
// hide if there's any filter
|
||||
it->setHidden( !filter.isEmpty() );
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// file matches filter?
|
||||
bool didMatch = it->text( 0 ).
|
||||
contains( filter, Qt::CaseInsensitive );
|
||||
it->setHidden( !didMatch );
|
||||
anyMatched = anyMatched || didMatch;
|
||||
auto f = dynamic_cast<FileItem*>(it);
|
||||
if (f)
|
||||
{
|
||||
// File
|
||||
bool didMatch = it->text(0).contains(filter, Qt::CaseInsensitive);
|
||||
it->setHidden(!didMatch);
|
||||
anyMatched = anyMatched || didMatch;
|
||||
}
|
||||
|
||||
// A standard item (i.e. no file or directory item?)
|
||||
else
|
||||
{
|
||||
// Hide if there's any filter
|
||||
it->setHidden(!filter.isEmpty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,15 +242,20 @@ bool FileBrowser::filterItems( const QString & filter, QTreeWidgetItem * item )
|
||||
|
||||
void FileBrowser::reloadTree()
|
||||
{
|
||||
QList<QString> expandedDirs = m_fileBrowserTreeWidget->expandedDirs();
|
||||
const QString text = m_filterEdit->text();
|
||||
m_filterEdit->clear();
|
||||
if (m_filterEdit->text().isEmpty())
|
||||
{
|
||||
saveDirectoriesStates();
|
||||
}
|
||||
|
||||
m_fileBrowserTreeWidget->clear();
|
||||
|
||||
QStringList paths = m_directories.split('*');
|
||||
|
||||
if (m_showUserContent && !m_showUserContent->isChecked())
|
||||
{
|
||||
paths.removeAll(m_userDir);
|
||||
}
|
||||
|
||||
if (m_showFactoryContent && !m_showFactoryContent->isChecked())
|
||||
{
|
||||
paths.removeAll(m_factoryDir);
|
||||
@@ -222,9 +268,15 @@ void FileBrowser::reloadTree()
|
||||
addItems(path);
|
||||
}
|
||||
}
|
||||
expandItems(nullptr, expandedDirs);
|
||||
m_filterEdit->setText( text );
|
||||
filterItems( text );
|
||||
|
||||
if (m_filterEdit->text().isEmpty())
|
||||
{
|
||||
restoreDirectoriesStates();
|
||||
}
|
||||
else
|
||||
{
|
||||
filterAndExpandItems(m_filterEdit->text());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -240,12 +292,16 @@ void FileBrowser::expandItems(QTreeWidgetItem* item, QList<QString> expandedDirs
|
||||
{
|
||||
// Expanding is required when recursive to load in its contents, even if it's collapsed right afterward
|
||||
if (m_recurse) { d->setExpanded(true); }
|
||||
|
||||
d->setExpanded(expandedDirs.contains(d->fullName()));
|
||||
|
||||
if (m_recurse && it->childCount())
|
||||
{
|
||||
expandItems(it, expandedDirs);
|
||||
}
|
||||
}
|
||||
|
||||
it->setHidden(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,8 +472,6 @@ QList<QString> FileBrowserTreeWidget::expandedDirs( QTreeWidgetItem * item ) con
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void FileBrowserTreeWidget::keyPressEvent(QKeyEvent * ke )
|
||||
{
|
||||
// Shorter names for some commonly used properties of the event
|
||||
@@ -440,7 +494,7 @@ void FileBrowserTreeWidget::keyPressEvent(QKeyEvent * ke )
|
||||
if (file == nullptr) { return; }
|
||||
|
||||
// When moving to a new sound, preview it. Skip presets, they can play forever
|
||||
if (vertical && file->type() == FileItem::SampleFile)
|
||||
if (vertical && file->type() == FileItem::FileType::Sample)
|
||||
{
|
||||
previewFileItem(file);
|
||||
}
|
||||
@@ -535,7 +589,7 @@ void FileBrowserTreeWidget::contextMenuEvent(QContextMenuEvent * e )
|
||||
QList<QAction*> FileBrowserTreeWidget::getContextActions(FileItem* file, bool songEditor)
|
||||
{
|
||||
QList<QAction*> result = QList<QAction*>();
|
||||
const bool fileIsSample = file->type() == FileItem::SampleFile;
|
||||
const bool fileIsSample = file->type() == FileItem::FileType::Sample;
|
||||
|
||||
QString instrumentAction = fileIsSample ?
|
||||
tr("Send to new AudioFileProcessor instance") :
|
||||
@@ -603,7 +657,7 @@ void FileBrowserTreeWidget::previewFileItem(FileItem* file)
|
||||
|
||||
// In special case of sample-files we do not care about
|
||||
// handling() rather than directly creating a SamplePlayHandle
|
||||
if (file->type() == FileItem::SampleFile)
|
||||
if (file->type() == FileItem::FileType::Sample)
|
||||
{
|
||||
TextFloat * tf = TextFloat::displayMessage(
|
||||
tr("Loading sample"),
|
||||
@@ -621,15 +675,15 @@ void FileBrowserTreeWidget::previewFileItem(FileItem* file)
|
||||
ext == "gig" || ext == "pat")
|
||||
&& !getPluginFactory()->pluginSupportingExtension(ext).isNull())
|
||||
{
|
||||
const bool isPlugin = file->handling() == FileItem::LoadByPlugin;
|
||||
const bool isPlugin = file->handling() == FileItem::FileHandling::LoadByPlugin;
|
||||
newPPH = new PresetPreviewPlayHandle(fileName, isPlugin);
|
||||
}
|
||||
else if (file->type() != FileItem::VstPluginFile && file->isTrack())
|
||||
else if (file->type() != FileItem::FileType::VstPlugin && file->isTrack())
|
||||
{
|
||||
DataFile dataFile(fileName);
|
||||
if (dataFile.validate(ext))
|
||||
{
|
||||
const bool isPlugin = file->handling() == FileItem::LoadByPlugin;
|
||||
const bool isPlugin = file->handling() == FileItem::FileHandling::LoadByPlugin;
|
||||
newPPH = new PresetPreviewPlayHandle(fileName, isPlugin, &dataFile);
|
||||
}
|
||||
else
|
||||
@@ -681,34 +735,34 @@ void FileBrowserTreeWidget::mouseMoveEvent( QMouseEvent * me )
|
||||
{
|
||||
switch( f->type() )
|
||||
{
|
||||
case FileItem::PresetFile:
|
||||
new StringPairDrag( f->handling() == FileItem::LoadAsPreset ?
|
||||
case FileItem::FileType::Preset:
|
||||
new StringPairDrag( f->handling() == FileItem::FileHandling::LoadAsPreset ?
|
||||
"presetfile" : "pluginpresetfile",
|
||||
f->fullName(),
|
||||
embed::getIconPixmap( "preset_file" ), this );
|
||||
break;
|
||||
|
||||
case FileItem::SampleFile:
|
||||
case FileItem::FileType::Sample:
|
||||
new StringPairDrag( "samplefile", f->fullName(),
|
||||
embed::getIconPixmap( "sample_file" ), this );
|
||||
break;
|
||||
case FileItem::SoundFontFile:
|
||||
case FileItem::FileType::SoundFont:
|
||||
new StringPairDrag( "soundfontfile", f->fullName(),
|
||||
embed::getIconPixmap( "soundfont_file" ), this );
|
||||
break;
|
||||
case FileItem::PatchFile:
|
||||
case FileItem::FileType::Patch:
|
||||
new StringPairDrag( "patchfile", f->fullName(),
|
||||
embed::getIconPixmap( "sample_file" ), this );
|
||||
break;
|
||||
case FileItem::VstPluginFile:
|
||||
case FileItem::FileType::VstPlugin:
|
||||
new StringPairDrag( "vstpluginfile", f->fullName(),
|
||||
embed::getIconPixmap( "vst_plugin_file" ), this );
|
||||
break;
|
||||
case FileItem::MidiFile:
|
||||
case FileItem::FileType::Midi:
|
||||
new StringPairDrag( "importedproject", f->fullName(),
|
||||
embed::getIconPixmap( "midi_file" ), this );
|
||||
break;
|
||||
case FileItem::ProjectFile:
|
||||
case FileItem::FileType::Project:
|
||||
new StringPairDrag( "projectfile", f->fullName(),
|
||||
embed::getIconPixmap( "project_file" ), this );
|
||||
break;
|
||||
@@ -732,7 +786,7 @@ void FileBrowserTreeWidget::mouseReleaseEvent(QMouseEvent * me )
|
||||
if (m_previewPlayHandle == nullptr) { return; }
|
||||
|
||||
// Only sample previews may continue after mouse up. Is this a sample preview?
|
||||
bool isSample = m_previewPlayHandle->type() == PlayHandle::TypeSamplePlayHandle;
|
||||
bool isSample = m_previewPlayHandle->type() == PlayHandle::Type::SamplePlayHandle;
|
||||
// Even sample previews should only continue if the user wants them to. Do they?
|
||||
bool shouldContinue = ConfigManager::inst()->value("ui", "letpreviewsfinish").toInt();
|
||||
// If both are true the preview may continue, otherwise we stop it
|
||||
@@ -747,14 +801,14 @@ void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it)
|
||||
Engine::audioEngine()->requestChangeInModel();
|
||||
switch( f->handling() )
|
||||
{
|
||||
case FileItem::LoadAsProject:
|
||||
case FileItem::FileHandling::LoadAsProject:
|
||||
if( getGUI()->mainWindow()->mayChangeProject(true) )
|
||||
{
|
||||
Engine::getSong()->loadProject( f->fullName() );
|
||||
}
|
||||
break;
|
||||
|
||||
case FileItem::LoadByPlugin:
|
||||
case FileItem::FileHandling::LoadByPlugin:
|
||||
{
|
||||
const QString e = f->extension();
|
||||
Instrument * i = it->instrument();
|
||||
@@ -769,17 +823,17 @@ void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it)
|
||||
break;
|
||||
}
|
||||
|
||||
case FileItem::LoadAsPreset: {
|
||||
case FileItem::FileHandling::LoadAsPreset: {
|
||||
DataFile dataFile(f->fullName());
|
||||
it->replaceInstrument(dataFile);
|
||||
break;
|
||||
}
|
||||
case FileItem::ImportAsProject:
|
||||
case FileItem::FileHandling::ImportAsProject:
|
||||
ImportFilter::import( f->fullName(),
|
||||
Engine::getSong() );
|
||||
break;
|
||||
|
||||
case FileItem::NotSupported:
|
||||
case FileItem::FileHandling::NotSupported:
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -799,14 +853,14 @@ void FileBrowserTreeWidget::activateListItem(QTreeWidgetItem * item,
|
||||
return;
|
||||
}
|
||||
|
||||
if( f->handling() == FileItem::LoadAsProject ||
|
||||
f->handling() == FileItem::ImportAsProject )
|
||||
if( f->handling() == FileItem::FileHandling::LoadAsProject ||
|
||||
f->handling() == FileItem::FileHandling::ImportAsProject )
|
||||
{
|
||||
handleFile( f, nullptr );
|
||||
}
|
||||
else if( f->handling() != FileItem::NotSupported )
|
||||
else if( f->handling() != FileItem::FileHandling::NotSupported )
|
||||
{
|
||||
auto it = dynamic_cast<InstrumentTrack*>(Track::create(Track::InstrumentTrack, Engine::patternStore()));
|
||||
auto it = dynamic_cast<InstrumentTrack*>(Track::create(Track::Type::Instrument, Engine::patternStore()));
|
||||
handleFile( f, it );
|
||||
}
|
||||
}
|
||||
@@ -818,7 +872,7 @@ void FileBrowserTreeWidget::openInNewInstrumentTrack(TrackContainer* tc, FileIte
|
||||
{
|
||||
if(item->isTrack())
|
||||
{
|
||||
auto it = dynamic_cast<InstrumentTrack*>(Track::create(Track::InstrumentTrack, tc));
|
||||
auto it = dynamic_cast<InstrumentTrack*>(Track::create(Track::Type::Instrument, tc));
|
||||
handleFile(item, it);
|
||||
}
|
||||
}
|
||||
@@ -840,10 +894,10 @@ void FileBrowserTreeWidget::openInNewInstrumentTrack(FileItem* item, bool songEd
|
||||
bool FileBrowserTreeWidget::openInNewSampleTrack(FileItem* item)
|
||||
{
|
||||
// Can't add non-samples to a sample track
|
||||
if (item->type() != FileItem::SampleFile) { return false; }
|
||||
if (item->type() != FileItem::FileType::Sample) { return false; }
|
||||
|
||||
// Create a new sample track for this sample
|
||||
auto sampleTrack = static_cast<SampleTrack*>(Track::create(Track::SampleTrack, Engine::getSong()));
|
||||
auto sampleTrack = static_cast<SampleTrack*>(Track::create(Track::Type::Sample, Engine::getSong()));
|
||||
|
||||
// Add the sample clip to the track
|
||||
Engine::audioEngine()->requestChangeInModel();
|
||||
@@ -1113,26 +1167,26 @@ void FileItem::initPixmaps()
|
||||
|
||||
switch( m_type )
|
||||
{
|
||||
case ProjectFile:
|
||||
case FileType::Project:
|
||||
setIcon( 0, *s_projectFilePixmap );
|
||||
break;
|
||||
case PresetFile:
|
||||
case FileType::Preset:
|
||||
setIcon( 0, *s_presetFilePixmap );
|
||||
break;
|
||||
case SoundFontFile:
|
||||
case FileType::SoundFont:
|
||||
setIcon( 0, *s_soundfontFilePixmap );
|
||||
break;
|
||||
case VstPluginFile:
|
||||
case FileType::VstPlugin:
|
||||
setIcon( 0, *s_vstPluginFilePixmap );
|
||||
break;
|
||||
case SampleFile:
|
||||
case PatchFile: // TODO
|
||||
case FileType::Sample:
|
||||
case FileType::Patch: // TODO
|
||||
setIcon( 0, *s_sampleFilePixmap );
|
||||
break;
|
||||
case MidiFile:
|
||||
case FileType::Midi:
|
||||
setIcon( 0, *s_midiFilePixmap );
|
||||
break;
|
||||
case UnknownFile:
|
||||
case FileType::Unknown:
|
||||
default:
|
||||
setIcon( 0, *s_unknownFilePixmap );
|
||||
break;
|
||||
@@ -1144,36 +1198,36 @@ void FileItem::initPixmaps()
|
||||
|
||||
void FileItem::determineFileType()
|
||||
{
|
||||
m_handling = NotSupported;
|
||||
m_handling = FileHandling::NotSupported;
|
||||
|
||||
const QString ext = extension();
|
||||
if( ext == "mmp" || ext == "mpt" || ext == "mmpz" )
|
||||
{
|
||||
m_type = ProjectFile;
|
||||
m_handling = LoadAsProject;
|
||||
m_type = FileType::Project;
|
||||
m_handling = FileHandling::LoadAsProject;
|
||||
}
|
||||
else if( ext == "xpf" || ext == "xml" )
|
||||
{
|
||||
m_type = PresetFile;
|
||||
m_handling = LoadAsPreset;
|
||||
m_type = FileType::Preset;
|
||||
m_handling = FileHandling::LoadAsPreset;
|
||||
}
|
||||
else if( ext == "xiz" && ! getPluginFactory()->pluginSupportingExtension(ext).isNull() )
|
||||
{
|
||||
m_type = PresetFile;
|
||||
m_handling = LoadByPlugin;
|
||||
m_type = FileType::Preset;
|
||||
m_handling = FileHandling::LoadByPlugin;
|
||||
}
|
||||
else if( ext == "sf2" || ext == "sf3" )
|
||||
{
|
||||
m_type = SoundFontFile;
|
||||
m_type = FileType::SoundFont;
|
||||
}
|
||||
else if( ext == "pat" )
|
||||
{
|
||||
m_type = PatchFile;
|
||||
m_type = FileType::Patch;
|
||||
}
|
||||
else if( ext == "mid" || ext == "midi" || ext == "rmi" )
|
||||
{
|
||||
m_type = MidiFile;
|
||||
m_handling = ImportAsProject;
|
||||
m_type = FileType::Midi;
|
||||
m_handling = FileHandling::ImportAsProject;
|
||||
}
|
||||
else if( ext == "dll"
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
@@ -1181,28 +1235,28 @@ void FileItem::determineFileType()
|
||||
#endif
|
||||
)
|
||||
{
|
||||
m_type = VstPluginFile;
|
||||
m_handling = LoadByPlugin;
|
||||
m_type = FileType::VstPlugin;
|
||||
m_handling = FileHandling::LoadByPlugin;
|
||||
}
|
||||
else if ( ext == "lv2" )
|
||||
{
|
||||
m_type = PresetFile;
|
||||
m_handling = LoadByPlugin;
|
||||
m_type = FileType::Preset;
|
||||
m_handling = FileHandling::LoadByPlugin;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_type = UnknownFile;
|
||||
m_type = FileType::Unknown;
|
||||
}
|
||||
|
||||
if( m_handling == NotSupported &&
|
||||
if( m_handling == FileHandling::NotSupported &&
|
||||
!ext.isEmpty() && ! getPluginFactory()->pluginSupportingExtension(ext).isNull() )
|
||||
{
|
||||
m_handling = LoadByPlugin;
|
||||
m_handling = FileHandling::LoadByPlugin;
|
||||
// classify as sample if not classified by anything yet but can
|
||||
// be handled by a certain plugin
|
||||
if( m_type == UnknownFile )
|
||||
if( m_type == FileType::Unknown )
|
||||
{
|
||||
m_type = SampleFile;
|
||||
m_type = FileType::Sample;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,9 +60,9 @@ LadspaControlView::LadspaControlView( QWidget * _parent,
|
||||
|
||||
switch( m_ctl->port()->data_type )
|
||||
{
|
||||
case TOGGLED:
|
||||
case BufferDataType::Toggled:
|
||||
{
|
||||
auto toggle = new LedCheckBox(m_ctl->port()->name, this, QString(), LedCheckBox::Green);
|
||||
auto toggle = new LedCheckBox(m_ctl->port()->name, this, QString(), LedCheckBox::LedColor::Green);
|
||||
toggle->setModel( m_ctl->toggledModel() );
|
||||
layout->addWidget( toggle );
|
||||
if( link != nullptr )
|
||||
@@ -78,14 +78,14 @@ LadspaControlView::LadspaControlView( QWidget * _parent,
|
||||
break;
|
||||
}
|
||||
|
||||
case INTEGER:
|
||||
case ENUM:
|
||||
case FLOATING:
|
||||
knb = new Knob( knobBright_26, this, m_ctl->port()->name );
|
||||
case BufferDataType::Integer:
|
||||
case BufferDataType::Enum:
|
||||
case BufferDataType::Floating:
|
||||
knb = new Knob( KnobType::Bright26, this, m_ctl->port()->name );
|
||||
break;
|
||||
|
||||
case TIME:
|
||||
knb = new TempoSyncKnob( knobBright_26, this, m_ctl->port()->name );
|
||||
case BufferDataType::Time:
|
||||
knb = new TempoSyncKnob( KnobType::Bright26, this, m_ctl->port()->name );
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -94,7 +94,7 @@ LadspaControlView::LadspaControlView( QWidget * _parent,
|
||||
|
||||
if( knb != nullptr )
|
||||
{
|
||||
if( m_ctl->port()->data_type != TIME )
|
||||
if( m_ctl->port()->data_type != BufferDataType::Time )
|
||||
{
|
||||
knb->setModel( m_ctl->knobModel() );
|
||||
}
|
||||
|
||||
@@ -61,22 +61,22 @@ LfoControllerDialog::LfoControllerDialog( Controller * _model, QWidget * _parent
|
||||
setWindowIcon( embed::getIconPixmap( "controller" ) );
|
||||
setFixedSize( 240, 58 );
|
||||
|
||||
m_baseKnob = new Knob( knobBright_26, this );
|
||||
m_baseKnob = new Knob( KnobType::Bright26, this );
|
||||
m_baseKnob->setLabel( tr( "BASE" ) );
|
||||
m_baseKnob->move( CD_LFO_BASE_CD_KNOB_X, CD_LFO_CD_KNOB_Y );
|
||||
m_baseKnob->setHintText( tr( "Base:" ), "" );
|
||||
|
||||
m_speedKnob = new TempoSyncKnob( knobBright_26, this );
|
||||
m_speedKnob = new TempoSyncKnob( KnobType::Bright26, this );
|
||||
m_speedKnob->setLabel( tr( "FREQ" ) );
|
||||
m_speedKnob->move( CD_LFO_SPEED_CD_KNOB_X, CD_LFO_CD_KNOB_Y );
|
||||
m_speedKnob->setHintText( tr( "LFO frequency:" ), "" );
|
||||
|
||||
m_amountKnob = new Knob( knobBright_26, this );
|
||||
m_amountKnob = new Knob( KnobType::Bright26, this );
|
||||
m_amountKnob->setLabel( tr( "AMNT" ) );
|
||||
m_amountKnob->move( CD_LFO_AMOUNT_CD_KNOB_X, CD_LFO_CD_KNOB_Y );
|
||||
m_amountKnob->setHintText( tr( "Modulation amount:" ), "" );
|
||||
|
||||
m_phaseKnob = new Knob( knobBright_26, this );
|
||||
m_phaseKnob = new Knob( KnobType::Bright26, this );
|
||||
m_phaseKnob->setLabel( tr( "PHS" ) );
|
||||
m_phaseKnob->move( CD_LFO_PHASE_CD_KNOB_X, CD_LFO_CD_KNOB_Y );
|
||||
m_phaseKnob->setHintText( tr( "Phase offset:" ) , "" + tr( " degrees" ) );
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "GuiApplication.h"
|
||||
#include "embed.h"
|
||||
#include "gui_templates.h"
|
||||
#include "lmms_math.h"
|
||||
#include "Lv2ControlBase.h"
|
||||
#include "Lv2Manager.h"
|
||||
#include "Lv2Proc.h"
|
||||
@@ -51,13 +52,13 @@ namespace lmms::gui
|
||||
{
|
||||
|
||||
|
||||
Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) :
|
||||
LinkedModelGroupView (parent, ctrlBase, colNum)
|
||||
Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* proc, int colNum) :
|
||||
LinkedModelGroupView (parent, proc, colNum)
|
||||
{
|
||||
class SetupWidget : public Lv2Ports::ConstVisitor
|
||||
class SetupTheWidget : public Lv2Ports::ConstVisitor
|
||||
{
|
||||
public:
|
||||
QWidget* m_par; // input
|
||||
QWidget* m_parent; // input
|
||||
const LilvNode* m_commentUri; // input
|
||||
Control* m_control = nullptr; // output
|
||||
void visit(const Lv2Ports::Control& port) override
|
||||
@@ -69,20 +70,22 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) :
|
||||
switch (port.m_vis)
|
||||
{
|
||||
case PortVis::Generic:
|
||||
m_control = new KnobControl(m_par);
|
||||
m_control = new KnobControl(m_parent);
|
||||
break;
|
||||
case PortVis::Integer:
|
||||
{
|
||||
sample_rate_t sr = Engine::audioEngine()->processingSampleRate();
|
||||
m_control = new LcdControl((port.max(sr) <= 9.0f) ? 1 : 2,
|
||||
m_par);
|
||||
auto pMin = port.min(sr);
|
||||
auto pMax = port.max(sr);
|
||||
int numDigits = std::max(numDigitsAsInt(pMin), numDigitsAsInt(pMax));
|
||||
m_control = new LcdControl(numDigits, m_parent);
|
||||
break;
|
||||
}
|
||||
case PortVis::Enumeration:
|
||||
m_control = new ComboControl(m_par);
|
||||
m_control = new ComboControl(m_parent);
|
||||
break;
|
||||
case PortVis::Toggled:
|
||||
m_control = new CheckControl(m_par);
|
||||
m_control = new CheckControl(m_parent);
|
||||
break;
|
||||
}
|
||||
m_control->setText(port.name());
|
||||
@@ -100,14 +103,14 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) :
|
||||
};
|
||||
|
||||
AutoLilvNode commentUri = uri(LILV_NS_RDFS "comment");
|
||||
ctrlBase->foreach_port(
|
||||
proc->foreach_port(
|
||||
[this, &commentUri](const Lv2Ports::PortBase* port)
|
||||
{
|
||||
if(!lilv_port_has_property(port->m_plugin, port->m_port,
|
||||
uri(LV2_PORT_PROPS__notOnGUI).get()))
|
||||
{
|
||||
SetupWidget setup;
|
||||
setup.m_par = this;
|
||||
SetupTheWidget setup;
|
||||
setup.m_parent = this;
|
||||
setup.m_commentUri = commentUri.get();
|
||||
port->accept(setup);
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ MainWindow::MainWindow() :
|
||||
m_autoSaveTimer( this ),
|
||||
m_viewMenu( nullptr ),
|
||||
m_metronomeToggle( 0 ),
|
||||
m_session( Normal )
|
||||
m_session( SessionState::Normal )
|
||||
{
|
||||
setAttribute( Qt::WA_DeleteOnClose );
|
||||
|
||||
@@ -363,10 +363,12 @@ void MainWindow::finalize()
|
||||
}
|
||||
|
||||
edit_menu->addSeparator();
|
||||
edit_menu->addAction( embed::getIconPixmap( "setup_general" ),
|
||||
tr( "Settings" ),
|
||||
this, SLOT(showSettingsDialog()));
|
||||
connect( edit_menu, SIGNAL(aboutToShow()), this, SLOT(updateUndoRedoButtons()));
|
||||
edit_menu->addAction(embed::getIconPixmap("microtuner"), tr("Scales and keymaps"),
|
||||
this, SLOT(toggleMicrotunerWin()));
|
||||
edit_menu->addAction(embed::getIconPixmap("setup_general"), tr("Settings"),
|
||||
this, SLOT(showSettingsDialog()));
|
||||
|
||||
connect(edit_menu, SIGNAL(aboutToShow()), this, SLOT(updateUndoRedoButtons()));
|
||||
|
||||
m_viewMenu = new QMenu( this );
|
||||
menuBar()->addMenu( m_viewMenu )->setText( tr( "&View" ) );
|
||||
@@ -377,7 +379,7 @@ void MainWindow::finalize()
|
||||
|
||||
|
||||
m_toolsMenu = new QMenu( this );
|
||||
for( const Plugin::Descriptor* desc : getPluginFactory()->descriptors(Plugin::Tool) )
|
||||
for( const Plugin::Descriptor* desc : getPluginFactory()->descriptors(Plugin::Type::Tool) )
|
||||
{
|
||||
m_toolsMenu->addAction( desc->logo->pixmap(), desc->displayName );
|
||||
m_tools.push_back( ToolPlugin::instantiate( desc->name, /*this*/nullptr )
|
||||
@@ -485,10 +487,6 @@ void MainWindow::finalize()
|
||||
tr("Show/hide project notes") + " (Ctrl+7)", this, SLOT(toggleProjectNotesWin()), m_toolBar);
|
||||
project_notes_window->setShortcut( Qt::CTRL + Qt::Key_7 );
|
||||
|
||||
auto microtuner_window = new ToolButton(embed::getIconPixmap("microtuner"),
|
||||
tr("Microtuner configuration") + " (Ctrl+8)", this, SLOT(toggleMicrotunerWin()), m_toolBar);
|
||||
microtuner_window->setShortcut( Qt::CTRL + Qt::Key_8 );
|
||||
|
||||
m_toolBarLayout->addWidget( song_editor_window, 1, 1 );
|
||||
m_toolBarLayout->addWidget( pattern_editor_window, 1, 2 );
|
||||
m_toolBarLayout->addWidget( piano_roll_window, 1, 3 );
|
||||
@@ -496,7 +494,6 @@ void MainWindow::finalize()
|
||||
m_toolBarLayout->addWidget( mixer_window, 1, 5 );
|
||||
m_toolBarLayout->addWidget( controllers_window, 1, 6 );
|
||||
m_toolBarLayout->addWidget( project_notes_window, 1, 7 );
|
||||
m_toolBarLayout->addWidget( microtuner_window, 1, 8 );
|
||||
m_toolBarLayout->setColumnStretch( 100, 1 );
|
||||
|
||||
// setup-dialog opened before?
|
||||
@@ -514,7 +511,7 @@ void MainWindow::finalize()
|
||||
ConfigManager::inst()->value( "audioengine", "audiodev" ) ) )
|
||||
{
|
||||
// if so, offer the audio settings section of the setup dialog
|
||||
SetupDialog sd( SetupDialog::AudioSettings );
|
||||
SetupDialog sd( SetupDialog::ConfigTab::AudioSettings );
|
||||
sd.exec();
|
||||
}
|
||||
|
||||
@@ -600,7 +597,7 @@ void MainWindow::resetWindowTitle()
|
||||
title += '*';
|
||||
}
|
||||
|
||||
if( getSession() == Recover )
|
||||
if( getSession() == SessionState::Recover )
|
||||
{
|
||||
title += " - " + tr( "Recover session. Please save your work!" );
|
||||
}
|
||||
@@ -618,7 +615,7 @@ bool MainWindow::mayChangeProject(bool stopPlayback)
|
||||
Engine::getSong()->stop();
|
||||
}
|
||||
|
||||
if( !Engine::getSong()->isModified() && getSession() != Recover )
|
||||
if( !Engine::getSong()->isModified() && getSession() != SessionState::Recover )
|
||||
{
|
||||
return( true );
|
||||
}
|
||||
@@ -635,9 +632,9 @@ bool MainWindow::mayChangeProject(bool stopPlayback)
|
||||
"last saving. Do you want to save it "
|
||||
"now?" );
|
||||
|
||||
QMessageBox mb( ( getSession() == Recover ?
|
||||
QMessageBox mb( ( getSession() == SessionState::Recover ?
|
||||
messageTitleRecovered : messageTitleUnsaved ),
|
||||
( getSession() == Recover ?
|
||||
( getSession() == SessionState::Recover ?
|
||||
messageRecovered : messageUnsaved ),
|
||||
QMessageBox::Question,
|
||||
QMessageBox::Save,
|
||||
@@ -652,7 +649,7 @@ bool MainWindow::mayChangeProject(bool stopPlayback)
|
||||
}
|
||||
else if( answer == QMessageBox::Discard )
|
||||
{
|
||||
if( getSession() == Recover )
|
||||
if( getSession() == SessionState::Recover )
|
||||
{
|
||||
sessionCleanup();
|
||||
}
|
||||
@@ -795,7 +792,7 @@ bool MainWindow::saveProject()
|
||||
}
|
||||
else if( this->guiSaveProject() )
|
||||
{
|
||||
if( getSession() == Recover )
|
||||
if( getSession() == SessionState::Recover )
|
||||
{
|
||||
sessionCleanup();
|
||||
}
|
||||
@@ -850,7 +847,7 @@ bool MainWindow::saveProjectAs()
|
||||
}
|
||||
if( this->guiSaveProjectAs( fname ) )
|
||||
{
|
||||
if( getSession() == Recover )
|
||||
if( getSession() == SessionState::Recover )
|
||||
{
|
||||
sessionCleanup();
|
||||
}
|
||||
@@ -1100,10 +1097,6 @@ void MainWindow::updateViewMenu()
|
||||
tr( "Project Notes" ) + "\tCtrl+7",
|
||||
this, SLOT(toggleProjectNotesWin())
|
||||
);
|
||||
m_viewMenu->addAction(embed::getIconPixmap( "microtuner" ),
|
||||
tr( "Microtuner" ) + "\tCtrl+8",
|
||||
this, SLOT(toggleMicrotunerWin())
|
||||
);
|
||||
|
||||
m_viewMenu->addSeparator();
|
||||
|
||||
@@ -1214,19 +1207,19 @@ void MainWindow::updatePlayPauseIcons()
|
||||
{
|
||||
switch( Engine::getSong()->playMode() )
|
||||
{
|
||||
case Song::Mode_PlaySong:
|
||||
case Song::PlayMode::Song:
|
||||
getGUI()->songEditor()->setPauseIcon( true );
|
||||
break;
|
||||
|
||||
case Song::Mode_PlayAutomationClip:
|
||||
case Song::PlayMode::AutomationClip:
|
||||
getGUI()->automationEditor()->setPauseIcon( true );
|
||||
break;
|
||||
|
||||
case Song::Mode_PlayPattern:
|
||||
case Song::PlayMode::Pattern:
|
||||
getGUI()->patternEditor()->setPauseIcon( true );
|
||||
break;
|
||||
|
||||
case Song::Mode_PlayMidiClip:
|
||||
case Song::PlayMode::MidiClip:
|
||||
getGUI()->pianoRoll()->setPauseIcon( true );
|
||||
break;
|
||||
|
||||
@@ -1288,7 +1281,7 @@ void MainWindow::sessionCleanup()
|
||||
{
|
||||
// delete recover session files
|
||||
QFile::remove( ConfigManager::inst()->recoveryFile() );
|
||||
setSession( Normal );
|
||||
setSession( SessionState::Normal );
|
||||
}
|
||||
|
||||
|
||||
@@ -1480,7 +1473,7 @@ void MainWindow::exportProject(bool multiExport)
|
||||
efd.setFileMode( FileDialog::AnyFile );
|
||||
int idx = 0;
|
||||
QStringList types;
|
||||
while( ProjectRenderer::fileEncodeDevices[idx].m_fileFormat != ProjectRenderer::NumFileFormats)
|
||||
while( ProjectRenderer::fileEncodeDevices[idx].m_fileFormat != ProjectRenderer::ExportFileFormat::Count)
|
||||
{
|
||||
if(ProjectRenderer::fileEncodeDevices[idx].isAvailable()) {
|
||||
types << tr(ProjectRenderer::fileEncodeDevices[idx].m_description);
|
||||
@@ -1625,17 +1618,17 @@ void MainWindow::onSongStopped()
|
||||
SongEditorWindow* songEditor = getGUI()->songEditor();
|
||||
switch( tl->behaviourAtStop() )
|
||||
{
|
||||
case TimeLineWidget::BackToZero:
|
||||
if( songEditor && ( tl->autoScroll() == TimeLineWidget::AutoScrollEnabled ) )
|
||||
case TimeLineWidget::BehaviourAtStopState::BackToZero:
|
||||
if( songEditor && ( tl->autoScroll() == TimeLineWidget::AutoScrollState::Enabled ) )
|
||||
{
|
||||
songEditor->m_editor->updatePosition(0);
|
||||
}
|
||||
break;
|
||||
|
||||
case TimeLineWidget::BackToStart:
|
||||
case TimeLineWidget::BehaviourAtStopState::BackToStart:
|
||||
if( tl->savedPos() >= 0 )
|
||||
{
|
||||
if(songEditor && ( tl->autoScroll() == TimeLineWidget::AutoScrollEnabled ) )
|
||||
if(songEditor && ( tl->autoScroll() == TimeLineWidget::AutoScrollState::Enabled ) )
|
||||
{
|
||||
songEditor->m_editor->updatePosition( TimePos(tl->savedPos().getTicks() ) );
|
||||
}
|
||||
@@ -1643,7 +1636,7 @@ void MainWindow::onSongStopped()
|
||||
}
|
||||
break;
|
||||
|
||||
case TimeLineWidget::KeepStopPosition:
|
||||
case TimeLineWidget::BehaviourAtStopState::KeepStopPosition:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,16 +56,26 @@ namespace lmms::gui
|
||||
|
||||
MicrotunerConfig::MicrotunerConfig() :
|
||||
QWidget(),
|
||||
m_scaleComboModel(nullptr, tr("Selected scale")),
|
||||
m_keymapComboModel(nullptr, tr("Selected keymap")),
|
||||
m_scaleComboModel(nullptr, tr("Selected scale slot")),
|
||||
m_keymapComboModel(nullptr, tr("Selected keymap slot")),
|
||||
m_firstKeyModel(0, 0, NumKeys - 1, nullptr, tr("First key")),
|
||||
m_lastKeyModel(NumKeys - 1, 0, NumKeys - 1, nullptr, tr("Last key")),
|
||||
m_middleKeyModel(DefaultMiddleKey, 0, NumKeys - 1, nullptr, tr("Middle key")),
|
||||
m_baseKeyModel(DefaultBaseKey, 0, NumKeys - 1, nullptr, tr("Base key")),
|
||||
m_baseFreqModel(DefaultBaseFreq, 0.1f, 9999.999f, 0.001f, nullptr, tr("Base note frequency"))
|
||||
{
|
||||
#if QT_VERSION < 0x50C00
|
||||
// Workaround for a bug in Qt versions below 5.12,
|
||||
// where argument-dependent-lookup fails for QFlags operators
|
||||
// declared inside a namepsace.
|
||||
// This affects the Q_DECLARE_OPERATORS_FOR_FLAGS macro in Instrument.h
|
||||
// See also: https://codereview.qt-project.org/c/qt/qtbase/+/225348
|
||||
|
||||
using ::operator|;
|
||||
#endif
|
||||
|
||||
setWindowIcon(embed::getIconPixmap("microtuner"));
|
||||
setWindowTitle(tr("Microtuner"));
|
||||
setWindowTitle(tr("Microtuner Configuration"));
|
||||
|
||||
// Organize into 2 main columns: scales and keymaps
|
||||
auto microtunerLayout = new QGridLayout();
|
||||
@@ -74,7 +84,7 @@ MicrotunerConfig::MicrotunerConfig() :
|
||||
// ----------------------------------
|
||||
// Scale sub-column
|
||||
//
|
||||
auto scaleLabel = new QLabel(tr("Scale:"));
|
||||
auto scaleLabel = new QLabel(tr("Scale slot to edit:"));
|
||||
microtunerLayout->addWidget(scaleLabel, 0, 0, 1, 2, Qt::AlignBottom);
|
||||
|
||||
for (unsigned int i = 0; i < MaxScaleCount; i++)
|
||||
@@ -92,6 +102,8 @@ MicrotunerConfig::MicrotunerConfig() :
|
||||
|
||||
auto loadScaleButton = new QPushButton(tr("Load"));
|
||||
auto saveScaleButton = new QPushButton(tr("Save"));
|
||||
loadScaleButton->setToolTip(tr("Load scale definition from a file."));
|
||||
saveScaleButton->setToolTip(tr("Save scale definition to a file."));
|
||||
microtunerLayout->addWidget(loadScaleButton, 3, 0, 1, 1);
|
||||
microtunerLayout->addWidget(saveScaleButton, 3, 1, 1, 1);
|
||||
connect(loadScaleButton, &QPushButton::clicked, [=] {loadScaleFromFile();});
|
||||
@@ -102,14 +114,15 @@ MicrotunerConfig::MicrotunerConfig() :
|
||||
m_scaleTextEdit->setToolTip(tr("Enter intervals on separate lines. Numbers containing a decimal point are treated as cents.\nOther inputs are treated as integer ratios and must be in the form of \'a/b\' or \'a\'.\nUnity (0.0 cents or ratio 1/1) is always present as a hidden first value; do not enter it manually."));
|
||||
microtunerLayout->addWidget(m_scaleTextEdit, 4, 0, 2, 2);
|
||||
|
||||
auto applyScaleButton = new QPushButton(tr("Apply scale"));
|
||||
auto applyScaleButton = new QPushButton(tr("Apply scale changes"));
|
||||
applyScaleButton->setToolTip(tr("Verify and apply changes made to the selected scale. To use the scale, select it in the settings of a supported instrument."));
|
||||
microtunerLayout->addWidget(applyScaleButton, 6, 0, 1, 2);
|
||||
connect(applyScaleButton, &QPushButton::clicked, [=] {applyScale();});
|
||||
|
||||
// ----------------------------------
|
||||
// Mapping sub-column
|
||||
//
|
||||
auto keymapLabel = new QLabel(tr("Keymap:"));
|
||||
auto keymapLabel = new QLabel(tr("Keymap slot to edit:"));
|
||||
microtunerLayout->addWidget(keymapLabel, 0, 2, 1, 2, Qt::AlignBottom);
|
||||
|
||||
for (unsigned int i = 0; i < MaxKeymapCount; i++)
|
||||
@@ -127,6 +140,8 @@ MicrotunerConfig::MicrotunerConfig() :
|
||||
|
||||
auto loadKeymapButton = new QPushButton(tr("Load"));
|
||||
auto saveKeymapButton = new QPushButton(tr("Save"));
|
||||
loadKeymapButton->setToolTip(tr("Load key mapping definition from a file."));
|
||||
saveKeymapButton->setToolTip(tr("Save key mapping definition to a file."));
|
||||
microtunerLayout->addWidget(loadKeymapButton, 3, 2, 1, 1);
|
||||
microtunerLayout->addWidget(saveKeymapButton, 3, 3, 1, 1);
|
||||
connect(loadKeymapButton, &QPushButton::clicked, [=] {loadKeymapFromFile();});
|
||||
@@ -171,7 +186,8 @@ MicrotunerConfig::MicrotunerConfig() :
|
||||
baseFreqSpin->setToolTip(tr("Base note frequency"));
|
||||
keymapRangeLayout->addWidget(baseFreqSpin, 1, 1, 1, 2);
|
||||
|
||||
auto applyKeymapButton = new QPushButton(tr("Apply keymap"));
|
||||
auto applyKeymapButton = new QPushButton(tr("Apply keymap changes"));
|
||||
applyKeymapButton->setToolTip(tr("Verify and apply changes made to the selected key mapping. To use the mapping, select it in the settings of a supported instrument."));
|
||||
microtunerLayout->addWidget(applyKeymapButton, 6, 2, 1, 2);
|
||||
connect(applyKeymapButton, &QPushButton::clicked, [=] {applyKeymap();});
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ MidiCCRackView::MidiCCRackView(InstrumentTrack * track) :
|
||||
// Adds the controller knobs
|
||||
for (int i = 0; i < MidiControllerCount; ++i)
|
||||
{
|
||||
m_controllerKnob[i] = new Knob(knobBright_26);
|
||||
m_controllerKnob[i] = new Knob(KnobType::Bright26);
|
||||
m_controllerKnob[i]->setLabel(tr("CC %1").arg(i));
|
||||
knobsAreaLayout->addWidget(m_controllerKnob[i], i/4, i%4);
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ MixerLine::MixerLine( QWidget * _parent, MixerView * _mv, int _channelIndex ) :
|
||||
setCursor( QCursor( embed::getIconPixmap( "hand" ), 3, 3 ) );
|
||||
|
||||
// mixer sends knob
|
||||
m_sendKnob = new Knob( knobBright_26, this, tr( "Channel send amount" ) );
|
||||
m_sendKnob = new Knob( KnobType::Bright26, this, tr( "Channel send amount" ) );
|
||||
m_sendKnob->move( 3, 22 );
|
||||
m_sendKnob->setVisible( false );
|
||||
|
||||
|
||||
@@ -57,6 +57,16 @@ MixerView::MixerView() :
|
||||
ModelView( nullptr, this ),
|
||||
SerializingObjectHook()
|
||||
{
|
||||
#if QT_VERSION < 0x50C00
|
||||
// Workaround for a bug in Qt versions below 5.12,
|
||||
// where argument-dependent-lookup fails for QFlags operators
|
||||
// declared inside a namepsace.
|
||||
// This affects the Q_DECLARE_OPERATORS_FOR_FLAGS macro in Instrument.h
|
||||
// See also: https://codereview.qt-project.org/c/qt/qtbase/+/225348
|
||||
|
||||
using ::operator|;
|
||||
#endif
|
||||
|
||||
Mixer * m = Engine::mixer();
|
||||
m->setHook( this );
|
||||
|
||||
@@ -238,20 +248,20 @@ void MixerView::refreshDisplay()
|
||||
// update the and max. channel number for every instrument
|
||||
void MixerView::updateMaxChannelSelector()
|
||||
{
|
||||
TrackContainer::TrackList songTracks = Engine::getSong()->tracks();
|
||||
TrackContainer::TrackList patternStoreTracks = Engine::patternStore()->tracks();
|
||||
const TrackContainer::TrackList& songTracks = Engine::getSong()->tracks();
|
||||
const TrackContainer::TrackList& patternStoreTracks = Engine::patternStore()->tracks();
|
||||
|
||||
for (const auto& trackList : {songTracks, patternStoreTracks})
|
||||
{
|
||||
for (const auto& track : trackList)
|
||||
{
|
||||
if (track->type() == Track::InstrumentTrack)
|
||||
if (track->type() == Track::Type::Instrument)
|
||||
{
|
||||
auto inst = (InstrumentTrack*)track;
|
||||
inst->mixerChannelModel()->setRange(0,
|
||||
m_mixerChannelViews.size()-1,1);
|
||||
}
|
||||
else if (track->type() == Track::SampleTrack)
|
||||
else if (track->type() == Track::Type::Sample)
|
||||
{
|
||||
auto strk = (SampleTrack*)track;
|
||||
strk->mixerChannelModel()->setRange(0,
|
||||
@@ -454,7 +464,7 @@ bool MixerView::confirmRemoval(int index)
|
||||
QString messageTitleRemoveTrack = tr("Confirm removal");
|
||||
QString askAgainText = tr("Don't ask again");
|
||||
auto askAgainCheckBox = new QCheckBox(askAgainText, nullptr);
|
||||
connect(askAgainCheckBox, &QCheckBox::stateChanged, [this](int state) {
|
||||
connect(askAgainCheckBox, &QCheckBox::stateChanged, [](int state) {
|
||||
// Invert button state, if it's checked we *shouldn't* ask again
|
||||
ConfigManager::inst()->setValue("ui", "mixerchanneldeletionwarning", state ? "0" : "1");
|
||||
});
|
||||
|
||||
@@ -161,7 +161,7 @@ void PluginBrowser::addPlugins()
|
||||
m_descTree->clear();
|
||||
|
||||
// Fetch and sort all instrument plugin descriptors
|
||||
auto descs = getPluginFactory()->descriptors(Plugin::Instrument);
|
||||
auto descs = getPluginFactory()->descriptors(Plugin::Type::Instrument);
|
||||
std::sort(descs.begin(), descs.end(),
|
||||
[](auto d1, auto d2)
|
||||
{
|
||||
@@ -305,7 +305,7 @@ void PluginDescWidget::contextMenuEvent(QContextMenuEvent* e)
|
||||
void PluginDescWidget::openInNewInstrumentTrack(QString value)
|
||||
{
|
||||
TrackContainer* tc = Engine::getSong();
|
||||
auto it = dynamic_cast<InstrumentTrack*>(Track::create(Track::InstrumentTrack, tc));
|
||||
auto it = dynamic_cast<InstrumentTrack*>(Track::create(Track::Type::Instrument, tc));
|
||||
auto ilt = new InstrumentLoaderThread(this, it, value);
|
||||
ilt->start();
|
||||
}
|
||||
|
||||
@@ -54,6 +54,16 @@ SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) :
|
||||
m_track(tv->model()),
|
||||
m_stv(tv)
|
||||
{
|
||||
#if QT_VERSION < 0x50C00
|
||||
// Workaround for a bug in Qt versions below 5.12,
|
||||
// where argument-dependent-lookup fails for QFlags operators
|
||||
// declared inside a namepsace.
|
||||
// This affects the Q_DECLARE_OPERATORS_FOR_FLAGS macro in Instrument.h
|
||||
// See also: https://codereview.qt-project.org/c/qt/qtbase/+/225348
|
||||
|
||||
using ::operator|;
|
||||
#endif
|
||||
|
||||
// init own layout + widgets
|
||||
setFocusPolicy(Qt::StrongFocus);
|
||||
auto vlayout = new QVBoxLayout(this);
|
||||
@@ -74,7 +84,6 @@ SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) :
|
||||
|
||||
// setup line edit for changing sample track name
|
||||
m_nameLineEdit = new QLineEdit;
|
||||
m_nameLineEdit->setFont(pointSize<9>(m_nameLineEdit->font()));
|
||||
connect(m_nameLineEdit, SIGNAL(textChanged(const QString&)),
|
||||
this, SLOT(textChanged(const QString&)));
|
||||
|
||||
@@ -94,7 +103,7 @@ SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) :
|
||||
Qt::Alignment widgetAlignment = Qt::AlignHCenter | Qt::AlignCenter;
|
||||
|
||||
// set up volume knob
|
||||
m_volumeKnob = new Knob(knobBright_26, nullptr, tr("Sample volume"));
|
||||
m_volumeKnob = new Knob(KnobType::Bright26, nullptr, tr("Sample volume"));
|
||||
m_volumeKnob->setVolumeKnob(true);
|
||||
m_volumeKnob->setHintText(tr("Volume:"), "%");
|
||||
|
||||
@@ -108,7 +117,7 @@ SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) :
|
||||
|
||||
|
||||
// set up panning knob
|
||||
m_panningKnob = new Knob(knobBright_26, nullptr, tr("Panning"));
|
||||
m_panningKnob = new Knob(KnobType::Bright26, nullptr, tr("Panning"));
|
||||
m_panningKnob->setHintText(tr("Panning:"), "");
|
||||
|
||||
basicControlsLayout->addWidget(m_panningKnob, 0, 1);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user