@@ -48,7 +48,7 @@ public:
|
||||
return "bbtrackcontainer";
|
||||
}
|
||||
|
||||
tact_t lengthOfBB( int _bb );
|
||||
tact_t lengthOfBB( int _bb ) const;
|
||||
inline tact_t lengthOfCurrentBB()
|
||||
{
|
||||
return lengthOfBB( currentBB() );
|
||||
@@ -62,6 +62,7 @@ public:
|
||||
void fixIncorrectPositions();
|
||||
void createTCOsForBB( int _bb );
|
||||
|
||||
AutomatedValueMap automatedValuesAt(MidiTime time, int tcoNum) const override;
|
||||
|
||||
public slots:
|
||||
void play();
|
||||
|
||||
@@ -204,7 +204,8 @@ public:
|
||||
return m_globalAutomationTrack;
|
||||
}
|
||||
|
||||
static AutomatedValueMap automatedValuesAt(const Track::tcoVector& tcos, MidiTime time);
|
||||
//TODO: Add Q_DECL_OVERRIDE when Qt4 is dropped
|
||||
AutomatedValueMap automatedValuesAt(MidiTime time, int tcoNum = -1) const;
|
||||
|
||||
// file management
|
||||
void createNewProject();
|
||||
@@ -326,7 +327,7 @@ private:
|
||||
|
||||
void removeAllControllers();
|
||||
|
||||
void processAutomations(const TrackList& tracks, MidiTime timeStart, fpp_t frames, int tcoNum);
|
||||
void processAutomations(const TrackList& tracks, MidiTime timeStart, fpp_t frames);
|
||||
|
||||
AutomationTrack * m_globalAutomationTrack;
|
||||
|
||||
|
||||
@@ -93,11 +93,14 @@ public:
|
||||
return m_TrackContainerType;
|
||||
}
|
||||
|
||||
virtual AutomatedValueMap automatedValuesAt(MidiTime time, int tcoNum = -1) const;
|
||||
|
||||
signals:
|
||||
void trackAdded( Track * _track );
|
||||
|
||||
protected:
|
||||
static AutomatedValueMap automatedValuesFromTracks(const TrackList &tracks, MidiTime timeStart, int tcoNum = -1);
|
||||
|
||||
mutable QReadWriteLock m_tracksMutex;
|
||||
|
||||
private:
|
||||
|
||||
@@ -90,7 +90,7 @@ void BBTrackContainer::updateAfterTrackAdd()
|
||||
|
||||
|
||||
|
||||
tact_t BBTrackContainer::lengthOfBB( int _bb )
|
||||
tact_t BBTrackContainer::lengthOfBB( int _bb ) const
|
||||
{
|
||||
MidiTime max_length = MidiTime::ticksPerTact();
|
||||
|
||||
@@ -239,6 +239,20 @@ void BBTrackContainer::createTCOsForBB( int _bb )
|
||||
}
|
||||
}
|
||||
|
||||
AutomatedValueMap BBTrackContainer::automatedValuesAt(MidiTime time, int tcoNum) const
|
||||
{
|
||||
Q_ASSERT(tcoNum >= 0);
|
||||
Q_ASSERT(time.getTicks() >= 0);
|
||||
|
||||
auto length_tacts = lengthOfBB(tcoNum);
|
||||
auto length_ticks = length_tacts * MidiTime::ticksPerTact();
|
||||
if (time > length_ticks) {
|
||||
time = length_ticks;
|
||||
}
|
||||
|
||||
return TrackContainer::automatedValuesAt(time + (MidiTime::ticksPerTact() * tcoNum), tcoNum);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -378,7 +378,7 @@ void Song::processNextBuffer()
|
||||
|
||||
if( ( f_cnt_t ) currentFrame == 0 )
|
||||
{
|
||||
processAutomations(trackList, m_playPos[m_playMode], framesToPlay, tcoNum);
|
||||
processAutomations(trackList, m_playPos[m_playMode], framesToPlay);
|
||||
|
||||
// loop through all tracks and play them
|
||||
for( int i = 0; i < trackList.size(); ++i )
|
||||
@@ -401,58 +401,45 @@ void Song::processNextBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
void Song::processAutomations(const TrackList &tracklist, MidiTime timeStart, fpp_t frames, int tcoNum)
|
||||
|
||||
void Song::processAutomations(const TrackList &tracklist, MidiTime timeStart, fpp_t)
|
||||
{
|
||||
QVector<AutomationTrack*> tracks;
|
||||
|
||||
if(m_playMode == Mode_PlaySong)
|
||||
{
|
||||
tracks << m_globalAutomationTrack;
|
||||
}
|
||||
for( Track* track : tracklist)
|
||||
{
|
||||
if (track->type() == Track::AutomationTrack || track->type() == Track::HiddenAutomationTrack)
|
||||
{
|
||||
tracks << dynamic_cast<AutomationTrack*>(track);
|
||||
}
|
||||
}
|
||||
std::remove_if(tracks.begin(), tracks.end(), std::mem_fn(&Track::isMuted));
|
||||
|
||||
Track::tcoVector tcos;
|
||||
AutomatedValueMap values;
|
||||
|
||||
if (tcoNum < 0)
|
||||
{
|
||||
// Collect all relevant patterns, sorted by start position
|
||||
MidiTime timeEnd = timeStart + static_cast<int>(frames / Engine::framesPerTick());
|
||||
for (AutomationTrack* track: tracks)
|
||||
{
|
||||
track->getTCOsInRange(tcos, 0, timeEnd);
|
||||
}
|
||||
|
||||
values = automatedValuesAt(tcos, timeStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tracklist.size() != 1)
|
||||
{
|
||||
qWarning() << "processAutomations called with specified tcoNum but not exactly one track";
|
||||
}
|
||||
|
||||
for (AutomationTrack* track: tracks)
|
||||
{
|
||||
TrackContentObject* tco = track->getTCO(tcoNum);
|
||||
auto p = dynamic_cast<AutomationPattern *>(tco);
|
||||
|
||||
for (AutomatableModel* object : p->objects())
|
||||
{
|
||||
values[object] = p->valueAt(timeStart);
|
||||
}
|
||||
tcos << tco;
|
||||
}
|
||||
}
|
||||
|
||||
QSet<const AutomatableModel*> recordedModels;
|
||||
|
||||
TrackContainer* container = this;
|
||||
int tcoNum = -1;
|
||||
|
||||
switch (m_playMode)
|
||||
{
|
||||
case Mode_PlaySong:
|
||||
break;
|
||||
case Mode_PlayBB:
|
||||
{
|
||||
Q_ASSERT(tracklist.size() == 1);
|
||||
Q_ASSERT(tracklist.at(0)->type() == Track::BBTrack);
|
||||
auto bbTrack = dynamic_cast<BBTrack*>(tracklist.at(0));
|
||||
auto bbContainer = Engine::getBBTrackContainer();
|
||||
container = bbContainer;
|
||||
tcoNum = bbTrack->index();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
values = container->automatedValuesAt(timeStart, tcoNum);
|
||||
TrackList tracks = container->tracks();
|
||||
|
||||
Track::tcoVector tcos;
|
||||
for (Track* track : tracks)
|
||||
{
|
||||
if (track->type() == Track::AutomationTrack) {
|
||||
track->getTCOsInRange(tcos, 0, timeStart);
|
||||
}
|
||||
}
|
||||
|
||||
// Process recording
|
||||
for (TrackContentObject* tco : tcos)
|
||||
{
|
||||
@@ -849,35 +836,10 @@ AutomationPattern * Song::tempoAutomationPattern()
|
||||
return AutomationPattern::globalAutomationPattern( &m_tempoModel );
|
||||
}
|
||||
|
||||
AutomatedValueMap Song::automatedValuesAt(const Track::tcoVector &tcos, MidiTime time)
|
||||
|
||||
AutomatedValueMap Song::automatedValuesAt(MidiTime time, int tcoNum) const
|
||||
{
|
||||
AutomatedValueMap valueMap;
|
||||
|
||||
for(TrackContentObject* tco : tcos)
|
||||
{
|
||||
if (tco->isMuted() || tco->startPosition() > time) {
|
||||
continue;
|
||||
}
|
||||
AutomationPattern* p = dynamic_cast<AutomationPattern *>(tco);
|
||||
if (!p) {
|
||||
qCritical() << "automatedValuesAt: tco passed is not an automation pattern";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! p->hasAutomation()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MidiTime relTime = time - p->startPosition();
|
||||
float value = p->valueAt(relTime);
|
||||
|
||||
for (AutomatableModel* model : p->objects())
|
||||
{
|
||||
valueMap[model] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return valueMap;
|
||||
return TrackContainer::automatedValuesFromTracks(TrackList(tracks()) << m_globalAutomationTrack, time, tcoNum);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,12 +29,16 @@
|
||||
#include <QDomElement>
|
||||
#include <QWriteLocker>
|
||||
|
||||
#include "AutomationPattern.h"
|
||||
#include "AutomationTrack.h"
|
||||
#include "BBTrack.h"
|
||||
#include "BBTrackContainer.h"
|
||||
#include "TrackContainer.h"
|
||||
#include "InstrumentTrack.h"
|
||||
#include "GuiApplication.h"
|
||||
#include "MainWindow.h"
|
||||
#include "Song.h"
|
||||
|
||||
#include "GuiApplication.h"
|
||||
#include "MainWindow.h"
|
||||
|
||||
TrackContainer::TrackContainer() :
|
||||
Model( NULL ),
|
||||
@@ -234,6 +238,85 @@ bool TrackContainer::isEmpty() const
|
||||
|
||||
|
||||
|
||||
AutomatedValueMap TrackContainer::automatedValuesAt(MidiTime time, int tcoNum) const
|
||||
{
|
||||
return automatedValuesFromTracks(tracks(), time, tcoNum);
|
||||
}
|
||||
|
||||
|
||||
AutomatedValueMap TrackContainer::automatedValuesFromTracks(const TrackList &tracks, MidiTime time, int tcoNum)
|
||||
{
|
||||
Track::tcoVector tcos;
|
||||
|
||||
for (Track* track: tracks)
|
||||
{
|
||||
if (track->isMuted()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(track->type())
|
||||
{
|
||||
case Track::AutomationTrack:
|
||||
case Track::HiddenAutomationTrack:
|
||||
case Track::BBTrack:
|
||||
if (tcoNum < 0) {
|
||||
track->getTCOsInRange(tcos, 0, time);
|
||||
} else {
|
||||
Q_ASSERT(track->numOfTCOs() > tcoNum);
|
||||
tcos << track->getTCO(tcoNum);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AutomatedValueMap valueMap;
|
||||
|
||||
Q_ASSERT(std::is_sorted(tcos.begin(), tcos.end(), TrackContentObject::comparePosition));
|
||||
|
||||
for(TrackContentObject* tco : tcos)
|
||||
{
|
||||
if (tco->isMuted() || tco->startPosition() > time) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto* p = dynamic_cast<AutomationPattern *>(tco))
|
||||
{
|
||||
if (! p->hasAutomation()) {
|
||||
continue;
|
||||
}
|
||||
MidiTime relTime = time - p->startPosition();
|
||||
float value = p->valueAt(relTime);
|
||||
|
||||
for (AutomatableModel* model : p->objects())
|
||||
{
|
||||
valueMap[model] = value;
|
||||
}
|
||||
}
|
||||
else if (auto* bb = dynamic_cast<BBTCO *>(tco))
|
||||
{
|
||||
auto bbIndex = dynamic_cast<class BBTrack*>(bb->getTrack())->index();
|
||||
auto bbContainer = Engine::getBBTrackContainer();
|
||||
|
||||
MidiTime bbTime = time - tco->startPosition();
|
||||
bbTime = std::min(bbTime, tco->length());
|
||||
bbTime = bbTime % (bbContainer->lengthOfBB(bbIndex) * MidiTime::ticksPerTact());
|
||||
|
||||
auto bbValues = bbContainer->automatedValuesAt(bbTime, bbIndex);
|
||||
for (auto it=bbValues.begin(); it != bbValues.end(); it++)
|
||||
{
|
||||
// override old values, bb track with the highest index takes precedence
|
||||
valueMap[it.key()] = it.value();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return valueMap;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
|
||||
#include "AutomationPattern.h"
|
||||
#include "AutomationTrack.h"
|
||||
#include "BBTrack.h"
|
||||
#include "BBTrackContainer.h"
|
||||
#include "TrackContainer.h"
|
||||
|
||||
#include "Engine.h"
|
||||
@@ -69,35 +71,77 @@ private slots:
|
||||
QCOMPARE(p.valueAt(150), 1.0f);
|
||||
}
|
||||
|
||||
void testTrack()
|
||||
void testPatterns()
|
||||
{
|
||||
FloatModel model;
|
||||
|
||||
AutomationPattern p1(nullptr);
|
||||
auto song = Engine::getSong();
|
||||
AutomationTrack track(song);
|
||||
|
||||
AutomationPattern p1(&track);
|
||||
p1.setProgressionType(AutomationPattern::LinearProgression);
|
||||
p1.putValue(0, 0.0, false);
|
||||
p1.putValue(10, 1.0, false);
|
||||
p1.movePosition(0);
|
||||
p1.addObject(&model);
|
||||
|
||||
AutomationPattern p2(nullptr);
|
||||
AutomationPattern p2(&track);
|
||||
p2.setProgressionType(AutomationPattern::LinearProgression);
|
||||
p2.putValue(0, 0.0, false);
|
||||
p2.putValue(100, 1.0, false);
|
||||
p2.movePosition(100);
|
||||
p2.addObject(&model);
|
||||
|
||||
AutomationPattern p3(nullptr);
|
||||
AutomationPattern p3(&track);
|
||||
p3.addObject(&model);
|
||||
//XXX: Why is this even necessary?
|
||||
p3.clear();
|
||||
|
||||
QCOMPARE(Song::automatedValuesAt({&p1, &p2, &p3}, 0)[&model], 0.0f);
|
||||
QCOMPARE(Song::automatedValuesAt({&p1, &p2, &p3}, 5)[&model], 0.5f);
|
||||
QCOMPARE(Song::automatedValuesAt({&p1, &p2, &p3}, 10)[&model], 1.0f);
|
||||
QCOMPARE(Song::automatedValuesAt({&p1, &p2, &p3}, 50)[&model], 1.0f);
|
||||
QCOMPARE(Song::automatedValuesAt({&p1, &p2, &p3}, 100)[&model], 0.0f);
|
||||
QCOMPARE(Song::automatedValuesAt({&p1, &p2, &p3}, 150)[&model], 0.5f);
|
||||
QCOMPARE(song->automatedValuesAt( 0)[&model], 0.0f);
|
||||
QCOMPARE(song->automatedValuesAt( 5)[&model], 0.5f);
|
||||
QCOMPARE(song->automatedValuesAt( 10)[&model], 1.0f);
|
||||
QCOMPARE(song->automatedValuesAt( 50)[&model], 1.0f);
|
||||
QCOMPARE(song->automatedValuesAt(100)[&model], 0.0f);
|
||||
QCOMPARE(song->automatedValuesAt(150)[&model], 0.5f);
|
||||
}
|
||||
|
||||
void testBBTrack()
|
||||
{
|
||||
auto song = Engine::getSong();
|
||||
auto bbContainer = Engine::getBBTrackContainer();
|
||||
BBTrack bbTrack(song);
|
||||
AutomationTrack automationTrack(bbContainer);
|
||||
bbTrack.createTCOsForBB(bbTrack.index());
|
||||
|
||||
QVERIFY(automationTrack.numOfTCOs());
|
||||
AutomationPattern* p1 = dynamic_cast<AutomationPattern*>(automationTrack.getTCO(0));
|
||||
QVERIFY(p1);
|
||||
|
||||
FloatModel model;
|
||||
|
||||
p1->setProgressionType(AutomationPattern::LinearProgression);
|
||||
p1->putValue(0, 0.0, false);
|
||||
p1->putValue(10, 1.0, false);
|
||||
p1->addObject(&model);
|
||||
|
||||
QCOMPARE(bbContainer->automatedValuesAt( 0, bbTrack.index())[&model], 0.0f);
|
||||
QCOMPARE(bbContainer->automatedValuesAt( 5, bbTrack.index())[&model], 0.5f);
|
||||
QCOMPARE(bbContainer->automatedValuesAt(10, bbTrack.index())[&model], 1.0f);
|
||||
QCOMPARE(bbContainer->automatedValuesAt(50, bbTrack.index())[&model], 1.0f);
|
||||
|
||||
BBTrack bbTrack2(song);
|
||||
bbTrack.createTCOsForBB(bbTrack2.index());
|
||||
|
||||
QCOMPARE(bbContainer->automatedValuesAt(5, bbTrack.index())[&model], 0.5f);
|
||||
QVERIFY(! bbContainer->automatedValuesAt(5, bbTrack2.index()).size());
|
||||
|
||||
BBTCO tco(&bbTrack);
|
||||
tco.changeLength(MidiTime::ticksPerTact() * 2);
|
||||
tco.movePosition(0);
|
||||
|
||||
QCOMPARE(song->automatedValuesAt(0)[&model], 0.0f);
|
||||
QCOMPARE(song->automatedValuesAt(5)[&model], 0.5f);
|
||||
QCOMPARE(song->automatedValuesAt(MidiTime::ticksPerTact() + 5)[&model], 0.5f);
|
||||
}
|
||||
} AutomationTrackTest;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user