MuseScore/zerberus/zerberus.cpp
Andres Fernandez de Prado 33dff96a20 This commit contains changes required for MuseScore to compile under MSVC with no warnings.
This commit contains changes required for MuseScore to compile under MSVC with no warnings.

MuseScore is being compiled with the /W4 setting (warning level 4), which is similar to -wall -wextra on clang. This generates lots of warnings on MSVC, mainly for non-standard constructs and for constructs which might be bugs or might lead to bugs.

Most warnings are in the following categories:
- Name hiding: a variable hides a variable with the same name on a larger scope (or a field, or a function parameter). This can easily lead to bugs, and it is a best practice to avoid hiding variable names (see recommendation ES.12 in the C++ Core Guidelines by Stroustrop & Sutter (http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-reuse : ES.12: Do not reuse names in nested scopes)
- Narrowing conversion: a numeric conversion results in loss of significant digits (for example, double -> float). The general recommendation is to use a cast to indicate this is designed behaviour.
- Unreachable code: in several instances, there is unreachable code. The unreachable code is commented out.
- (Potentially) uninitialized local variable. Just initialized the vars.
- foreach(,) -> for(:): this does not generate a warning per-se (only a few of these generate warnings due to name hiding), but changed in keeping with "MuseScore Coding Rules" (https://musescore.org/en/handbook/musescore-coding-rules#Loops), which tells explicitly "Use C++11's "for" instead of Qt's "foreach":" ... "If you happen to be fixing some code and see a "foreach", please change that loop into a "for"."

Most changes are in the categories indicated above. The next listing shows detailed changes for files which are *not* of the aforementioned types.

- all.h: Disable warning C4127 (conditional expression is constant - generated in Qt header file qvector.h)
- awl/aslider.h: unreachable code.
- awl/knob.cpp: name hiding
- awl/mslider.cpp: name hiding
- awl/slider.cpp: name hiding
- bww2mxml/parser.cpp: name hiding
- effects/compressor/compressor.cpp: narrowing conversion
- effects/zita1/zitagui.cpp: name hiding
- fluid/fluid.cpp: foreach replacement. Name hiding.
- fluid/mod.cpp: name hiding.
- fluid/sfont.cpp: foreach replacement. Name hiding. Initialize vars.
- fluid/voice.cpp: Name hiding.
- libmscore/accidental.cpp: Name hiding.
- libmscore/ambitus.cpp: Initialize vars.
- libmscore/barline.cpp: Name hiding. Unreachable code.
- libmscore/beam.cpp: Name hiding.
- libmscore/chordrest.cpp: Unreachable code.
- libmscore/scorefile.cpp: Name hiding.
- manual/genManual.cpp: Name hiding. foreach replacement.
- midi/midifile.cpp: Name hiding. Unreachable code.
- omr/importpdf.cpp: Name hiding. foreach replacement.
- omr/omr.cpp: Name hiding. foreach replacement.
- omr/omrpage.cpp: Name hiding. foreach replacement.
- omr/omrview.cpp: Name hiding. foreach replacement.
- synthesizer/event.cpp: Unreachable code.
- zerberus\channel.cpp: Narrowing conversion.
- zerberus\instrument.cpp: Name hiding.
- zerberus\sfz.cpp: Name hiding.
- zerberus\voice.h: Suppress warning C4201: "nonstandard extension used: nameless struct/union"
- zerberus\zerberus.cpp: Name hiding. Unreferenced parameter.
- zerberus\zerberusgui.cpp: Name hiding.
2018-08-03 09:15:42 +02:00

484 lines
15 KiB
C++

//=============================================================================
// Zerberus
// Zample player
//
// Copyright (C) 2013 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 "mscore/preferences.h"
#include "synthesizer/event.h"
#include "synthesizer/midipatch.h"
#include "zerberus.h"
#include "zerberusgui.h"
#include "voice.h"
#include "channel.h"
#include "instrument.h"
#include "zone.h"
#include <stdio.h>
bool Zerberus::initialized = false;
// instruments can be shared between several zerberus instances
std::list<ZInstrument*> Zerberus::globalInstruments;
//---------------------------------------------------------
// createZerberus
//---------------------------------------------------------
Ms::Synthesizer* createZerberus()
{
return new Zerberus();
}
//---------------------------------------------------------
// Zerberus
//---------------------------------------------------------
Zerberus::Zerberus()
: Synthesizer()
{
if (!initialized) {
initialized = true;
Voice::init();
}
freeVoices.init(this);
for (int i = 0; i < MAX_CHANNEL; ++i)
_channel[i] = new Channel(this, i);
busy = true; // no sf loaded yet
}
//---------------------------------------------------------
// ~Zerberus
//---------------------------------------------------------
Zerberus::~Zerberus()
{
busy = true;
while (!instruments.empty()) {
auto i = instruments.front();
auto it = instruments.begin();
instruments.erase(it);
i->setRefCount(i->refCount() - 1);
if (i->refCount() <= 0) {
delete i;
auto it1 = find(globalInstruments.begin(), globalInstruments.end(), i);
if (it1 != globalInstruments.end())
globalInstruments.erase(it1);
}
}
for (Channel* c : _channel)
delete c;
}
//---------------------------------------------------------
// programChange
//---------------------------------------------------------
void Zerberus::programChange(int channel, int program)
{
qDebug("Zerberus programChange %d %d", channel, program);
}
//---------------------------------------------------------
// trigger
// gui
//---------------------------------------------------------
void Zerberus::trigger(Channel* channel, int key, int velo, Trigger trigger, int cc, int ccVal, double durSinceNoteOn)
{
ZInstrument* i = channel->instrument();
double random = (double) rand() / (double) RAND_MAX;
for (Zone* z : i->zones()) {
if (z->match(channel, key, velo, trigger, random, cc, ccVal)) {
//
// handle offBy voices
//
if (z->group) {
for (Voice* v = activeVoices; v; v = v->next()) {
if (v->offBy() == z->group) {
if (v->offMode() == OffMode::FAST)
v->stop(1);
else
v->stop();
}
}
}
if (freeVoices.empty()) {
qDebug("Zerberus: out of voices...");
return;
}
Voice* voice = freeVoices.pop();
Q_ASSERT(voice->isOff());
voice->start(channel, key, velo, z, durSinceNoteOn);
voice->setNext(activeVoices);
activeVoices = voice;
}
}
}
//---------------------------------------------------------
// processNoteOff
//---------------------------------------------------------
void Zerberus::processNoteOff(Channel* cp, int key)
{
for (Voice* v = activeVoices; v; v = v->next()) {
if ((v->channel() == cp)
&& (v->key() == key)
&& (v->loopMode() != LoopMode::ONE_SHOT)
) {
if (cp->sustain() < 0x40 && !v->isStopped()) {
v->stop();
double durSinceNoteOn = v->getSamplesSinceStart() / sampleRate();
trigger(cp, key, v->velocity(), Trigger::RELEASE, -1, -1, durSinceNoteOn);
}
else {
if (v->isPlaying())
v->sustained();
}
}
}
}
//---------------------------------------------------------
// processNoteOn
//---------------------------------------------------------
void Zerberus::processNoteOn(Channel* cp, int key, int velo)
{
for (Voice* v = activeVoices; v; v = v->next()) {
if (v->channel() == cp && v->key() == key) {
if (v->isSustained()) {
// if (v->isPlaying())
// printf("retrigger (stop) %p\n", v);
v->stop(100); // fast stop
}
}
}
trigger(cp, key, velo, Trigger::ATTACK, -1, -1, 0);
}
//---------------------------------------------------------
// process
//---------------------------------------------------------
void Zerberus::play(const Ms::PlayEvent& event)
{
if (busy)
return;
if (event.channel() >= MAX_CHANNEL)
return;
Channel* cp = _channel[int(event.channel())];
if (cp->instrument() == 0) {
// qDebug("Zerberus::play(): no instrument for channel %d", event.channel());
return;
}
switch(event.type()) {
case Ms::ME_NOTEOFF:
processNoteOff(cp, event.dataA());
break;
case Ms::ME_NOTEON: {
int key = event.dataA();
int vel = event.dataB();
if (vel)
processNoteOn(cp, key, vel);
else
processNoteOff(cp, key);
}
break;
case Ms::ME_CONTROLLER:
cp->controller(event.dataA(), event.dataB());
trigger(cp, -1, -1, Trigger::CC, event.dataA(), event.dataB(), 0);
break;
default:
qDebug("Zerberus: event type 0x%02x", event.type());
break;
}
}
//---------------------------------------------------------
// process
// realtime
//---------------------------------------------------------
void Zerberus::process(unsigned frames, float* p, float*, float*)
{
if (busy)
return;
Voice* v = activeVoices;
Voice* pv = 0;
while (v) {
v->process(frames, p);
if (v->isOff()) {
if (pv)
pv->setNext(v->next());
else
activeVoices = v->next();
freeVoices.push(v);
}
else
pv = v;
v = v->next();
}
}
//---------------------------------------------------------
// name
//---------------------------------------------------------
const char* Zerberus::name() const
{
return "Zerberus";
}
//---------------------------------------------------------
// getPatchInfo
//---------------------------------------------------------
const QList<Ms::MidiPatch*>& Zerberus::getPatchInfo() const
{
static QList<Ms::MidiPatch*> pl;
qDeleteAll(pl);
pl.clear();
int idx = 0;
for (ZInstrument* i : instruments) {
Ms::MidiPatch* p = new Ms::MidiPatch { false, name(), 0, idx, i->name() };
pl.append(p);
++idx;
}
return pl;
}
//---------------------------------------------------------
// allSoundsOff
//---------------------------------------------------------
void Zerberus::allSoundsOff(int channel)
{
allNotesOff(channel);
}
//---------------------------------------------------------
// allNotesOff
//---------------------------------------------------------
void Zerberus::allNotesOff(int channel)
{
busy = true;
for (Voice* v = activeVoices; v; v = v->next()) {
if (channel == -1 || (v->channel()->idx() == channel))
v->stop();
}
busy = false;
}
//---------------------------------------------------------
// loadSoundFonts
//---------------------------------------------------------
bool Zerberus::loadSoundFonts(const QStringList& sl)
{
foreach (const QString& s, sl) {
if (!loadInstrument(s))
return false;
}
return true;
}
//---------------------------------------------------------
// removeSoundFonts
//---------------------------------------------------------
bool Zerberus::removeSoundFonts(const QStringList& fileNames)
{
for (auto fileName : fileNames) {
if (!removeSoundFont(QFileInfo(fileName).absoluteFilePath()))
return false;
}
return true;
}
//---------------------------------------------------------
// soundFonts
//---------------------------------------------------------
QStringList Zerberus::soundFonts() const
{
QStringList sl;
for (ZInstrument* i : instruments)
sl.append(i->path());
return sl;
}
//---------------------------------------------------------
// addSoundFont
//---------------------------------------------------------
bool Zerberus::addSoundFont(const QString& s)
{
QMutexLocker locker(&mutex);
return loadInstrument(s);
}
//---------------------------------------------------------
// removeSoundFont
//---------------------------------------------------------
bool Zerberus::removeSoundFont(const QString& s)
{
for (ZInstrument* i : instruments) {
if (i->path() == s) {
auto it = find(instruments.begin(), instruments.end(), i);
if (it == instruments.end())
return false;
instruments.erase(it);
for (int k = 0; k < MAX_CHANNEL; ++k) {
if (_channel[k]->instrument() == i)
_channel[k]->setInstrument(0);
}
if (!instruments.empty()) {
for (int ii = 0; ii < MAX_CHANNEL; ++ii) {
if (_channel[ii]->instrument() == 0)
_channel[ii]->setInstrument(instruments.front());
}
}
i->setRefCount(i->refCount() - 1);
if (i->refCount() <= 0) {
auto it1 = find(globalInstruments.begin(), globalInstruments.end(), i);
if (it1 == globalInstruments.end())
return false;
globalInstruments.erase(it1);
delete i;
}
return true;
}
}
return false;
}
//---------------------------------------------------------
// state
//---------------------------------------------------------
Ms::SynthesizerGroup Zerberus::state() const
{
Ms::SynthesizerGroup g;
g.setName(name());
QStringList sfl = soundFonts();
foreach(QString sf, sfl)
g.push_back(Ms::IdValue(0, sf));
return g;
}
//---------------------------------------------------------
// setState
//---------------------------------------------------------
bool Zerberus::setState(const Ms::SynthesizerGroup& sp)
{
QStringList sfs;
for (const Ms::IdValue& v : sp)
sfs.append(v.data);
return loadSoundFonts(sfs);
}
//---------------------------------------------------------
// instrument
//---------------------------------------------------------
ZInstrument* Zerberus::instrument(int n) const
{
int idx = 0;
for (auto i = instruments.begin(); i != instruments.end(); ++i) {
if (idx == n)
return *i;
++idx;
}
return 0;
}
//---------------------------------------------------------
// loadInstrument
// return true on success
//---------------------------------------------------------
bool Zerberus::loadInstrument(const QString& s)
{
if (s.isEmpty())
return false;
QFileInfo fis(s);
QString fileName = fis.fileName();
for (ZInstrument* instr : instruments) {
if (QFileInfo(instr->path()).fileName() == fileName) { // already loaded?
return true;
}
}
for (ZInstrument* instr : globalInstruments) {
if (QFileInfo(instr->path()).fileName() == fileName) {
instruments.push_back(instr);
instr->setRefCount(instr->refCount() + 1);
if (instruments.size() == 1) {
for (int i = 0; i < MAX_CHANNEL; ++i)
_channel[i]->setInstrument(instr);
}
busy = false;
return true;
}
}
QFileInfoList l = Zerberus::sfzFiles();
QString path;
foreach (const QFileInfo& fi, l) {
if (fi.fileName() == fileName) {
path = fi.absoluteFilePath();
break;
}
}
busy = true;
ZInstrument* instr = new ZInstrument(this);
try {
if (instr->load(path)) {
globalInstruments.push_back(instr);
instruments.push_back(instr);
instr->setRefCount(1);
//
// set default instrument for all channels:
//
if (instruments.size() == 1) {
for (int i = 0; i < MAX_CHANNEL; ++i)
_channel[i]->setInstrument(instr);
}
busy = false;
return true;
}
}
catch (std::bad_alloc& a) {
qDebug("Unable to allocate memory when loading Zerberus soundfont %s", qPrintable(s));
// Prevent "Unreferenced local variable" warning for a
Q_UNUSED(a);
}
catch (...) {
}
qDebug("Zerberus::loadInstrument failed");
busy = false;
delete instr;
return false;
}