Skip to content

Commit

Permalink
Merge pull request musescore#4978 from dmitrio95/rendermidi
Browse files Browse the repository at this point in the history
Implement partial MIDI rendering during playback
  • Loading branch information
anatoly-os committed May 5, 2019
2 parents dc11916 + e43c51a commit 1a6058f
Show file tree
Hide file tree
Showing 18 changed files with 756 additions and 301 deletions.
502 changes: 313 additions & 189 deletions libmscore/rendermidi.cpp

Large diffs are not rendered by default.

116 changes: 116 additions & 0 deletions libmscore/rendermidi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2019 Werner Schweer and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// 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; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================

#ifndef __RENDERMIDI_H__
#define __RENDERMIDI_H__

#include "fraction.h"
#include "measure.h"

namespace Ms {

class EventMap;
class MasterScore;
class Staff;
class SynthesizerState;

enum class DynamicsRenderMethod : signed char {
FIXED_MAX,
SEG_START,
SIMPLE
};

//---------------------------------------------------------
// RangeMap
/// Helper class to keep track of status of status of
/// certain parts of score or MIDI representation.
//---------------------------------------------------------

class RangeMap {
enum class Range { BEGIN, END };
std::map<int, Range> status;

public:
void setOccupied(int tick1, int tick2);
void setOccupied(std::pair<int, int> range) { setOccupied(range.first, range.second); }

int occupiedRangeEnd(int tick) const;

void clear() { status.clear(); }
};

//---------------------------------------------------------
// MidiRenderer
/// MIDI renderer for a score
//---------------------------------------------------------

class MidiRenderer {
MasterScore* score;
bool needUpdate = true;
int minChunkSize = 0;

public:
class Chunk {
int _tickOffset;
Measure* first;
Measure* last;

public:
Chunk(int tickOffset, Measure* fst, Measure* lst)
: _tickOffset(tickOffset), first(fst), last(lst) {}

Chunk() // "invalid chunk" constructor
: _tickOffset(0), first(nullptr), last(nullptr) {}

operator bool() const { return bool(first); }
int tickOffset() const { return _tickOffset; }
Measure* startMeasure() const { return first; }
Measure* endMeasure() const { return last ? last->nextMeasure() : nullptr; }
int tick1() const { return first->tick().ticks(); }
int tick2() const { return last ? last->endTick().ticks() : tick1(); }
int utick1() const { return tick1() + tickOffset(); }
int utick2() const { return tick2() + tickOffset(); }
};

private:
std::vector<Chunk> chunks;

void updateChunksPartition();
void updateState();

void renderStaffChunk(const Chunk&, EventMap* events, Staff*, DynamicsRenderMethod method, int cc);
void renderSpanners(const Chunk&, EventMap* events);
void renderMetronome(const Chunk&, EventMap* events);
void renderMetronome(EventMap* events, Measure* m, const Fraction& tickOffset);

public:
explicit MidiRenderer(MasterScore* s) : score(s) {}

void renderScore(EventMap* events, const SynthesizerState& synthState, bool metronome = true);
void renderChunk(const Chunk&, EventMap* events, const SynthesizerState& synthState, bool metronome = true);

void setScoreChanged() { needUpdate = true; }
void setMinChunkSize(int sizeMeasures) { minChunkSize = sizeMeasures; needUpdate = true; }

Chunk getChunkAt(int utick);
};

} // namespace Ms

#endif
74 changes: 65 additions & 9 deletions libmscore/repeatlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ RepeatSegment::RepeatSegment(RepeatSegment * const rs, Measure * const fromMeasu

void RepeatSegment::addMeasure(Measure * const m)
{
Q_ASSERT(measureList.empty() || measureList.back().first->nextMeasure() == m);
if (measureList.empty()) {
tick = m->tick().ticks();
}
Expand Down Expand Up @@ -153,14 +154,23 @@ RepeatList::RepeatList(Score* s)
idx2 = 0;
}

//---------------------------------------------------------
// ~RepeatList
//---------------------------------------------------------

RepeatList::~RepeatList()
{
qDeleteAll(*this);
}

//---------------------------------------------------------
// ticks
//---------------------------------------------------------

int RepeatList::ticks()
int RepeatList::ticks() const
{
if (length() > 0) {
RepeatSegment* s = last();
const RepeatSegment* s = last();
return s->utick + s->len();
}
return 0;
Expand All @@ -170,7 +180,24 @@ int RepeatList::ticks()
// update
//---------------------------------------------------------

void RepeatList::update()
void RepeatList::update(bool expand)
{
if (!_scoreChanged && expand == _expanded)
return;

if (expand)
unwind();
else
flatten();

_scoreChanged = false;
}

//---------------------------------------------------------
// updateTempo
//---------------------------------------------------------

void RepeatList::updateTempo()
{
const TempoMap* tl = _score->tempomap();

Expand Down Expand Up @@ -270,19 +297,48 @@ void RepeatList::dump() const
{
#if 0
qDebug("==Dump Repeat List:==");
foreach(const RepeatSegment* s, *this) {
for (const RepeatSegment* s : *this) {
qDebug("%p tick: %3d(%d) %3d(%d) len %d(%d) beats %f + %f", s,
s->utick / MScore::division,
s->utick / MScore::division / 4,
s->tick / MScore::division,
s->tick / MScore::division / 4,
s->len / MScore::division,
s->len / MScore::division / 4,
s->len() / MScore::division,
s->len() / MScore::division / 4,
s->utime, s->timeOffset);
}
#endif
}

//---------------------------------------------------------
// flatten
/// Make this repeat list flat (don't expand repeats)
//---------------------------------------------------------

void RepeatList::flatten()
{
qDeleteAll(*this);
clear();

Measure* m = _score->firstMeasure();
if (!m)
return;

RepeatSegment* s = new RepeatSegment;
s->tick = 0;
s->utick = 0;
s->utime = 0.0;
s->timeOffset = 0.0;
do {
s->addMeasure(m);
m = m->nextMeasure();
}
while (m);
push_back(s);

_expanded = false;
}

//---------------------------------------------------------
// unwind
// implements:
Expand Down Expand Up @@ -342,7 +398,8 @@ void RepeatList::unwind()
}
}

update();
updateTempo();
_expanded = true;
dump();
}

Expand Down Expand Up @@ -413,7 +470,6 @@ std::map<Volta*, Measure*>::const_iterator RepeatList::searchVolta(Measure * con
//---------------------------------------------------------
// unwindSection
// unwinds from sectionStartMeasure through sectionEndMeasure
// appends repeat segments using rs
//---------------------------------------------------------

void RepeatList::unwindSection(Measure* const sectionStartMeasure, Measure* const sectionEndMeasure)
Expand All @@ -425,7 +481,7 @@ void RepeatList::unwindSection(Measure* const sectionStartMeasure, Measure* cons
return;
}

rs = nullptr; // no measures to be played yet
RepeatSegment* rs = nullptr; // no measures to be played yet

Measure* prevMeasure = nullptr; // the last processed measure that is part of this RepeatSegment
Measure* currentMeasure = sectionStartMeasure; // the measure to be processed/evaluated
Expand Down
20 changes: 16 additions & 4 deletions libmscore/repeatlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ class RepeatSegment {
int len() const;
int playbackCount(Measure * const) const;

Measure* firstMeasure() const { return measureList.empty() ? nullptr : measureList.front().first; }
Measure* lastMeasure() const { return measureList.empty() ? nullptr : measureList.back().first; }

friend class RepeatList;
};

Expand All @@ -52,7 +55,9 @@ class RepeatList: public QList<RepeatSegment*>
Score* _score;
mutable unsigned idx1, idx2; // cached values

RepeatSegment* rs; // tmp value during unwind()
bool _expanded = false;
bool _scoreChanged = true;

std::map<Volta*, Measure*> _voltaRanges; // open volta possibly ends past the end of its spanner, used during unwind
std::set<Jump*> _jumpsTaken; // take the jumps only once, so track them during unwind

Expand All @@ -63,16 +68,23 @@ class RepeatList: public QList<RepeatSegment*>
int findStartFromRepeatCount(Measure * const startFrom) const;
bool isFinalPlaythrough(Measure * const measure, QList<RepeatSegment*>::const_iterator repeatSegmentIt) const;

void unwind();
void flatten();

public:
RepeatList(Score* s);
void unwind();
RepeatList(const RepeatList&) = delete;
RepeatList& operator=(const RepeatList&) = delete;
~RepeatList();
void update(bool expand);
void setScoreChanged() { _scoreChanged = true; }
int utick2tick(int tick) const;
int tick2utick(int tick) const;
void dump() const;
int utime2utick(qreal) const;
qreal utick2utime(int) const;
void update();
int ticks();
void updateTempo();
int ticks() const;
};


Expand Down
Loading

0 comments on commit 1a6058f

Please sign in to comment.