Merge pull request #16265 from handrok/export-midi_channels
midi-export: each string has its own channel
This commit is contained in:
commit
76cb0adf9c
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-3.0-only
|
||||
* MuseScore-CLA-applies
|
||||
*
|
||||
|
@ -492,8 +492,8 @@ static void renderHarmony(EventMap* events, Measure const* m, Harmony* h, int ti
|
|||
}
|
||||
}
|
||||
|
||||
static void collectGraceBeforeChordEvents(Chord* chord, EventMap* events, int channel, double veloMultiplier,
|
||||
Staff* st, int tickOffset, PitchWheelRenderer& pitchWheelRenderer)
|
||||
void MidiRenderer::collectGraceBeforeChordEvents(Chord* chord, EventMap* events, double veloMultiplier,
|
||||
Staff* st, int tickOffset, PitchWheelRenderer& pitchWheelRenderer)
|
||||
{
|
||||
// calculate offset for grace notes here
|
||||
const auto& grChords = chord->graceNotesBefore();
|
||||
|
@ -515,6 +515,7 @@ static void collectGraceBeforeChordEvents(Chord* chord, EventMap* events, int ch
|
|||
int currentBeaforeBeatNote = 0;
|
||||
for (Chord* c : chord->graceNotesBefore()) {
|
||||
for (const Note* note : c->notes()) {
|
||||
int channel = getChannel(chord->part()->instrument(chord->tick()), note);
|
||||
if (note->noteType() == NoteType::ACCIACCATURA) {
|
||||
collectNote(events, channel, note, veloMultiplier, tickOffset, st,
|
||||
pitchWheelRenderer,
|
||||
|
@ -582,10 +583,6 @@ void MidiRenderer::doCollectMeasureEvents(EventMap* events, Measure const* m, co
|
|||
Chord* chord = toChord(cr);
|
||||
|
||||
Instrument* instr = st1->part()->instrument(tick);
|
||||
int subchannel = chord->upNote()->subchannel();
|
||||
int channel = instr->channel(subchannel)->channel();
|
||||
|
||||
events->registerChannel(channel);
|
||||
|
||||
// Get a velocity multiplier
|
||||
double veloMultiplier = 1;
|
||||
|
@ -598,15 +595,17 @@ void MidiRenderer::doCollectMeasureEvents(EventMap* events, Measure const* m, co
|
|||
//
|
||||
// Add normal note events
|
||||
//
|
||||
collectGraceBeforeChordEvents(chord, events, channel, veloMultiplier, st1, tickOffset, pitchWheelRenderer);
|
||||
collectGraceBeforeChordEvents(chord, events, veloMultiplier, st1, tickOffset, pitchWheelRenderer);
|
||||
|
||||
for (const Note* note : chord->notes()) {
|
||||
int channel = getChannel(instr, note);
|
||||
collectNote(events, channel, note, veloMultiplier, tickOffset, st1, pitchWheelRenderer);
|
||||
}
|
||||
|
||||
if (!graceNotesMerged(chord)) {
|
||||
for (Chord* c : chord->graceNotesAfter()) {
|
||||
for (const Note* note : c->notes()) {
|
||||
int channel = getChannel(instr, note);
|
||||
collectNote(events, channel, note, veloMultiplier, tickOffset, st1, pitchWheelRenderer);
|
||||
}
|
||||
}
|
||||
|
@ -639,28 +638,35 @@ void MidiRenderer::collectMeasureEvents(EventMap* events, Measure const* m, cons
|
|||
|
||||
void MidiRenderer::renderStaff(EventMap* events, const Staff* staff, PitchWheelRenderer& pitchWheelRenderer)
|
||||
{
|
||||
Measure const* const start = score->firstMeasure();
|
||||
|
||||
Measure const* lastMeasure = nullptr;
|
||||
|
||||
for (Measure const* m = start; m; m = m->nextMeasure()) {
|
||||
staff_idx_t staffIdx = staff->idx();
|
||||
if (m->isMeasureRepeatGroup(staffIdx)) {
|
||||
MeasureRepeat* mr = m->measureRepeatElement(staffIdx);
|
||||
Measure const* playMeasure = lastMeasure;
|
||||
if (!playMeasure || !mr) {
|
||||
continue;
|
||||
}
|
||||
const RepeatList& repeatList = score->repeatList();
|
||||
|
||||
for (int i = m->measureRepeatCount(staffIdx); i < mr->numMeasures() && playMeasure->prevMeasure(); ++i) {
|
||||
playMeasure = playMeasure->prevMeasure();
|
||||
}
|
||||
for (const RepeatSegment* rs : repeatList) {
|
||||
const int tickOffset = rs->utick - rs->tick;
|
||||
|
||||
int offset = (m->tick() - playMeasure->tick()).ticks();
|
||||
collectMeasureEvents(events, playMeasure, staff, offset, pitchWheelRenderer);
|
||||
} else {
|
||||
lastMeasure = m;
|
||||
collectMeasureEvents(events, lastMeasure, staff, 0, pitchWheelRenderer);
|
||||
Measure const* const start = rs->firstMeasure();
|
||||
Measure const* const end = rs->lastMeasure()->nextMeasure();
|
||||
|
||||
for (Measure const* m = start; m; m = m->nextMeasure()) {
|
||||
staff_idx_t staffIdx = staff->idx();
|
||||
if (m->isMeasureRepeatGroup(staffIdx)) {
|
||||
MeasureRepeat* mr = m->measureRepeatElement(staffIdx);
|
||||
Measure const* playMeasure = lastMeasure;
|
||||
if (!playMeasure || !mr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = m->measureRepeatCount(staffIdx); i < mr->numMeasures() && playMeasure->prevMeasure(); ++i) {
|
||||
playMeasure = playMeasure->prevMeasure();
|
||||
}
|
||||
|
||||
int offset = (m->tick() - playMeasure->tick()).ticks();
|
||||
collectMeasureEvents(events, playMeasure, staff, tickOffset + offset, pitchWheelRenderer);
|
||||
} else {
|
||||
lastMeasure = m;
|
||||
collectMeasureEvents(events, lastMeasure, staff, tickOffset, pitchWheelRenderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -671,83 +677,90 @@ void MidiRenderer::renderStaff(EventMap* events, const Staff* staff, PitchWheelR
|
|||
|
||||
void MidiRenderer::renderSpanners(EventMap* events, PitchWheelRenderer& pitchWheelRenderer)
|
||||
{
|
||||
std::map<int, std::vector<std::pair<int, std::pair<bool, int> > > > channelPedalEvents;
|
||||
for (const auto& sp : score->spannerMap().map()) {
|
||||
Spanner* s = sp.second;
|
||||
|
||||
int staff = static_cast<int>(s->staffIdx());
|
||||
int idx = s->staff()->channel(s->tick(), 0);
|
||||
int channel = s->part()->instrument(s->tick())->channel(idx)->channel();
|
||||
|
||||
if (s->isPedal()) {
|
||||
channelPedalEvents.insert({ channel, std::vector<std::pair<int, std::pair<bool, int> > >() });
|
||||
std::vector<std::pair<int, std::pair<bool, int> > > pedalEventList = channelPedalEvents.at(channel);
|
||||
std::pair<int, std::pair<bool, int> > lastEvent;
|
||||
|
||||
if (!pedalEventList.empty()) {
|
||||
lastEvent = pedalEventList.back();
|
||||
} else {
|
||||
lastEvent = std::pair<int, std::pair<bool, int> >(0, std::pair<bool, int>(true, staff));
|
||||
}
|
||||
|
||||
int st = s->tick().ticks();
|
||||
|
||||
if (lastEvent.second.first == false && lastEvent.first >= (st + 2)) {
|
||||
channelPedalEvents.at(channel).pop_back();
|
||||
channelPedalEvents.at(channel).push_back(std::pair<int,
|
||||
std::pair<bool,
|
||||
int> >(st + (2 - MScore::pedalEventsMinTicks),
|
||||
std::pair<bool, int>(false, staff)));
|
||||
}
|
||||
int a = st + 2;
|
||||
channelPedalEvents.at(channel).push_back(std::pair<int, std::pair<bool, int> >(a, std::pair<bool, int>(true, staff)));
|
||||
|
||||
int t = s->tick2().ticks() + (2 - MScore::pedalEventsMinTicks);
|
||||
const RepeatSegment& lastRepeat = *score->repeatList().back();
|
||||
if (t > lastRepeat.utick + lastRepeat.len()) {
|
||||
t = lastRepeat.utick + lastRepeat.len();
|
||||
}
|
||||
channelPedalEvents.at(channel).push_back(std::pair<int, std::pair<bool, int> >(t, std::pair<bool, int>(false, staff)));
|
||||
} else if (s->isVibrato()) {
|
||||
int stick = s->tick().ticks();
|
||||
int etick = s->tick2().ticks();
|
||||
|
||||
// from start to end of trill, send bend events at regular interval
|
||||
Vibrato* t = toVibrato(s);
|
||||
// guitar vibrato, up only
|
||||
int spitch = 0; // 1/8 (100 is a semitone)
|
||||
int epitch = 12;
|
||||
if (t->vibratoType() == VibratoType::GUITAR_VIBRATO_WIDE) {
|
||||
spitch = 0; // 1/4
|
||||
epitch = 25;
|
||||
}
|
||||
// vibrato with whammy bar up and down
|
||||
else if (t->vibratoType() == VibratoType::VIBRATO_SAWTOOTH_WIDE) {
|
||||
spitch = -25; // 1/16
|
||||
epitch = 25;
|
||||
} else if (t->vibratoType() == VibratoType::VIBRATO_SAWTOOTH) {
|
||||
spitch = -12;
|
||||
epitch = 12;
|
||||
}
|
||||
|
||||
collectVibrato(channel, stick, etick, spitch, epitch, pitchWheelRenderer);
|
||||
const auto& channels = _context.channels->channelsMap[channel];
|
||||
if (channels.empty()) {
|
||||
doRenderSpanners(events, s, channel, pitchWheelRenderer);
|
||||
} else {
|
||||
continue;
|
||||
for (const auto& channel : channels) {
|
||||
doRenderSpanners(events, s, channel.second, pitchWheelRenderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& pedalEvents : channelPedalEvents) {
|
||||
int channel = pedalEvents.first;
|
||||
for (const auto& pe : pedalEvents.second) {
|
||||
NPlayEvent event;
|
||||
if (pe.second.first == true) {
|
||||
event = NPlayEvent(ME_CONTROLLER, static_cast<uint8_t>(channel), CTRL_SUSTAIN, 127);
|
||||
} else {
|
||||
event = NPlayEvent(ME_CONTROLLER, static_cast<uint8_t>(channel), CTRL_SUSTAIN, 0);
|
||||
}
|
||||
event.setOriginatingStaff(pe.second.second);
|
||||
events->insert(std::pair<int, NPlayEvent>(pe.first, event));
|
||||
void MidiRenderer::doRenderSpanners(EventMap* events, Spanner* s, uint32_t channel, PitchWheelRenderer& pitchWheelRenderer)
|
||||
{
|
||||
std::vector<std::pair<int, std::pair<bool, int> > > pedalEventList;
|
||||
|
||||
int staff = static_cast<int>(s->staffIdx());
|
||||
|
||||
if (s->isPedal()) {
|
||||
std::pair<int, std::pair<bool, int> > lastEvent;
|
||||
|
||||
if (!pedalEventList.empty()) {
|
||||
lastEvent = pedalEventList.back();
|
||||
} else {
|
||||
lastEvent = std::pair<int, std::pair<bool, int> >(0, std::pair<bool, int>(true, staff));
|
||||
}
|
||||
|
||||
int st = s->tick().ticks();
|
||||
|
||||
if (lastEvent.second.first == false && lastEvent.first >= (st + 2)) {
|
||||
pedalEventList.pop_back();
|
||||
pedalEventList.push_back(std::pair<int,
|
||||
std::pair<bool,
|
||||
int> >(st + (2 - MScore::pedalEventsMinTicks),
|
||||
std::pair<bool, int>(false, staff)));
|
||||
}
|
||||
int a = st + 2;
|
||||
pedalEventList.push_back(std::pair<int, std::pair<bool, int> >(a, std::pair<bool, int>(true, staff)));
|
||||
|
||||
int t = s->tick2().ticks() + (2 - MScore::pedalEventsMinTicks);
|
||||
const RepeatSegment& lastRepeat = *score->repeatList().back();
|
||||
if (t > lastRepeat.utick + lastRepeat.len()) {
|
||||
t = lastRepeat.utick + lastRepeat.len();
|
||||
}
|
||||
pedalEventList.push_back(std::pair<int, std::pair<bool, int> >(t, std::pair<bool, int>(false, staff)));
|
||||
} else if (s->isVibrato()) {
|
||||
int stick = s->tick().ticks();
|
||||
int etick = s->tick2().ticks();
|
||||
|
||||
// from start to end of trill, send bend events at regular interval
|
||||
Vibrato* t = toVibrato(s);
|
||||
// guitar vibrato, up only
|
||||
int spitch = 0; // 1/8 (100 is a semitone)
|
||||
int epitch = 12;
|
||||
if (t->vibratoType() == VibratoType::GUITAR_VIBRATO_WIDE) {
|
||||
spitch = 0; // 1/4
|
||||
epitch = 25;
|
||||
}
|
||||
// vibrato with whammy bar up and down
|
||||
else if (t->vibratoType() == VibratoType::VIBRATO_SAWTOOTH_WIDE) {
|
||||
spitch = -25; // 1/16
|
||||
epitch = 25;
|
||||
} else if (t->vibratoType() == VibratoType::VIBRATO_SAWTOOTH) {
|
||||
spitch = -12;
|
||||
epitch = 12;
|
||||
}
|
||||
|
||||
collectVibrato(channel, stick, etick, spitch, epitch, pitchWheelRenderer);
|
||||
}
|
||||
|
||||
for (const auto& pe : pedalEventList) {
|
||||
NPlayEvent event;
|
||||
if (pe.second.first == true) {
|
||||
event = NPlayEvent(ME_CONTROLLER, static_cast<uint8_t>(channel), CTRL_SUSTAIN, 127);
|
||||
} else {
|
||||
event = NPlayEvent(ME_CONTROLLER, static_cast<uint8_t>(channel), CTRL_SUSTAIN, 0);
|
||||
}
|
||||
event.setOriginatingStaff(pe.second.second);
|
||||
events->insert(std::pair<int, NPlayEvent>(pe.first, event));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -920,6 +933,7 @@ void MidiRenderer::renderMetronome(EventMap* events, Measure const* m)
|
|||
|
||||
void MidiRenderer::renderScore(EventMap* events, const Context& ctx)
|
||||
{
|
||||
_context = ctx;
|
||||
PitchWheelRenderer pitchWheelRender(wheelSpec);
|
||||
|
||||
score->updateSwing();
|
||||
|
@ -970,47 +984,38 @@ void MidiRenderer::renderScore(EventMap* events, const Context& ctx)
|
|||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// RangeMap::setOccupied
|
||||
//---------------------------------------------------------
|
||||
|
||||
void RangeMap::setOccupied(int tick1, int tick2)
|
||||
uint32_t MidiRenderer::getChannel(const Instrument* instr, const Note* note)
|
||||
{
|
||||
auto it1 = status.upper_bound(tick1);
|
||||
const bool beforeBegin = (it1 == status.begin());
|
||||
if (beforeBegin || (--it1)->second != Range::BEGIN) {
|
||||
if (!beforeBegin && it1->first == tick1) {
|
||||
status.erase(it1);
|
||||
int subchannel = note->subchannel();
|
||||
int channel = instr->channel(subchannel)->channel();
|
||||
|
||||
if (!_context.eachStringHasChannel || !instr->hasStrings()) {
|
||||
return channel;
|
||||
}
|
||||
|
||||
return _context.channels->getChannel(channel, note->string());
|
||||
}
|
||||
|
||||
uint32_t MidiRenderer::ChannelLookup::getChannel(uint32_t instrumentChannel, int32_t string)
|
||||
{
|
||||
auto& channelsForString = channelsMap[instrumentChannel];
|
||||
|
||||
if (string == -1) {
|
||||
auto channelIt = channelsForString.find(string);
|
||||
if (channelIt != channelsForString.end()) {
|
||||
return channelIt->second;
|
||||
} else {
|
||||
status.insert(std::make_pair(tick1, Range::BEGIN));
|
||||
channelsForString[string] = maxChannel;
|
||||
return maxChannel++;
|
||||
}
|
||||
}
|
||||
|
||||
const auto it2 = status.lower_bound(tick2);
|
||||
const bool afterEnd = (it2 == status.end());
|
||||
if (afterEnd || it2->second != Range::END) {
|
||||
if (!afterEnd && it2->first == tick2) {
|
||||
status.erase(it2);
|
||||
} else {
|
||||
status.insert(std::make_pair(tick2, Range::END));
|
||||
}
|
||||
auto channelIt = channelsForString.find(string);
|
||||
if (channelIt != channelsForString.end()) {
|
||||
return channelIt->second;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// RangeMap::occupiedRangeEnd
|
||||
//---------------------------------------------------------
|
||||
|
||||
int RangeMap::occupiedRangeEnd(int tick) const
|
||||
{
|
||||
const auto it = status.upper_bound(tick);
|
||||
if (it == status.begin()) {
|
||||
return tick;
|
||||
}
|
||||
const int rangeEnd = (it == status.end()) ? tick : it->first;
|
||||
if (it->second == Range::END) {
|
||||
return rangeEnd;
|
||||
}
|
||||
return tick;
|
||||
channelsForString.insert({ string, maxChannel });
|
||||
return maxChannel++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
#ifndef __RENDERMIDI_H__
|
||||
#define __RENDERMIDI_H__
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "libmscore/instrument.h"
|
||||
#include "libmscore/measure.h"
|
||||
#include "libmscore/synthesizerstate.h"
|
||||
#include "pitchwheelrenderer.h"
|
||||
|
@ -33,28 +36,6 @@ class MasterScore;
|
|||
class Staff;
|
||||
class SynthesizerState;
|
||||
|
||||
//---------------------------------------------------------
|
||||
// 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
|
||||
|
@ -63,10 +44,19 @@ public:
|
|||
class MidiRenderer
|
||||
{
|
||||
public:
|
||||
//! @brief helper structure to find channel in a case eachStringHasChannel = true
|
||||
struct ChannelLookup {
|
||||
std::map<int, std::map<int, int> > channelsMap;
|
||||
uint32_t maxChannel = 0;
|
||||
uint32_t getChannel(uint32_t instrumentChannel, int32_t string);
|
||||
};
|
||||
|
||||
struct Context
|
||||
{
|
||||
SynthesizerState synthState;
|
||||
bool metronome = true;
|
||||
bool eachStringHasChannel = false;//!to better display the guitar instrument, each string has its own channel
|
||||
std::shared_ptr<ChannelLookup> channels = std::make_shared<ChannelLookup>();
|
||||
|
||||
Context() {}
|
||||
};
|
||||
|
@ -79,11 +69,10 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
Score* score = nullptr;
|
||||
|
||||
void renderStaff(EventMap* events, const Staff* sctx, PitchWheelRenderer& pitchWheelRenderer);
|
||||
|
||||
void renderSpanners(EventMap* events, PitchWheelRenderer& pitchWheelRenderer);
|
||||
void doRenderSpanners(EventMap* events, Spanner* s, uint32_t channel, PitchWheelRenderer& pitchWheelRenderer);
|
||||
|
||||
void renderMetronome(EventMap* events);
|
||||
void renderMetronome(EventMap* events, Measure const* m);
|
||||
|
@ -92,6 +81,14 @@ private:
|
|||
PitchWheelRenderer& pitchWheelRenderer);
|
||||
void doCollectMeasureEvents(EventMap* events, Measure const* m, const Staff* sctx, int tickOffset,
|
||||
PitchWheelRenderer& pitchWheelRenderer);
|
||||
void collectGraceBeforeChordEvents(Chord* chord, EventMap* events, double veloMultiplier, Staff* st, int tickOffset,
|
||||
PitchWheelRenderer& pitchWheelRenderer);
|
||||
|
||||
uint32_t getChannel(const Instrument* instr, const Note* note);
|
||||
|
||||
Score* score = nullptr;
|
||||
|
||||
Context _context;
|
||||
};
|
||||
} // namespace mu::engraving
|
||||
|
||||
|
|
|
@ -1425,23 +1425,10 @@ void Score::createPlayEvents(Measure const* start, Measure const* const end)
|
|||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// renderMidi
|
||||
// export score to event list
|
||||
//---------------------------------------------------------
|
||||
|
||||
void Score::renderMidi(EventMap* events, const SynthesizerState& synthState)
|
||||
{
|
||||
renderMidi(events, true, MScore::playRepeats, synthState);
|
||||
}
|
||||
|
||||
void Score::renderMidi(EventMap* events, bool metronome, bool expandRepeats, const SynthesizerState& synthState)
|
||||
void Score::renderMidi(EventMap* events, const MidiRenderer::Context& ctx, bool expandRepeats)
|
||||
{
|
||||
bool expandRepeatsBackup = masterScore()->expandRepeats();
|
||||
masterScore()->setExpandRepeats(expandRepeats);
|
||||
MidiRenderer::Context ctx;
|
||||
ctx.synthState = synthState;
|
||||
ctx.metronome = metronome;
|
||||
MidiRenderer(this).renderScore(events, ctx);
|
||||
masterScore()->setExpandRepeats(expandRepeatsBackup);
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "async/channel.h"
|
||||
#include "io/iodevice.h"
|
||||
#include "types/ret.h"
|
||||
#include "compat/midi/midirender.h"
|
||||
|
||||
#include "modularity/ioc.h"
|
||||
#include "draw/iimageprovider.h"
|
||||
|
@ -935,8 +936,7 @@ public:
|
|||
bool pasteStaff(XmlReader&, Segment* dst, staff_idx_t staffIdx, Fraction scale = Fraction(1, 1));
|
||||
void readAddConnector(ConnectorInfoReader* info, bool pasteMode) override;
|
||||
void pasteSymbols(XmlReader& e, ChordRest* dst);
|
||||
void renderMidi(EventMap* events, const SynthesizerState& synthState);
|
||||
void renderMidi(EventMap* events, bool metronome, bool expandRepeats, const SynthesizerState& synthState);
|
||||
void renderMidi(EventMap* events, const MidiRenderer::Context& ctx, bool expandRepeats);
|
||||
|
||||
BeatType tick2beatType(const Fraction& tick) const;
|
||||
|
||||
|
|
|
@ -229,7 +229,11 @@ bool ExportMidi::write(QIODevice* device, bool midiExpandRepeats, bool exportRPN
|
|||
}
|
||||
|
||||
EventMap events;
|
||||
m_score->renderMidi(&events, false, midiExpandRepeats, synthState);
|
||||
MidiRenderer::Context ctx;
|
||||
ctx.eachStringHasChannel = false;
|
||||
ctx.metronome = false;
|
||||
ctx.synthState = synthState;
|
||||
m_score->renderMidi(&events, ctx, true);
|
||||
|
||||
m_pauseMap.calculate(m_score);
|
||||
writeHeader();
|
||||
|
|
|
@ -563,7 +563,11 @@ void TestMidi::events()
|
|||
EventMap events;
|
||||
// a temporary, uninitialized synth state so we can render the midi - should fall back correctly
|
||||
SynthesizerState ss;
|
||||
score->renderMidi(&events, ss);
|
||||
MidiRenderer::Context ctx;
|
||||
ctx.eachStringHasChannel = false;
|
||||
ctx.metronome = true;
|
||||
ctx.synthState = ss;
|
||||
score->renderMidi(&events, ctx, true);
|
||||
qDebug() << "Opened score " << readFile;
|
||||
QFile filehandler(writeFile);
|
||||
filehandler.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
|
|
Loading…
Reference in a new issue