diff options
Diffstat (limited to 'attic/muse_qt4_evolution/muse/synth.cpp')
-rw-r--r-- | attic/muse_qt4_evolution/muse/synth.cpp | 588 |
1 files changed, 588 insertions, 0 deletions
diff --git a/attic/muse_qt4_evolution/muse/synth.cpp b/attic/muse_qt4_evolution/muse/synth.cpp new file mode 100644 index 00000000..bbec5e34 --- /dev/null +++ b/attic/muse_qt4_evolution/muse/synth.cpp @@ -0,0 +1,588 @@ +//============================================================================= +// MusE +// Linux Music Editor +// $Id:$ +// +// Copyright (C) 2002-2006 by 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. +//============================================================================= + +#include <dlfcn.h> + +#include "al/al.h" +#include "al/xml.h" +#include "al/tempo.h" +#include "muse.h" +#include "synth.h" +#include "midi.h" +#include "synti/libsynti/mess.h" +#include "song.h" +#include "audio.h" +#include "event.h" +#include "midievent.h" +#include "audio.h" +#include "midictrl.h" +#include "instruments/minstrument.h" +#include "audiodev.h" + +std::vector<Synth*> synthis; // array of available synthis + +extern void connectNodes(AudioTrack*, AudioTrack*); + +//--------------------------------------------------------- +// description +//--------------------------------------------------------- + +const char* MessSynth::description() const + { + return descr ? descr->description : ""; + } + +//--------------------------------------------------------- +// version +//--------------------------------------------------------- + +const char* MessSynth::version() const + { + return descr ? descr->version : ""; + } + +bool MessSynthIF::guiVisible() const + { + return _mess ? _mess->guiVisible() : false; + } + +void MessSynthIF::showGui(bool v) + { + if (v == guiVisible()) + return; + if (_mess) + _mess->showGui(v); + } + +bool MessSynthIF::hasGui() const + { + if (_mess) + return _mess->hasGui(); + return false; + } + +MidiEvent MessSynthIF::receiveEvent() + { + if (_mess) + return _mess->receiveEvent(); + return MidiEvent(); + } + +int MessSynthIF::eventsPending() const + { + if (_mess) + return _mess->eventsPending(); + return 0; + } + +void MessSynthIF::getGeometry(int* x, int* y, int* w, int* h) const + { + if (_mess) + _mess->getGeometry(x, y, w, h); + } + +void MessSynthIF::setGeometry(int x, int y, int w, int h) + { + if (_mess) + _mess->setGeometry(x, y, w, h); + } + +//--------------------------------------------------------- +// findSynth +// search for synthesizer base class +//--------------------------------------------------------- + +Synth* findSynth(const QString& sclass) + { + for (std::vector<Synth*>::iterator i = synthis.begin(); + i != synthis.end(); ++i) { + if ((*i)->name() == sclass) + return *i; + } + printf("synthi class <%s> not found\n", sclass.toLatin1().data()); + return 0; + } + +//--------------------------------------------------------- +// Synth +//--------------------------------------------------------- + +Synth::Synth(const QFileInfo* fi, QString s) + : info(*fi), _name(s) + { + _instances = 0; + } + +//--------------------------------------------------------- +// instantiate +//--------------------------------------------------------- + +void* MessSynth::instantiate(const QString& instanceName) + { + ++_instances; + const char* path = strdup(info.filePath().toAscii().data()); + + // load Synti dll + if (debugMsg) + printf(" load synti <%s>\n", path); + void* handle = dlopen(path, RTLD_NOW); + // void* handle = dlopen(path, RTLD_LAZY); + if (handle == 0) { + fprintf(stderr, "Synth::instantiate: dlopen(%s) failed: %s\n", + path, dlerror()); + delete path; + return 0; + } + typedef const MESS* (*MESS_Function)(); + MESS_Function msynth = (MESS_Function)dlsym(handle, "mess_descriptor"); + + if (!msynth) { + const char *txt = dlerror(); + if (txt) { + fprintf(stderr, + "Unable to find msynth_descriptor() function in plugin " + "library file \"%s\": %s.\n" + "Are you sure this is a MESS plugin file?\n", + path, txt); + delete path; + return 0; + } + } + delete path; + descr = msynth(); + if (descr == 0) { + fprintf(stderr, "Synth::instantiate: no MESS descr found\n"); + return 0; + } + Mess* mess = descr->instantiate(AL::sampleRate, instanceName.toLatin1().data()); + return mess; + } + +//--------------------------------------------------------- +// SynthI +//--------------------------------------------------------- + +SynthI::SynthI() + : AudioTrack() + { + track = this; + synthesizer = 0; + _sif = 0; + // setVolume(1.0); + // setPan(0.0); + setReadonly(true); // midi instrument cannot be edited + } + +//--------------------------------------------------------- +// ~SynthI +//--------------------------------------------------------- + +SynthI::~SynthI() + { + deactivate2(); + deactivate3(); + } + +//--------------------------------------------------------- +// setName +//--------------------------------------------------------- + +void SynthI::setName(const QString& s) + { + Track::setName(s); + } + +//--------------------------------------------------------- +// init +//--------------------------------------------------------- + +bool MessSynthIF::init(Synth* s, SynthI* si) + { + _mess = (Mess*)((MessSynth*)s)->instantiate(si->name()); + return (_mess == 0); + } + +//--------------------------------------------------------- +// channels +//--------------------------------------------------------- + +int MessSynthIF::channels() const + { + return _mess->channels(); + } + +//--------------------------------------------------------- +// createSIF +//--------------------------------------------------------- + +SynthIF* MessSynth::createSIF(SynthI* si) + { + MessSynthIF* sif = new MessSynthIF(si); + sif->init(this, si); + return sif; + } + +//--------------------------------------------------------- +// initInstance +// returns false on success +//--------------------------------------------------------- + +bool SynthI::initInstance(Synth* s) + { + synthesizer = s; + _sif = s->createSIF(this); + + setIName(name()); // set instrument name + int n = _sif->channels(); + AudioTrack::setChannels(n); + + //--------------------------------------------------- + // read available controller from synti + //--------------------------------------------------- + + int id = 0; + MidiControllerList* cl = MidiInstrument::controller(); + for (;;) { + const char* name; + int ctrl; + int min; + int max; + id = _sif->getControllerInfo(id, &name, &ctrl, &min, &max); + if (id == 0) + break; + MidiController* c = new MidiController(QString(name), ctrl, min, max, 0); + cl->push_back(c); + } + + EventList* iel = midiState(); + if (!iel->empty()) { + for (iEvent i = iel->begin(); i != iel->end(); ++i) { + Event ev = i->second; + MidiEvent pev(0, 0, ev); + if (_sif->putEvent(pev)) + putFifo.put(pev); // save for later retry + } + iel->clear(); + } + + int idx = 0; + for (std::vector<float>::iterator i = initParams.begin(); i != initParams.end(); ++i, ++idx) + _sif->setParameter(idx, *i); + return false; + } + +//--------------------------------------------------------- +// getControllerInfo +//--------------------------------------------------------- + +int MessSynthIF::getControllerInfo(int id, const char** name, int* ctrl, int* min, int* max) + { + return _mess->getControllerInfo(id, name, ctrl, min, max); + } + +//--------------------------------------------------------- +// SynthI::deactivate +//--------------------------------------------------------- + +void SynthI::deactivate2() + { + removeMidiInstrument(this); + } + +//--------------------------------------------------------- +// deactivate3 +//--------------------------------------------------------- + +void SynthI::deactivate3() + { + delete _sif; + _sif = 0; + synthesizer->incInstances(-1); + } + +void MessSynthIF::deactivate3() + { + if (_mess) { + delete _mess; + _mess = 0; + } + } + +MessSynthIF::~MessSynthIF() + { + deactivate3(); + } + +//--------------------------------------------------------- +// initMidiSynth +// search for software synthis and advertise +//--------------------------------------------------------- + +void initMidiSynth() + { + QString s = museGlobalLib + "/synthi"; + +#ifdef __APPLE__ + QDir pluginDir(s, QString("*.dylib"), 0, QDir::Files); +#else + QDir pluginDir(s, QString("*.so"), 0, QDir::Files); +#endif + if (debugMsg) + printf("searching for software synthesizer in <%s>\n", s.toLatin1().data()); + if (pluginDir.exists()) { + QFileInfoList list = pluginDir.entryInfoList(); + for (int i = 0; i < list.size(); ++i) { + QFileInfo fi = list.at(i); + synthis.push_back(new MessSynth(&fi)); + } + if (debugMsg) + printf("%zd soft synth found\n", synthis.size()); + } + } + +//--------------------------------------------------------- +// write +//--------------------------------------------------------- + +void SynthI::write(Xml& xml) const + { + xml.stag("SynthI"); + AudioTrack::writeProperties(xml); + xml.tag("class", synth()->name()); + + //--------------------------------------------- + // if soft synth is attached to a midi port, + // write out port number + //--------------------------------------------- + + if (hasGui()) { + xml.tag("guiVisible", guiVisible()); + int x, y, w, h; + w = 0; + h = 0; + getGeometry(&x, &y, &w, &h); + if (h || w) + xml.tag("geometry", QRect(x, y, w, h)); + } + _sif->write(xml); + xml.etag("SynthI"); + + } + +void MessSynthIF::write(Xml& xml) const + { + //--------------------------------------------- + // dump current state of synth + //--------------------------------------------- + + int len = 0; + const unsigned char* p; + _mess->getInitData(&len, &p); + if (len) { + xml.stag("midistate"); + xml.stag(QString("event type=\"%1\" datalen=\"%2\"").arg(Sysex).arg(len)); + xml.dump(len, p); + xml.etag("event"); + xml.etag("midistate"); + } + } + +//--------------------------------------------------------- +// SynthI::read +//--------------------------------------------------------- + +void SynthI::read(QDomNode node) + { + QString sclass; + int port = -1; + bool startGui = false; + QRect r; + + for (; !node.isNull(); node = node.nextSibling()) { + QDomElement e = node.toElement(); + QString tag(e.tagName()); + if (tag == "class") + sclass = e.text(); + else if (tag == "port") + port = e.text().toInt(); + else if (tag == "guiVisible") + startGui = e.text().toInt(); + else if (tag == "midistate") + readMidiState(node.firstChild()); + else if (tag == "param") { + float val = e.text().toFloat(); + initParams.push_back(val); + } + else if (tag == "geometry") + r = AL::readGeometry(node); + else if (AudioTrack::readProperties(node)) { + printf("MusE:SynthI: unknown tag %s\n", e.tagName().toLatin1().data()); + } + } + Synth* s = findSynth(sclass); + if (s == 0) + return; + if (initInstance(s)) + return; + song->insertTrack0(this, -1); + setGeometry(r.x(), r.y(), r.width(), r.height()); + showGui(startGui); + } + +//--------------------------------------------------------- +// getPatchName +//--------------------------------------------------------- + +QString MessSynthIF::getPatchName(int channel, int prog) + { + if (_mess) + return _mess->getPatchName(channel, prog, 0); + return ""; + } + +//--------------------------------------------------------- +// populatePatchPopup +//--------------------------------------------------------- + +void MessSynthIF::populatePatchPopup(QMenu* menu, int ch) + { + menu->clear(); + const MidiPatch* mp = _mess->getPatchInfo(ch, 0); + QMenu *hm = NULL, *lm = NULL; + while (mp) { + switch(mp->typ) { + case MP_TYPE_HBANK : + hm = menu->addMenu(QString(mp->name)); + break; + case MP_TYPE_LBANK : + if(hm) lm = hm->addMenu(QString(mp->name)); + else lm = menu->addMenu(QString(mp->name)); + break; + default : + int id = ((mp->hbank & 0xff) << 16) + + ((mp->lbank & 0xff) << 8) + mp->prog; + QAction* a; + if(lm) a = lm->addAction(QString(mp->name)); + else a = menu->addAction(QString(mp->name)); + a->setData(id); + break; + } + mp = _mess->getPatchInfo(ch, mp); + } + } + + + +//--------------------------------------------------------- +// getData +//--------------------------------------------------------- + +void MessSynthIF::getData(MidiEventList* el, unsigned pos, int ports, unsigned n, float** buffer) + { + // Reset buffers first + for (int port = 0; port < ports; ++port) + memset(buffer[port], 0, n * sizeof(float)); + + int curPos = pos; + int endPos = pos + n; + + while (!synti->putFifo.isEmpty()) { + if (putEvent(synti->putFifo.peek())) + break; + synti->putFifo.remove(); + } + + // Echo events from Synti back + while (_mess->eventsPending()) + // _mess->processEvent(_mess->receiveEvent()); + _mess->receiveEvent(); // throw away event + + if (ports >= channels()) { + iMidiEvent i = el->begin(); + for (; i != el->end(); ++i) { + int frame = i->time(); + if (frame >= endPos) + break; + if (frame > curPos) { + // Several following notes during same segmentsize? + _mess->process(buffer, curPos-pos, frame - curPos); + curPos = frame; // don't process this piece again + } + if (putEvent(*i)) + synti->putFifo.put(*i); + } + if (endPos - curPos > 0) + _mess->process(buffer, curPos-pos, endPos - curPos); + el->erase(el->begin(), i); + } + else { + // this happens if the synth has stereo and we switch the + // channel to mono + + printf("MessSynthIF::getData - ports %d < channels %d\n", + ports, channels()); + } + } + +//--------------------------------------------------------- +// putEvent +// return true on error (busy), event will later be +// resend +//--------------------------------------------------------- + +bool MessSynthIF::putEvent(const MidiEvent& ev) + { + bool rv = true; + if (_mess) { + rv = _mess->processEvent(ev); + if (midiOutputTrace && !rv) { + printf("MidiOut<%s>", synti->name().toLatin1().data()); + ev.dump(); + } + } + return rv; + } + +//--------------------------------------------------------- +// collectInputData +//--------------------------------------------------------- + +void SynthI::collectInputData() + { + bufferEmpty = false; + _sif->getData(&_schedEvents, audioDriver->frameTime(), channels(), + segmentSize, buffer); + } + +//------------------------------------------------------------------- +// process +// Collect all midi events for the current process cycle and put +// into _schedEvents queue. For note on events create the proper +// note off events. The note off events maybe played after the +// current process cycle. +//------------------------------------------------------------------- + +void SynthI::processMidi(SeqTime* t) + { + if (mute()) + return; + MidiOut::processMidi(_schedEvents, t); + } + |