Merge remote-tracking branch 'upstream/master' into refactor-samplebuffer
This commit is contained in:
@@ -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 )
|
||||
{
|
||||
@@ -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
|
||||
@@ -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(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;
|
||||
}
|
||||
|
||||
@@ -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 = std::clamp<int>((newCpuLoad * 0.1f + m_cpuLoad * 0.9f), 0, 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() )
|
||||
{
|
||||
|
||||
@@ -1106,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.
|
||||
@@ -1123,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
|
||||
@@ -1159,7 +1151,6 @@ void AutomationClip::generateTangents(timeMap::iterator it, int numToGenerate)
|
||||
it.value().setOutTangent(outTangent);
|
||||
}
|
||||
}
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
|
||||
#include <QDomElement>
|
||||
#include <cassert>
|
||||
|
||||
#include "EffectChain.h"
|
||||
#include "Effect.h"
|
||||
@@ -162,6 +163,7 @@ void EffectChain::moveDown( Effect * _effect )
|
||||
if (_effect != m_effects.back())
|
||||
{
|
||||
auto it = std::find(m_effects.begin(), m_effects.end(), _effect);
|
||||
assert(it != m_effects.end());
|
||||
std::swap(*std::next(it), *it);
|
||||
}
|
||||
}
|
||||
@@ -174,6 +176,7 @@ void EffectChain::moveUp( Effect * _effect )
|
||||
if (_effect != m_effects.front())
|
||||
{
|
||||
auto it = std::find(m_effects.begin(), m_effects.end(), _effect);
|
||||
assert(it != m_effects.end());
|
||||
std::swap(*std::prev(it), *it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,8 +394,8 @@ 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})
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
@@ -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);
|
||||
@@ -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)
|
||||
@@ -215,7 +215,7 @@ 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::Type::Pattern)
|
||||
@@ -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);
|
||||
|
||||
@@ -97,7 +97,7 @@ 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)
|
||||
@@ -112,7 +112,7 @@ void RenderManager::renderTracks()
|
||||
}
|
||||
}
|
||||
|
||||
const TrackContainer::TrackList t2 = Engine::patternStore()->tracks();
|
||||
const TrackContainer::TrackList& t2 = Engine::patternStore()->tracks();
|
||||
for (const auto& tk : t2)
|
||||
{
|
||||
Track::Type type = tk->type();
|
||||
|
||||
@@ -383,7 +383,7 @@ 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)
|
||||
|
||||
@@ -62,7 +62,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
|
||||
|
||||
@@ -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" ) );
|
||||
@@ -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?
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -56,8 +56,8 @@ 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")),
|
||||
@@ -75,7 +75,7 @@ MicrotunerConfig::MicrotunerConfig() :
|
||||
#endif
|
||||
|
||||
setWindowIcon(embed::getIconPixmap("microtuner"));
|
||||
setWindowTitle(tr("Microtuner"));
|
||||
setWindowTitle(tr("Microtuner Configuration"));
|
||||
|
||||
// Organize into 2 main columns: scales and keymaps
|
||||
auto microtunerLayout = new QGridLayout();
|
||||
@@ -84,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++)
|
||||
@@ -102,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();});
|
||||
@@ -112,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++)
|
||||
@@ -137,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();});
|
||||
@@ -181,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();});
|
||||
|
||||
|
||||
@@ -248,8 +248,8 @@ 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})
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "ClipView.h"
|
||||
|
||||
#include <set>
|
||||
#include <cassert>
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMouseEvent>
|
||||
@@ -545,6 +546,7 @@ DataFile ClipView::createClipDataFiles(
|
||||
// Insert into the dom under the "clips" element
|
||||
Track* clipTrack = clipView->m_trackView->getTrack();
|
||||
int trackIndex = std::distance(tc->tracks().begin(), std::find(tc->tracks().begin(), tc->tracks().end(), clipTrack));
|
||||
assert(trackIndex != tc->tracks().size());
|
||||
QDomElement clipElement = dataFile.createElement("clip");
|
||||
clipElement.setAttribute( "trackIndex", trackIndex );
|
||||
clipElement.setAttribute( "trackType", static_cast<int>(clipTrack->type()) );
|
||||
@@ -1308,7 +1310,7 @@ void ClipView::mergeClips(QVector<ClipView*> clipvs)
|
||||
continue;
|
||||
}
|
||||
|
||||
NoteVector currentClipNotes = mcView->getMidiClip()->notes();
|
||||
const NoteVector& currentClipNotes = mcView->getMidiClip()->notes();
|
||||
TimePos mcViewPos = mcView->getMidiClip()->startPosition();
|
||||
|
||||
for (Note* note: currentClipNotes)
|
||||
|
||||
@@ -69,7 +69,7 @@ void PatternEditor::cloneSteps()
|
||||
|
||||
void PatternEditor::removeSteps()
|
||||
{
|
||||
TrackContainer::TrackList tl = model()->tracks();
|
||||
const TrackContainer::TrackList& tl = model()->tracks();
|
||||
|
||||
for (const auto& track : tl)
|
||||
{
|
||||
@@ -176,7 +176,7 @@ void PatternEditor::updatePosition()
|
||||
|
||||
void PatternEditor::makeSteps( bool clone )
|
||||
{
|
||||
TrackContainer::TrackList tl = model()->tracks();
|
||||
const TrackContainer::TrackList& tl = model()->tracks();
|
||||
|
||||
for (const auto& track : tl)
|
||||
{
|
||||
|
||||
@@ -741,10 +741,10 @@ void PianoRoll::fitNoteLengths(bool fill)
|
||||
{
|
||||
if (!hasValidMidiClip()) { return; }
|
||||
m_midiClip->addJournalCheckPoint();
|
||||
m_midiClip->rearrangeAllNotes();
|
||||
|
||||
// Reference notes
|
||||
NoteVector refNotes = m_midiClip->notes();
|
||||
std::sort(refNotes.begin(), refNotes.end(), Note::lessThan);
|
||||
const NoteVector& refNotes = m_midiClip->notes();
|
||||
|
||||
// Notes to edit
|
||||
NoteVector notes = getSelectedNotes();
|
||||
@@ -762,7 +762,7 @@ void PianoRoll::fitNoteLengths(bool fill)
|
||||
}
|
||||
|
||||
int length;
|
||||
NoteVector::iterator ref = refNotes.begin();
|
||||
auto ref = refNotes.begin();
|
||||
for (Note* note : notes)
|
||||
{
|
||||
// Fast forward to next reference note
|
||||
@@ -797,14 +797,11 @@ void PianoRoll::constrainNoteLengths(bool constrainMax)
|
||||
if (!hasValidMidiClip()) { return; }
|
||||
m_midiClip->addJournalCheckPoint();
|
||||
|
||||
NoteVector notes = getSelectedNotes();
|
||||
if (notes.empty())
|
||||
{
|
||||
notes = m_midiClip->notes();
|
||||
}
|
||||
const NoteVector selectedNotes = getSelectedNotes();
|
||||
const auto& notes = selectedNotes.empty() ? m_midiClip->notes() : selectedNotes;
|
||||
|
||||
TimePos bound = m_lenOfNewNotes; // will be length of last note
|
||||
for (Note* note : notes)
|
||||
TimePos bound = m_lenOfNewNotes; // will be length of last note
|
||||
for (auto note : notes)
|
||||
{
|
||||
if (constrainMax ? note->length() > bound : note->length() < bound)
|
||||
{
|
||||
@@ -1207,11 +1204,11 @@ void PianoRoll::shiftSemiTone(int amount) //Shift notes by amount semitones
|
||||
|
||||
auto selectedNotes = getSelectedNotes();
|
||||
//If no notes are selected, shift all of them, otherwise shift selection
|
||||
if (selectedNotes.empty()) { return shiftSemiTone(m_midiClip->notes(), amount); }
|
||||
else { return shiftSemiTone(selectedNotes, amount); }
|
||||
if (selectedNotes.empty()) { shiftSemiTone(m_midiClip->notes(), amount); }
|
||||
else { shiftSemiTone(selectedNotes, amount); }
|
||||
}
|
||||
|
||||
void PianoRoll::shiftSemiTone(NoteVector notes, int amount)
|
||||
void PianoRoll::shiftSemiTone(const NoteVector& notes, int amount)
|
||||
{
|
||||
m_midiClip->addJournalCheckPoint();
|
||||
for (Note *note : notes) { note->setKey( note->key() + amount ); }
|
||||
@@ -1232,11 +1229,11 @@ void PianoRoll::shiftPos(int amount) //Shift notes pos by amount
|
||||
|
||||
auto selectedNotes = getSelectedNotes();
|
||||
//If no notes are selected, shift all of them, otherwise shift selection
|
||||
if (selectedNotes.empty()) { return shiftPos(m_midiClip->notes(), amount); }
|
||||
else { return shiftPos(selectedNotes, amount); }
|
||||
if (selectedNotes.empty()) { shiftPos(m_midiClip->notes(), amount); }
|
||||
else { shiftPos(selectedNotes, amount); }
|
||||
}
|
||||
|
||||
void PianoRoll::shiftPos(NoteVector notes, int amount)
|
||||
void PianoRoll::shiftPos(const NoteVector& notes, int amount)
|
||||
{
|
||||
m_midiClip->addJournalCheckPoint();
|
||||
|
||||
@@ -4134,9 +4131,9 @@ void PianoRoll::finishRecordNote(const Note & n )
|
||||
{
|
||||
if( it->key() == n.key() )
|
||||
{
|
||||
Note n1( n.length(), it->pos(),
|
||||
Note n1(n.length(), it->pos(),
|
||||
it->key(), it->getVolume(),
|
||||
it->getPanning() );
|
||||
it->getPanning(), n.detuning());
|
||||
n1.quantizeLength( quantization() );
|
||||
m_midiClip->addNote( n1 );
|
||||
update();
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
#include "InstrumentFunctions.h"
|
||||
#include "InstrumentFunctionViews.h"
|
||||
#include "InstrumentMidiIOView.h"
|
||||
#include "InstrumentMiscView.h"
|
||||
#include "InstrumentTuningView.h"
|
||||
#include "InstrumentSoundShapingView.h"
|
||||
#include "InstrumentTrack.h"
|
||||
#include "InstrumentTrackView.h"
|
||||
@@ -255,25 +255,25 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) :
|
||||
instrumentFunctionsLayout->addStretch();
|
||||
|
||||
// MIDI tab
|
||||
m_midiView = new InstrumentMidiIOView( m_tabWidget );
|
||||
m_midiView = new InstrumentMidiIOView(m_tabWidget);
|
||||
|
||||
// FX tab
|
||||
m_effectView = new EffectRackView( m_track->m_audioPort.effects(), m_tabWidget );
|
||||
m_effectView = new EffectRackView(m_track->m_audioPort.effects(), m_tabWidget);
|
||||
|
||||
// MISC tab
|
||||
m_miscView = new InstrumentMiscView( m_track, m_tabWidget );
|
||||
// Tuning tab
|
||||
m_tuningView = new InstrumentTuningView(m_track, m_tabWidget);
|
||||
|
||||
|
||||
m_tabWidget->addTab( m_ssView, tr( "Envelope, filter & LFO" ), "env_lfo_tab", 1 );
|
||||
m_tabWidget->addTab( instrumentFunctions, tr( "Chord stacking & arpeggio" ), "func_tab", 2 );
|
||||
m_tabWidget->addTab( m_effectView, tr( "Effects" ), "fx_tab", 3 );
|
||||
m_tabWidget->addTab( m_midiView, tr( "MIDI" ), "midi_tab", 4 );
|
||||
m_tabWidget->addTab( m_miscView, tr( "Miscellaneous" ), "misc_tab", 5 );
|
||||
m_tabWidget->addTab(m_ssView, tr("Envelope, filter & LFO"), "env_lfo_tab", 1);
|
||||
m_tabWidget->addTab(instrumentFunctions, tr("Chord stacking & arpeggio"), "func_tab", 2);
|
||||
m_tabWidget->addTab(m_effectView, tr("Effects"), "fx_tab", 3);
|
||||
m_tabWidget->addTab(m_midiView, tr("MIDI"), "midi_tab", 4);
|
||||
m_tabWidget->addTab(m_tuningView, tr("Tuning and transposition"), "tuning_tab", 5);
|
||||
adjustTabSize(m_ssView);
|
||||
adjustTabSize(instrumentFunctions);
|
||||
m_effectView->resize(EffectRackView::DEFAULT_WIDTH, INSTRUMENT_HEIGHT - 4 - 1);
|
||||
adjustTabSize(m_midiView);
|
||||
adjustTabSize(m_miscView);
|
||||
adjustTabSize(m_tuningView);
|
||||
|
||||
// setup piano-widget
|
||||
m_pianoView = new PianoView( this );
|
||||
@@ -376,12 +376,14 @@ void InstrumentTrackWindow::modelChanged()
|
||||
|
||||
if (m_track->instrument() && m_track->instrument()->flags().testFlag(Instrument::Flag::IsMidiBased))
|
||||
{
|
||||
m_miscView->microtunerGroupBox()->hide();
|
||||
m_tuningView->microtunerNotSupportedLabel()->show();
|
||||
m_tuningView->microtunerGroupBox()->hide();
|
||||
m_track->m_microtuner.enabledModel()->setValue(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_miscView->microtunerGroupBox()->show();
|
||||
m_tuningView->microtunerNotSupportedLabel()->hide();
|
||||
m_tuningView->microtunerGroupBox()->show();
|
||||
}
|
||||
|
||||
m_ssView->setModel( &m_track->m_soundShaping );
|
||||
@@ -389,11 +391,11 @@ void InstrumentTrackWindow::modelChanged()
|
||||
m_arpeggioView->setModel( &m_track->m_arpeggio );
|
||||
m_midiView->setModel( &m_track->m_midiPort );
|
||||
m_effectView->setModel( m_track->m_audioPort.effects() );
|
||||
m_miscView->pitchGroupBox()->setModel(&m_track->m_useMasterPitchModel);
|
||||
m_miscView->microtunerGroupBox()->setModel(m_track->m_microtuner.enabledModel());
|
||||
m_miscView->scaleCombo()->setModel(m_track->m_microtuner.scaleModel());
|
||||
m_miscView->keymapCombo()->setModel(m_track->m_microtuner.keymapModel());
|
||||
m_miscView->rangeImportCheckbox()->setModel(m_track->m_microtuner.keyRangeImportModel());
|
||||
m_tuningView->pitchGroupBox()->setModel(&m_track->m_useMasterPitchModel);
|
||||
m_tuningView->microtunerGroupBox()->setModel(m_track->m_microtuner.enabledModel());
|
||||
m_tuningView->scaleCombo()->setModel(m_track->m_microtuner.scaleModel());
|
||||
m_tuningView->keymapCombo()->setModel(m_track->m_microtuner.keymapModel());
|
||||
m_tuningView->rangeImportCheckbox()->setModel(m_track->m_microtuner.keyRangeImportModel());
|
||||
updateName();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* InstrumentMiscView.cpp - Miscellaneous instrument settings
|
||||
* InstrumentTuningView.cpp - Instrument settings for tuning and transpositions
|
||||
*
|
||||
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2020 Martin Pavelek <he29.HS/at/gmail.com>
|
||||
* Copyright (c) 2020-2022 Martin Pavelek <he29.HS/at/gmail.com>
|
||||
*
|
||||
* This file is part of LMMS - https://lmms.io
|
||||
*
|
||||
@@ -23,24 +23,28 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "InstrumentMiscView.h"
|
||||
#include "InstrumentTuningView.h"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QPixmap>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "ComboBox.h"
|
||||
#include "GroupBox.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "gui_templates.h"
|
||||
#include "InstrumentTrack.h"
|
||||
#include "LedCheckBox.h"
|
||||
#include "MainWindow.h"
|
||||
#include "PixmapButton.h"
|
||||
|
||||
|
||||
namespace lmms::gui
|
||||
{
|
||||
|
||||
|
||||
InstrumentMiscView::InstrumentMiscView(InstrumentTrack *it, QWidget *parent) :
|
||||
InstrumentTuningView::InstrumentTuningView(InstrumentTrack *it, QWidget *parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
auto layout = new QVBoxLayout(this);
|
||||
@@ -60,6 +64,11 @@ InstrumentMiscView::InstrumentMiscView(InstrumentTrack *it, QWidget *parent) :
|
||||
masterPitchLayout->addWidget(tlabel);
|
||||
|
||||
// Microtuner settings
|
||||
m_microtunerNotSupportedLabel = new QLabel(tr("Microtuner is not available for MIDI-based instruments."));
|
||||
m_microtunerNotSupportedLabel->setWordWrap(true);
|
||||
m_microtunerNotSupportedLabel->hide();
|
||||
layout->addWidget(m_microtunerNotSupportedLabel);
|
||||
|
||||
m_microtunerGroupBox = new GroupBox(tr("MICROTUNER"));
|
||||
m_microtunerGroupBox->setModel(it->m_microtuner.enabledModel());
|
||||
layout->addWidget(m_microtunerGroupBox);
|
||||
@@ -67,8 +76,22 @@ InstrumentMiscView::InstrumentMiscView(InstrumentTrack *it, QWidget *parent) :
|
||||
auto microtunerLayout = new QVBoxLayout(m_microtunerGroupBox);
|
||||
microtunerLayout->setContentsMargins(8, 18, 8, 8);
|
||||
|
||||
auto scaleEditLayout = new QHBoxLayout();
|
||||
scaleEditLayout->setContentsMargins(0, 0, 4, 0);
|
||||
microtunerLayout->addLayout(scaleEditLayout);
|
||||
|
||||
auto scaleLabel = new QLabel(tr("Active scale:"));
|
||||
microtunerLayout->addWidget(scaleLabel);
|
||||
scaleEditLayout->addWidget(scaleLabel);
|
||||
|
||||
QPixmap editPixmap(embed::getIconPixmap("edit_draw_small"));
|
||||
auto editPixButton = new PixmapButton(this, tr("Edit scales and keymaps"));
|
||||
editPixButton->setToolTip(tr("Edit scales and keymaps"));
|
||||
editPixButton->setInactiveGraphic(editPixmap);
|
||||
editPixButton->setActiveGraphic(editPixmap);
|
||||
editPixButton->setFixedSize(16, 16);
|
||||
connect(editPixButton, SIGNAL(clicked()), getGUI()->mainWindow(), SLOT(toggleMicrotunerWin()));
|
||||
|
||||
scaleEditLayout->addWidget(editPixButton);
|
||||
|
||||
m_scaleCombo = new ComboBox();
|
||||
m_scaleCombo->setModel(it->m_microtuner.scaleModel());
|
||||
@@ -322,70 +322,65 @@ void PianoView::modelChanged()
|
||||
|
||||
|
||||
|
||||
// gets the key from the given mouse-position
|
||||
// Gets the key from the given mouse position
|
||||
/*! \brief Get the key from the mouse position in the piano display
|
||||
*
|
||||
* First we determine it roughly by the position of the point given in
|
||||
* white key widths from our start. We then add in any black keys that
|
||||
* might have been skipped over (they take a key number, but no 'white
|
||||
* key' space). We then add in our starting key number.
|
||||
*
|
||||
* We then determine whether it was a black key that was pressed by
|
||||
* checking whether it was within the vertical range of black keys.
|
||||
* Black keys sit exactly between white keys on this keyboard, so
|
||||
* we then shift the note down or up if we were in the left or right
|
||||
* half of the white note. We only do this, of course, if the white
|
||||
* note has a black key on that side, so to speak.
|
||||
*
|
||||
* This function returns const because there is a linear mapping from
|
||||
* the point given to the key returned that never changes.
|
||||
*
|
||||
* \param _p The point that the mouse was pressed.
|
||||
* \param p The point that the mouse was pressed.
|
||||
*/
|
||||
int PianoView::getKeyFromMouse( const QPoint & _p ) const
|
||||
int PianoView::getKeyFromMouse(const QPoint& p) const
|
||||
{
|
||||
int offset = _p.x() % PW_WHITE_KEY_WIDTH;
|
||||
if( offset < 0 ) offset += PW_WHITE_KEY_WIDTH;
|
||||
int key_num = ( _p.x() - offset) / PW_WHITE_KEY_WIDTH;
|
||||
// The left-most key visible in the piano display is always white
|
||||
const int startingWhiteKey = m_pianoScroll->value();
|
||||
|
||||
for( int i = 0; i <= key_num; ++i )
|
||||
// Adjust the mouse x position as if x == 0 was the left side of the lowest key
|
||||
const int adjX = p.x() + (startingWhiteKey * PW_WHITE_KEY_WIDTH);
|
||||
|
||||
// Can early return for notes too low
|
||||
if (adjX <= 0) { return 0; }
|
||||
|
||||
// Now we can calculate the key number (in only white keys) and the octave
|
||||
const int whiteKey = adjX / PW_WHITE_KEY_WIDTH;
|
||||
const int octave = whiteKey / Piano::WhiteKeysPerOctave;
|
||||
|
||||
// Calculate for full octaves
|
||||
int key = octave * KeysPerOctave;
|
||||
|
||||
// Adjust for white notes in the current octave
|
||||
// (WhiteKeys maps each white key to the number of notes to their left in the octave)
|
||||
key += static_cast<int>(WhiteKeys[whiteKey % Piano::WhiteKeysPerOctave]);
|
||||
|
||||
// Might be a black key, which would require further adjustment
|
||||
if (p.y() < PIANO_BASE + PW_BLACK_KEY_HEIGHT)
|
||||
{
|
||||
if ( Piano::isBlackKey( m_startKey+i ) )
|
||||
// Maps white keys to neighboring black keys
|
||||
static constexpr std::array neighboringKeyMap {
|
||||
std::pair{ 0, 1 }, // C --> no B#; C#
|
||||
std::pair{ 1, 1 }, // D --> C#; D#
|
||||
std::pair{ 1, 0 }, // E --> D#; no E#
|
||||
std::pair{ 0, 1 }, // F --> no E#; F#
|
||||
std::pair{ 1, 1 }, // G --> F#; G#
|
||||
std::pair{ 1, 1 }, // A --> G#; A#
|
||||
std::pair{ 1, 0 }, // B --> A#; no B#
|
||||
};
|
||||
|
||||
const auto neighboringBlackKeys = neighboringKeyMap[whiteKey % Piano::WhiteKeysPerOctave];
|
||||
const int offset = adjX - (whiteKey * PW_WHITE_KEY_WIDTH); // mouse X offset from white key
|
||||
|
||||
if (offset < PW_BLACK_KEY_WIDTH / 2)
|
||||
{
|
||||
++key_num;
|
||||
// At the location of a (possibly non-existent) black key on the left side
|
||||
key -= neighboringBlackKeys.first;
|
||||
}
|
||||
}
|
||||
for( int i = 0; i >= key_num; --i )
|
||||
{
|
||||
if ( Piano::isBlackKey( m_startKey+i ) )
|
||||
else if (offset > PW_WHITE_KEY_WIDTH - (PW_BLACK_KEY_WIDTH / 2))
|
||||
{
|
||||
--key_num;
|
||||
// At the location of a (possibly non-existent) black key on the right side
|
||||
key += neighboringBlackKeys.second;
|
||||
}
|
||||
|
||||
// For white keys in between black keys, no further adjustment is needed
|
||||
}
|
||||
|
||||
key_num += m_startKey;
|
||||
|
||||
// is it a black key?
|
||||
if( _p.y() < PIANO_BASE + PW_BLACK_KEY_HEIGHT )
|
||||
{
|
||||
// then do extra checking whether the mouse-cursor is over
|
||||
// a black key
|
||||
if( key_num > 0 && Piano::isBlackKey( key_num-1 ) &&
|
||||
offset <= ( PW_WHITE_KEY_WIDTH / 2 ) -
|
||||
( PW_BLACK_KEY_WIDTH / 2 ) )
|
||||
{
|
||||
--key_num;
|
||||
}
|
||||
if( key_num < NumKeys - 1 && Piano::isBlackKey( key_num+1 ) &&
|
||||
offset >= ( PW_WHITE_KEY_WIDTH -
|
||||
PW_BLACK_KEY_WIDTH / 2 ) )
|
||||
{
|
||||
++key_num;
|
||||
}
|
||||
}
|
||||
|
||||
// some range-checking-stuff
|
||||
return qBound( 0, key_num, NumKeys - 1 );
|
||||
return std::clamp(key, 0, NumKeys - 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -396,12 +391,12 @@ int PianoView::getKeyFromMouse( const QPoint & _p ) const
|
||||
*
|
||||
* We need to update our start key position based on the new position.
|
||||
*
|
||||
* \param _new_pos the new key position.
|
||||
* \param newPos the new key position, counting only white keys.
|
||||
*/
|
||||
void PianoView::pianoScrolled(int new_pos)
|
||||
void PianoView::pianoScrolled(int newPos)
|
||||
{
|
||||
m_startKey = static_cast<Octave>(new_pos / Piano::WhiteKeysPerOctave)
|
||||
+ WhiteKeys[new_pos % Piano::WhiteKeysPerOctave];
|
||||
m_startKey = static_cast<Octave>(newPos / Piano::WhiteKeysPerOctave)
|
||||
+ WhiteKeys[newPos % Piano::WhiteKeysPerOctave];
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ bool TrackContentWidget::canPasteSelection( TimePos clipPos, const QMimeData* md
|
||||
const int initialTrackIndex = tiAttr.value().toInt();
|
||||
|
||||
// Get the current track's index
|
||||
const TrackContainer::TrackList tracks = t->trackContainer()->tracks();
|
||||
const TrackContainer::TrackList& tracks = t->trackContainer()->tracks();
|
||||
const auto currentTrackIt = std::find(tracks.begin(), tracks.end(), t);
|
||||
const int currentTrackIndex = currentTrackIt != tracks.end() ? std::distance(tracks.begin(), currentTrackIt) : -1;
|
||||
|
||||
@@ -443,7 +443,7 @@ bool TrackContentWidget::pasteSelection( TimePos clipPos, const QMimeData * md,
|
||||
TimePos grabbedClipPos = clipPosAttr.value().toInt();
|
||||
|
||||
// Snap the mouse position to the beginning of the dropped bar, in ticks
|
||||
const TrackContainer::TrackList tracks = getTrack()->trackContainer()->tracks();
|
||||
const TrackContainer::TrackList& tracks = getTrack()->trackContainer()->tracks();
|
||||
const auto currentTrackIt = std::find(tracks.begin(), tracks.end(), getTrack());
|
||||
const int currentTrackIndex = currentTrackIt != tracks.end() ? std::distance(tracks.begin(), currentTrackIt) : -1;
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <QPainter>
|
||||
|
||||
#include "AudioEngine.h"
|
||||
@@ -72,10 +73,9 @@ void CPULoadWidget::paintEvent( QPaintEvent * )
|
||||
QPainter p( &m_temp );
|
||||
p.drawPixmap( 0, 0, m_background );
|
||||
|
||||
// as load-indicator consists of small 2-pixel wide leds with
|
||||
// 1 pixel spacing, we have to make sure, only whole leds are
|
||||
// shown which we achieve by the following formula
|
||||
int w = ( m_leds.width() * m_currentLoad / 300 ) * 3;
|
||||
// Normally the CPU load indicator moves smoothly, with 1 pixel resolution. However, some themes may want to
|
||||
// draw discrete elements (like LEDs), so the stepSize property can be used to specify a larger step size.
|
||||
int w = (m_leds.width() * std::min(m_currentLoad, 100) / (stepSize() * 100)) * stepSize();
|
||||
if( w > 0 )
|
||||
{
|
||||
p.drawPixmap( 23, 3, m_leds, 0, 0, w,
|
||||
@@ -91,10 +91,21 @@ void CPULoadWidget::paintEvent( QPaintEvent * )
|
||||
|
||||
void CPULoadWidget::updateCpuLoad()
|
||||
{
|
||||
// smooth load-values a bit
|
||||
int new_load = ( m_currentLoad + Engine::audioEngine()->cpuLoad() ) / 2;
|
||||
if( new_load != m_currentLoad )
|
||||
// Additional display smoothing for the main load-value. Stronger averaging
|
||||
// cannot be used directly in the profiler: cpuLoad() must react fast enough
|
||||
// to be useful as overload indicator in AudioEngine::criticalXRuns().
|
||||
const int new_load = (m_currentLoad + Engine::audioEngine()->cpuLoad()) / 2;
|
||||
|
||||
if (new_load != m_currentLoad)
|
||||
{
|
||||
auto engine = Engine::audioEngine();
|
||||
setToolTip(
|
||||
tr("DSP total: %1%").arg(new_load) + "\n"
|
||||
+ tr(" - Notes and setup: %1%").arg(engine->detailLoad(AudioEngineProfiler::DetailType::NoteSetup)) + "\n"
|
||||
+ tr(" - Instruments: %1%").arg(engine->detailLoad(AudioEngineProfiler::DetailType::Instruments)) + "\n"
|
||||
+ tr(" - Effects: %1%").arg(engine->detailLoad(AudioEngineProfiler::DetailType::Effects)) + "\n"
|
||||
+ tr(" - Mixing: %1%").arg(engine->detailLoad(AudioEngineProfiler::DetailType::Mixing))
|
||||
);
|
||||
m_currentLoad = new_load;
|
||||
m_changed = true;
|
||||
update();
|
||||
@@ -102,4 +113,4 @@ void CPULoadWidget::updateCpuLoad()
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms::gui
|
||||
} // namespace lmms::gui
|
||||
|
||||
@@ -46,11 +46,11 @@ SimpleTextFloat::SimpleTextFloat() :
|
||||
m_textLabel = new QLabel(this);
|
||||
layout->addWidget(m_textLabel);
|
||||
|
||||
m_showTimer = new QTimer();
|
||||
m_showTimer = new QTimer(this);
|
||||
m_showTimer->setSingleShot(true);
|
||||
QObject::connect(m_showTimer, &QTimer::timeout, this, &SimpleTextFloat::show);
|
||||
|
||||
m_hideTimer = new QTimer();
|
||||
m_hideTimer = new QTimer(this);
|
||||
m_hideTimer->setSingleShot(true);
|
||||
QObject::connect(m_hideTimer, &QTimer::timeout, this, &SimpleTextFloat::hide);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "ConfigManager.h"
|
||||
#include "ControllerConnection.h"
|
||||
#include "DataFile.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "Mixer.h"
|
||||
#include "InstrumentTrackView.h"
|
||||
#include "Instrument.h"
|
||||
@@ -37,6 +38,7 @@
|
||||
#include "MixHelpers.h"
|
||||
#include "PatternStore.h"
|
||||
#include "PatternTrack.h"
|
||||
#include "PianoRoll.h"
|
||||
#include "Pitch.h"
|
||||
#include "Song.h"
|
||||
|
||||
@@ -72,6 +74,7 @@ InstrumentTrack::InstrumentTrack( TrackContainer* tc ) :
|
||||
m_microtuner()
|
||||
{
|
||||
m_pitchModel.setCenterValue( 0 );
|
||||
m_pitchModel.setStrictStepSize(true);
|
||||
m_panningModel.setCenterValue( DefaultPanning );
|
||||
m_baseNoteModel.setInitValue( DefaultKey );
|
||||
m_firstKeyModel.setInitValue(0);
|
||||
@@ -341,9 +344,10 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const TimePos& tim
|
||||
NotePlayHandleManager::acquire(
|
||||
this, offset,
|
||||
typeInfo<f_cnt_t>::max() / 2,
|
||||
Note( TimePos(), TimePos(), event.key(), event.volume( midiPort()->baseVelocity() ) ),
|
||||
Note(TimePos(), Engine::getSong()->getPlayPos(Engine::getSong()->playMode()),
|
||||
event.key(), event.volume(midiPort()->baseVelocity())),
|
||||
nullptr, event.channel(),
|
||||
NotePlayHandle::Origin::MidiInput );
|
||||
NotePlayHandle::Origin::MidiInput);
|
||||
m_notes[event.key()] = nph;
|
||||
if( ! Engine::audioEngine()->addPlayHandle( nph ) )
|
||||
{
|
||||
@@ -710,7 +714,7 @@ bool InstrumentTrack::play( const TimePos & _start, const fpp_t _frames,
|
||||
// Handle automation: detuning
|
||||
for (const auto& processHandle : m_processHandles)
|
||||
{
|
||||
processHandle->processTimePos(_start);
|
||||
processHandle->processTimePos(_start, m_pitchModel.value(), gui::GuiApplication::instance()->pianoRoll()->isRecording());
|
||||
}
|
||||
|
||||
if ( clips.size() == 0 )
|
||||
|
||||
@@ -305,7 +305,7 @@ void MidiClip::setStep( int step, bool enabled )
|
||||
|
||||
|
||||
|
||||
void MidiClip::splitNotes(NoteVector notes, TimePos pos)
|
||||
void MidiClip::splitNotes(const NoteVector& notes, TimePos pos)
|
||||
{
|
||||
if (notes.empty()) { return; }
|
||||
|
||||
@@ -472,7 +472,7 @@ MidiClip * MidiClip::nextMidiClip() const
|
||||
|
||||
MidiClip * MidiClip::adjacentMidiClipByOffset(int offset) const
|
||||
{
|
||||
std::vector<Clip *> clips = m_instrumentTrack->getClips();
|
||||
auto& clips = m_instrumentTrack->getClips();
|
||||
int clipNum = m_instrumentTrack->getClipNum(this);
|
||||
if (clipNum < 0 || clipNum > clips.size() - 1) { return nullptr; }
|
||||
return dynamic_cast<MidiClip*>(clips[clipNum + offset]);
|
||||
|
||||
Reference in New Issue
Block a user