MuseScore/libmscore/midimapping.cpp

274 lines
11 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2002-2011 Werner Schweer
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
#include "score.h"
#include "instrument.h"
#include "part.h"
namespace Ms {
//---------------------------------------------------------
// rebuildMidiMapping
//---------------------------------------------------------
void MasterScore::rebuildMidiMapping()
{
removeDeletedMidiMapping();
int maxport = updateMidiMapping();
reorderMidiMapping();
masterScore()->setMidiPortCount(maxport);
}
//---------------------------------------------------------
// checkMidiMapping
// midi mapping is simple if all ports and channels
// don't decrease and don't have 'holes' except drum tracks
//---------------------------------------------------------
void MasterScore::checkMidiMapping()
{
isSimpleMidiMaping = true;
rebuildMidiMapping();
QList<bool> drum;
drum.reserve(_midiMapping.size());
for (Part* part : parts()) {
const InstrumentList* il = part->instruments();
for (auto i = il->begin(); i != il->end(); ++i) {
const Instrument* instr = i->second;
for (int j = 0; j < instr->channel().size(); ++j)
drum.append(instr->useDrumset());
}
}
int lastChannel = -1; // port*16+channel
int lastDrumPort = -1;
int index = 0;
for (MidiMapping m : _midiMapping) {
if (index >= drum.size())
break;
if (drum[index]) {
lastDrumPort++;
if (m.port != lastDrumPort) {
isSimpleMidiMaping = false;
return;
}
}
else {
lastChannel++;
if (lastChannel % 16 == 9)
lastChannel++;
int p = lastChannel / 16;
int c = lastChannel % 16;
if (m.port != p || m.channel != c) {
isSimpleMidiMaping = false;
return;
}
}
index++;
}
}
//---------------------------------------------------------
// getNextFreeMidiMapping
//---------------------------------------------------------
int MasterScore::getNextFreeMidiMapping(int p, int ch)
{
if (ch != -1 && p != -1)
return p*16+ch;
else if (ch != -1 && p == -1) {
for (int port = 0;; port++) {
if (!occupiedMidiChannels.contains(port*16+ch)) {
occupiedMidiChannels.insert(port*16+ch);
return port*16+ch;
}
}
}
else if (ch == -1 && p != -1) {
for (int channel = 0; channel < 16; channel++) {
if (channel != 9 && !occupiedMidiChannels.contains(p*16+channel)) {
occupiedMidiChannels.insert(p*16+channel);
return p*16+channel;
}
}
}
for (;;searchMidiMappingFrom++) {
if (searchMidiMappingFrom % 16 != 9 && !occupiedMidiChannels.contains(searchMidiMappingFrom)) {
occupiedMidiChannels.insert(searchMidiMappingFrom);
return searchMidiMappingFrom;
}
}
}
//---------------------------------------------------------
// getNextFreeDrumMidiMapping
//---------------------------------------------------------
int MasterScore::getNextFreeDrumMidiMapping()
{
for (int i = 0;; i++) {
if (!occupiedMidiChannels.contains(i*16+9)) {
occupiedMidiChannels.insert(i*16+9);
return i*16+9;
}
}
}
//---------------------------------------------------------
// reorderMidiMapping
// Set mappings in order you see in Add->Instruments
//---------------------------------------------------------
void MasterScore::reorderMidiMapping()
{
int sequenceNumber = 0;
for (Part* part : parts()) {
const InstrumentList* il = part->instruments();
for (auto i = il->begin(); i != il->end(); ++i) {
const Instrument* instr = i->second;
for (Channel* channel : instr->channel()) {
if (!(_midiMapping[sequenceNumber].part == part
&& _midiMapping[sequenceNumber].articulation == channel)) {
int shouldBe = channel->channel;
_midiMapping.swap(sequenceNumber, shouldBe);
_midiMapping[sequenceNumber].articulation->channel = sequenceNumber;
channel->channel = sequenceNumber;
_midiMapping[shouldBe].articulation->channel = shouldBe;
}
sequenceNumber++;
}
}
}
}
//---------------------------------------------------------
// removeDeletedMidiMapping
// Remove mappings to deleted instruments
//---------------------------------------------------------
void MasterScore::removeDeletedMidiMapping()
{
int removeOffset = 0;
int mappingSize = _midiMapping.size();
for (int index = 0; index < mappingSize; index++) {
Part* p = midiMapping(index)->part;
if (!parts().contains(p)) {
removeOffset++;
continue;
}
// Not all channels could exist
bool channelExists = false;
const InstrumentList* il = p->instruments();
for (auto i = il->begin(); i != il->end() && !channelExists; ++i) {
const Instrument* instr = i->second;
channelExists = (_midiMapping[index].articulation->channel != -1 && instr->channel().contains(_midiMapping[index].articulation)
&& !(_midiMapping[index].port == -1 && _midiMapping[index].channel == -1));
if (channelExists)
break;
}
if (!channelExists) {
removeOffset++;
continue;
}
// Let's do a left shift by 'removeOffset' items if necessary
if (index != 0 && removeOffset != 0) {
_midiMapping[index-removeOffset] = _midiMapping[index];
_midiMapping[index-removeOffset].articulation->channel -= removeOffset;
}
}
// We have 'removeOffset' deleted instruments, let's remove their mappings
for (int index = 0; index < removeOffset; index++)
_midiMapping.removeLast();
}
//---------------------------------------------------------
// updateMidiMapping
// Add mappings to new instruments and repair existing ones
//---------------------------------------------------------
int MasterScore::updateMidiMapping()
{
int maxport = 0;
occupiedMidiChannels.clear();
searchMidiMappingFrom = 0;
occupiedMidiChannels.reserve(_midiMapping.size()); // Bringing down the complexity of insertion to amortized O(1)
for(MidiMapping mm :_midiMapping) {
if (mm.port == -1 || mm.channel == -1)
continue;
occupiedMidiChannels.insert((int)(mm.port)*16+(int)mm.channel);
if (maxport < mm.port)
maxport = mm.port;
}
for (Part* part : parts()) {
const InstrumentList* il = part->instruments();
for (auto i = il->begin(); i != il->end(); ++i) {
const Instrument* instr = i->second;
bool drum = instr->useDrumset();
for (Channel* channel : instr->channel()) {
bool channelExists = false;
for (MidiMapping mapping: _midiMapping) {
if (channel == mapping.articulation && channel->channel != -1) {
channelExists = true;
break;
}
}
// Channel could already exist, but have unassigned port or channel. Repair and continue
if (channelExists) {
if (_midiMapping[channel->channel].port == -1) {
int nm = getNextFreeMidiMapping(-1, _midiMapping[channel->channel].channel);
_midiMapping[channel->channel].port = nm / 16;
}
else if (_midiMapping[channel->channel].channel == -1) {
if (drum) {
_midiMapping[channel->channel].port = getNextFreeDrumMidiMapping() / 16;
_midiMapping[channel->channel].channel = 9;
continue;
}
int nm = getNextFreeMidiMapping(_midiMapping[channel->channel].port);
_midiMapping[channel->channel].port = nm / 16;
_midiMapping[channel->channel].channel = nm % 16;
}
continue;
}
MidiMapping mm;
if (drum) {
mm.port = getNextFreeDrumMidiMapping() / 16;
mm.channel = 9;
}
else {
int nm = getNextFreeMidiMapping();
mm.port = nm / 16;
mm.channel = nm % 16;
}
if (mm.port > maxport)
maxport = mm.port;
mm.part = part;
mm.articulation = channel;
_midiMapping.append(mm);
channel->channel = _midiMapping.size()-1;
}
}
}
return maxport;
}
} // namespace Ms