Skip to content

Commit

Permalink
MIDI Import 시간 정밀도 개선 (#11)
Browse files Browse the repository at this point in the history
* fea) Use rounding instead of truncation in MIDI import to prevent errors by 1 tick

* fea) Change default ppqn value from 48 to 480(Logic Pro X precision)

* etc) Fix compatibility issues for loading older project
  • Loading branch information
PhysSong authored Jan 21, 2022
1 parent b2c74fb commit 7406e28
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 10 deletions.
1 change: 1 addition & 0 deletions include/DataFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ class LMMS_EXPORT DataFile : public QDomDocument
void upgrade_automationNodes();
void upgrade_extendedNoteRange();
void upgrade_defaultTripleOscillatorHQ();
void upgrade_ppqn();

// List of all upgrade methods
static const std::vector<UpgradeMethod> UPGRADE_METHODS;
Expand Down
2 changes: 1 addition & 1 deletion include/MidiTime.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
#include "lmms_basics.h"

// note: a bar was erroneously called "tact" in older versions of LMMS
const int DefaultTicksPerBar = 192;
const int DefaultTicksPerBar = 1920;
const int DefaultStepsPerBar = 16;
const int DefaultBeatsPerBar = DefaultTicksPerBar / DefaultStepsPerBar;

Expand Down
4 changes: 2 additions & 2 deletions plugins/MidiImport/MidiImport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,10 +536,10 @@ bool MidiImport::readSMF( TrackContainer* tc )
{
smfMidiChannel * ch = chs[evt->chan].create(tc, trackName, mappingForChannel(evt->chan));
Alg_note_ptr noteEvt = dynamic_cast<Alg_note_ptr>( evt );
int ticks = noteEvt->get_duration() * ticksPerBeat;
int ticks = std::round(noteEvt->get_duration() * ticksPerBeat);
//int pitchCorrection = ch->it_inst->flags() & Instrument::IsMidiBased ? 0 : -12;
Note n( (ticks < 1 ? 1 : ticks ),
noteEvt->get_start_time() * ticksPerBeat,
std::round(noteEvt->get_start_time() * ticksPerBeat),
noteEvt->get_identifier(),
std::round(noteEvt->get_loud() * (200.f / 127.f))); // Map from MIDI velocity to LMMS volume
ch->addNote( n );
Expand Down
54 changes: 53 additions & 1 deletion src/core/DataFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ const std::vector<DataFile::UpgradeMethod> DataFile::UPGRADE_METHODS = {
&DataFile::upgrade_1_1_91 , &DataFile::upgrade_1_2_0_rc3,
&DataFile::upgrade_1_3_0 , &DataFile::upgrade_noHiddenClipNames,
&DataFile::upgrade_automationNodes , &DataFile::upgrade_extendedNoteRange,
&DataFile::upgrade_defaultTripleOscillatorHQ
&DataFile::upgrade_defaultTripleOscillatorHQ,
&DataFile::upgrade_ppqn
};

// Vector of all versions that have upgrade routines.
Expand Down Expand Up @@ -1759,6 +1760,57 @@ void DataFile::upgrade_defaultTripleOscillatorHQ()
}


/** \brief Default PPQN has been changed from 48 to 480
*
* Older projects will be shortened by 10 times without this upgrade routine
*/
void DataFile::upgrade_ppqn()
{
const int scaleFactor = 10; // 48 to 480
auto scaleAttr = [scaleFactor] (QDomElement& e, const QString& name) {
e.setAttribute(name, e.attribute(name).toInt() * scaleFactor);
};

QDomNodeList aps = elementsByTagName("automationpattern");
for (int i = 0; i < aps.size(); ++i)
{
QDomElement ap = aps.item(i).toElement();
scaleAttr(ap, "pos");
scaleAttr(ap, "len");

QDomNodeList times = ap.elementsByTagName("time");
for (int j=0; j < times.size(); ++j)
{
QDomElement el = times.item(j).toElement();
scaleAttr(el, "pos");
}
}

QDomNodeList ps = elementsByTagName("pattern");
for (int i = 0; i < ps.size(); ++i)
{
QDomElement p = ps.item(i).toElement();
scaleAttr(p, "pos");

QDomNodeList notes = p.elementsByTagName("note");
for (int j=0; j < notes.size(); ++j)
{
QDomElement el = notes.item(j).toElement();
scaleAttr(el, "pos");
scaleAttr(el, "len");
}
}

QDomNodeList stcos = elementsByTagName("sampletco");
for (int i = 0; i < stcos.size(); ++i)
{
QDomElement stco = stcos.item(i).toElement();
scaleAttr(stco, "pos");
scaleAttr(stco, "len");
}
}


void DataFile::upgrade()
{
// Runs all necessary upgrade methods
Expand Down
10 changes: 6 additions & 4 deletions src/core/VstSyncController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,11 @@ VstSyncController::~VstSyncController()

void VstSyncController::setAbsolutePosition( double ticks )
{
const double ppqn = DefaultTicksPerBar / 4.0;
#ifdef VST_SNC_LATENCY
m_syncData->ppqPos = ( ( ticks + 0 ) / 48.0 ) - m_syncData->m_latency;
m_syncData->ppqPos = ( ( ticks + 0 ) / ppqn ) - m_syncData->m_latency;
#else
m_syncData->ppqPos = ( ( ticks + 0 ) / 48.0 );
m_syncData->ppqPos = ( ( ticks + 0 ) / ppqn );
#endif
}

Expand All @@ -111,9 +112,10 @@ void VstSyncController::setTempo( int newTempo )

void VstSyncController::startCycle( int startTick, int endTick )
{
const float ppqn = DefaultTicksPerBar / 4.0f;
m_syncData->isCycle = true;
m_syncData->cycleStart = startTick / (float)48;
m_syncData->cycleEnd = endTick / (float)48;
m_syncData->cycleStart = startTick / ppqn;
m_syncData->cycleEnd = endTick / ppqn;
}


Expand Down
4 changes: 2 additions & 2 deletions src/core/midi/MidiTime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,6 @@ double MidiTime::ticksToMilliseconds( tick_t ticks, bpm_t beatsPerMinute )

double MidiTime::ticksToMilliseconds(double ticks, bpm_t beatsPerMinute)
{
// 60 * 1000 / 48 = 1250
return ( ticks * 1250 ) / beatsPerMinute;
const double msOfTickAt1Bpm = 240000.0 / DefaultTicksPerBar;
return ( ticks * msOfTickAt1Bpm ) / beatsPerMinute;
}

0 comments on commit 7406e28

Please sign in to comment.