diff options
Diffstat (limited to 'muse_qt4_evolution/muse/midiout.cpp')
-rw-r--r-- | muse_qt4_evolution/muse/midiout.cpp | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/muse_qt4_evolution/muse/midiout.cpp b/muse_qt4_evolution/muse/midiout.cpp new file mode 100644 index 00000000..c35f115f --- /dev/null +++ b/muse_qt4_evolution/muse/midiout.cpp @@ -0,0 +1,398 @@ +//============================================================================= +// MusE +// Linux Music Editor +// $Id:$ +// +// Copyright (C) 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 "midiout.h" +#include "midictrl.h" +#include "miditrackbase.h" +#include "al/al.h" +#include "al/tempo.h" +#include "event.h" +#include "sync.h" +#include "audio.h" +#include "gconfig.h" + +static const unsigned char mmcDeferredPlayMsg[] = { 0x7f, 0x7f, 0x06, 0x03 }; +static const unsigned char mmcStopMsg[] = { 0x7f, 0x7f, 0x06, 0x01 }; + +//--------------------------------------------------------- +// MidiOut +//--------------------------------------------------------- + +MidiOut::MidiOut() + { + track = 0; + } + +//--------------------------------------------------------- +// sendGmOn +// send GM-On message to midi device and keep track +// of device state +//--------------------------------------------------------- + +void MidiOut::sendGmOn() + { + sendSysex(gmOnMsg, gmOnMsgLen); + track->setHwCtrlState(CTRL_PROGRAM, 0); + track->setHwCtrlState(CTRL_PITCH, 0); + track->setHwCtrlState(CTRL_VOLUME, 100); + track->setHwCtrlState(CTRL_PANPOT, 64); + track->setHwCtrlState(CTRL_REVERB_SEND, 40); + track->setHwCtrlState(CTRL_CHORUS_SEND, 0); + track->setMeter(0, 0.0); + } + +//--------------------------------------------------------- +// sendGsOn +// send Roland GS-On message to midi device and keep track +// of device state +//--------------------------------------------------------- + +void MidiOut::sendGsOn() + { + static unsigned char data2[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x33, 0x50, 0x3c }; + static unsigned char data3[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x34, 0x50, 0x3b }; + + sendSysex(data2, sizeof(data2)); + sendSysex(data3, sizeof(data3)); + } + +//--------------------------------------------------------- +// sendXgOn +// send Yamaha XG-On message to midi device and keep track +// of device state +//--------------------------------------------------------- + +void MidiOut::sendXgOn() + { + sendSysex(xgOnMsg, xgOnMsgLen); + track->setHwCtrlState(CTRL_PROGRAM, 0); + track->setHwCtrlState(CTRL_MODULATION, 0); + track->setHwCtrlState(CTRL_PORTAMENTO_TIME, 0); + track->setHwCtrlState(CTRL_VOLUME, 0x64); + track->setHwCtrlState(CTRL_PANPOT, 0x40); + track->setHwCtrlState(CTRL_EXPRESSION, 0x7f); + track->setHwCtrlState(CTRL_SUSTAIN, 0x0); + track->setHwCtrlState(CTRL_PORTAMENTO, 0x0); + track->setHwCtrlState(CTRL_SOSTENUTO, 0x0); + track->setHwCtrlState(CTRL_SOFT_PEDAL, 0x0); + track->setHwCtrlState(CTRL_HARMONIC_CONTENT, 0x40); + track->setHwCtrlState(CTRL_RELEASE_TIME, 0x40); + track->setHwCtrlState(CTRL_ATTACK_TIME, 0x40); + track->setHwCtrlState(CTRL_BRIGHTNESS, 0x40); + track->setHwCtrlState(CTRL_REVERB_SEND, 0x28); + track->setHwCtrlState(CTRL_CHORUS_SEND, 0x0); + track->setHwCtrlState(CTRL_VARIATION_SEND, 0x0); + track->setMeter(0, 0.0); + } + +//--------------------------------------------------------- +// sendSysex +// send SYSEX message to midi device +//--------------------------------------------------------- + +void MidiOut::sendSysex(const unsigned char* p, int n) + { + MidiEvent event(0, ME_SYSEX, p, n); + track->routeEvent(event); + } + +//--------------------------------------------------------- +// sendStart +//--------------------------------------------------------- + +void MidiOut::sendStart() + { + MidiEvent event(0, 0, ME_START, 0, 0); + track->routeEvent(event); + } + +//--------------------------------------------------------- +// sendStop +//--------------------------------------------------------- + +void MidiOut::sendStop() + { + MidiEvent event(0, 0, ME_STOP, 0, 0); + track->routeEvent(event); + } + +//--------------------------------------------------------- +// sendClock +//--------------------------------------------------------- + +void MidiOut::sendClock() + { + MidiEvent event(0, 0, ME_CLOCK, 0, 0); + track->routeEvent(event); + } + +//--------------------------------------------------------- +// sendContinue +//--------------------------------------------------------- + +void MidiOut::sendContinue() + { + MidiEvent event(0, 0, ME_CONTINUE, 0, 0); + track->routeEvent(event); + } + +//--------------------------------------------------------- +// sendSongpos +//--------------------------------------------------------- + +void MidiOut::sendSongpos(int pos) + { + MidiEvent event(0, 0, ME_SONGPOS, pos, 0); + track->routeEvent(event); + } + +//--------------------------------------------------------- +// playMidiEvent +// called from GUI +//--------------------------------------------------------- + +void MidiOut::playMidiEvent(MidiEvent* ev) + { + if (eventFifo.put(*ev)) + printf("MidiPort::playMidiEvent(): port overflow, drop event\n"); + } + +//--------------------------------------------------------- +// seek +//--------------------------------------------------------- + +void MidiOut::seek(unsigned tickPos, unsigned framePos) + { + if (genMCSync && track->sendSync()) { + int beat = (tickPos * 4) / config.division; + sendStop(); + sendSongpos(beat); + sendContinue(); + } + if (track->mute()) + return; + +// if (pos == 0 && !song->record()) +// audio->initDevices(); + + //--------------------------------------------------- + // stop all notes + //--------------------------------------------------- + + for (iMidiEvent i = _schedEvents.begin(); i != _schedEvents.end(); ++i) { + MidiEvent ev = *i; + if (ev.isNoteOff()) { + ev.setTime(framePos); + track->routeEvent(ev); + } + } + _schedEvents.clear(); + + //--------------------------------------------------- + // set all controller + //--------------------------------------------------- + + if (track->autoRead()) { + CtrlList* cl = track->controller(); + for (iCtrl ic = cl->begin(); ic != cl->end(); ++ic) { + Ctrl* c = ic->second; + int val = c->value(tickPos).i; + if ((val != CTRL_VAL_UNKNOWN) && (c->curVal().i != val)) { + MidiEvent ev(0, -1, ME_CONTROLLER, c->id(), val); + track->routeEvent(ev); + c->setCurVal(val); + } + } + } + foreach (const Route& r, *track->inRoutes()) { + MidiTrackBase* t = (MidiTrackBase*)r.src.track; + int dstChannel = r.dst.channel; + if (t->isMute() || !t->autoRead()) + continue; + CtrlList* cl = t->controller(); + for (iCtrl ic = cl->begin(); ic != cl->end(); ++ic) { + Ctrl* c = ic->second; + int val = c->value(tickPos).i; + if ((val != CTRL_VAL_UNKNOWN) && (c->curVal().i != val)) { + MidiEvent ev(0, dstChannel, ME_CONTROLLER, c->id(), val); + track->routeEvent(ev); + c->setCurVal(val); + } + } + } + } + +//--------------------------------------------------------- +// stop +//--------------------------------------------------------- + +void MidiOut::stop() + { + //--------------------------------------------------- + // stop all notes + //--------------------------------------------------- + + int time = audio->seqTime()->lastFrameTime; + for (iMidiEvent i = _schedEvents.begin(); i != _schedEvents.end(); ++i) { + MidiEvent ev = *i; + if (ev.isNoteOff()) { + ev.setTime(time); + track->routeEvent(ev); + } + } + _schedEvents.clear(); + + //--------------------------------------------------- + // reset sustain + //--------------------------------------------------- + + foreach (const Route& r, *track->inRoutes()) { + MidiTrackBase* t = (MidiTrackBase*)r.src.track; + int dstChannel = r.dst.channel; + if (t->isMute() || !t->autoRead()) + continue; + CtrlList* cl = t->controller(); + for (iCtrl ic = cl->begin(); ic != cl->end(); ++ic) { + Ctrl* c = ic->second; + if (c->id() == CTRL_SUSTAIN) { + if (c->curVal().i > 0) { + MidiEvent ev(time, dstChannel, ME_CONTROLLER, c->id(), 0); + track->routeEvent(ev); + c->setCurVal(0); + } + } + } + } + + if (track->sendSync()) { + if (genMMC) { + unsigned char mmcPos[] = { + 0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, + 0, 0, 0, 0, 0 + }; + double frame = double(audio->seqTime()->pos.frame()); + MTC mtc(frame / double(AL::sampleRate)); + mmcPos[6] = mtc.h() | (AL::mtcType << 5); + mmcPos[7] = mtc.m(); + mmcPos[8] = mtc.s(); + mmcPos[9] = mtc.f(); + mmcPos[10] = mtc.sf(); + sendSysex(mmcStopMsg, sizeof(mmcStopMsg)); + sendSysex(mmcPos, sizeof(mmcPos)); + } + if (genMCSync) { // Midi Clock + // send STOP and + // "set song position pointer" + sendStop(); + sendSongpos(audio->seqTime()->curTickPos * 4 / config.division); + } + } + } + +//--------------------------------------------------------- +// start +//--------------------------------------------------------- + +void MidiOut::start() + { + // TODO: set sustain to old value? + + if (!(genMMC || genMCSync || track->sendSync())) + return; + if (genMMC) + sendSysex(mmcDeferredPlayMsg, sizeof(mmcDeferredPlayMsg)); + if (genMCSync) { + if (audio->seqTime()->curTickPos) + sendContinue(); + else + sendStart(); + } + } + +//--------------------------------------------------------- +// reset +//--------------------------------------------------------- + +void MidiOut::reset() + { +/* TODO + MidiEvent ev; + ev.setType(0x90); + for (int chan = 0; chan < MIDI_CHANNELS; ++chan) { + ev.setChannel(chan); + for (int pitch = 0; pitch < 128; ++pitch) { + ev.setA(pitch); + ev.setB(0); + mp->putEvent(ev); + } + } +*/ + } + +//------------------------------------------------------------------- +// 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 later after the +// current process cycle. +// From _schedEvents queue copy all events for the current cycle +// to all output routes. Events routed to ALSA go into the +// _playEvents queue which is processed by the MidiSeq thread. +//------------------------------------------------------------------- + +void MidiOut::processMidi(MidiEventList& el, const SeqTime* time) + { + while (!eventFifo.isEmpty()) + el.insert(eventFifo.get()); + + // collect port controller + if (track->autoRead() && (time->curTickPos != time->nextTickPos)) { // if rolling + CtrlList* cl = track->controller(); + for (iCtrl ic = cl->begin(); ic != cl->end(); ++ic) { + Ctrl* c = ic->second; + iCtrlVal is = c->lowerBound(time->curTickPos); + iCtrlVal ie = c->lowerBound(time->nextTickPos); + for (iCtrlVal ic = is; ic != ie; ++ic) { + unsigned frame = AL::tempomap.tick2frame(ic.key()); + MidiEvent ev(frame, -1, ME_CONTROLLER, c->id(), ic.value().i); + el.insert(ev); + c->setCurVal(ic.value().i); + } + } + } + foreach (const Route& r, *track->inRoutes()) { + MidiTrackBase* t = (MidiTrackBase*)r.src.track; + int dstChannel = r.dst.channel; + if (t->isMute()) + continue; + MidiEventList ell; + t->getEvents(time->curTickPos, time->nextTickPos, r.src.channel, &ell); + int velo = 0; + for (iMidiEvent i = ell.begin(); i != ell.end(); ++i) { + MidiEvent ev(*i); + ev.setChannel(dstChannel); + el.insert(ev); + if (ev.type() == ME_NOTEON) + velo += ev.dataB(); + } + t->addMidiMeter(velo); + } + } + |