From 2120ea4f083228dde0d7307203391a4ec8f57e2d Mon Sep 17 00:00:00 2001 From: "Tim E. Real" Date: Fri, 9 Sep 2011 10:04:11 +0000 Subject: Mostly engine fixes/corrections. Please see ChangeLog. --- muse2/ChangeLog | 16 + muse2/README.effects-rack | 11 +- muse2/muse/audio.cpp | 542 ++------------------- muse2/muse/audio.h | 8 +- muse2/muse/driver/alsamidi.cpp | 369 +++++++++----- muse2/muse/driver/alsamidi.h | 20 +- muse2/muse/driver/jackmidi.cpp | 851 ++------------------------------- muse2/muse/driver/jackmidi.h | 89 +--- muse2/muse/instruments/minstrument.cpp | 1 + muse2/muse/midi.cpp | 756 ++++++++--------------------- muse2/muse/midi.h | 2 +- muse2/muse/mididev.cpp | 29 ++ muse2/muse/mididev.h | 22 +- muse2/muse/midiseq.cpp | 84 ++-- muse2/muse/midiseq.h | 8 +- muse2/muse/mpevent.h | 4 +- muse2/muse/song.cpp | 22 + muse2/muse/synth.cpp | 28 +- muse2/muse/synth.h | 1 + 19 files changed, 719 insertions(+), 2144 deletions(-) diff --git a/muse2/ChangeLog b/muse2/ChangeLog index bdc4cb57..b0bc5121 100644 --- a/muse2/ChangeLog +++ b/muse2/ChangeLog @@ -1,3 +1,19 @@ +09.09.2011: + - Removed sending of SEQM_ADD_TRACK, SEQM_REMOVE_TRACK, SEQM_CHANGE_TRACK, SEQM_REMOVE_PART, and + SEQM_CHANGE_PART to ALSA midi thread (which waits) from inside Audio::processMsg. + Replaced with hand-offs to Song::processMsg. TODO: SEQM_SET_TRACK_OUT_CHAN etc. (Tim) + - MidiDevice play and stuck event lists no longer directly accessible from outside - + replaced with ::addScheduledEvent and ::addStuckNote. (Tim) + - Installed a ring buffer for ALSA MidiDevice play/stuck notes event lists, filled by ::addScheduledEvent + and ::addStuckNote, because ALSA midi is processed in a thread other than audio. (Tim) + TODO: processMidi could do stuck notes better by bypassing their insertion in the play event list, + also applies to seek/stop handler block, but I'm not sure about the time comparisons. + - Some more separation of ALSA and Jack Midi, one step closer to configurable choice of either/or. (Tim) + - Massive cleanup of audio, midi, mididev, alsamidi, jackmidi modules. Moved some stuff from Audio::processMidi + into MidiDevice. (Tim) + - Test OK here so far, with stress files and complete pre-existing songs, ALSA, Jack, and Synth midi. (Tim) + TODO: Restoration of sustain upon play seems broken for a while - try putEvent instead of addScheduledEvent + in Audio::startRolling(). 08.09.2011: - Finished namespace MusEWidget. Added namespaces MusEUtil, MusEDialog. (Orcan) 07.09.2011: diff --git a/muse2/README.effects-rack b/muse2/README.effects-rack index 823ae74c..071ba08e 100644 --- a/muse2/README.effects-rack +++ b/muse2/README.effects-rack @@ -1,4 +1,4 @@ -May 6, 2010 v0.0.2 by Tim. +Sept 1, 2011 v0.0.3 by Tim. Understanding the Effects Rack ------------------------------ @@ -48,15 +48,12 @@ These same rules apply to inter-plugin audio when more than one plugin is in the rack chain. Extra audio outputs of one plugin may be ignored by the next plugin if not used. -Currently plugins with no audio outputs are not really useful in MusE, - chiefly because plugin control outputs are not used or displayed. -Nor are specialized plugins with many inputs and/or outputs. +Currently specialized plugins with many inputs and/or outputs are not + really useful in MusE. Nor are so-called 'realtime' control plugins which use audio inputs and outputs for control signals. -<<< - Loud noise alert! Beware of using such plugins in an audio effects rack. ->>> +Loud noise alert! Beware of using such plugins in an audio effects rack. Example: Consider a stereo Audio Input track with these effect rack diff --git a/muse2/muse/audio.cpp b/muse2/muse/audio.cpp index a82fbe13..60eb5c73 100644 --- a/muse2/muse/audio.cpp +++ b/muse2/muse/audio.cpp @@ -4,6 +4,7 @@ // $Id: audio.cpp,v 1.59.2.30 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -30,10 +31,8 @@ #include "song.h" #include "node.h" #include "audiodev.h" -//#include "driver/audiodev.h" // p4.0.2 #include "mididev.h" #include "alsamidi.h" -//#include "driver/alsamidi.h" // p4.0.2 #include "synth.h" #include "audioprefetch.h" #include "plugin.h" @@ -55,8 +54,7 @@ extern double curTime(); Audio* audio; AudioDevice* audioDevice; // current audio device in use -// p3.3.25 -extern unsigned int volatile midiExtSyncTicks; +extern unsigned int volatile midiExtSyncTicks; // p3.3.25 //static const unsigned char mmcDeferredPlayMsg[] = { 0x7f, 0x7f, 0x06, 0x03 }; @@ -126,7 +124,7 @@ Audio::Audio() _pos.setType(Pos::FRAMES); _pos.setFrame(0); - curTickPos = 0; + nextTickPos = curTickPos = 0; midiClick = 0; clickno = 0; @@ -140,10 +138,9 @@ Audio::Audio() state = STOP; msg = 0; - // Changed by Tim. p3.3.8 //startRecordPos.setType(Pos::TICKS); //endRecordPos.setType(Pos::TICKS); - startRecordPos.setType(Pos::FRAMES); + startRecordPos.setType(Pos::FRAMES); // Tim endRecordPos.setType(Pos::FRAMES); _audioMonitor = 0; @@ -187,16 +184,12 @@ bool Audio::start() _loopCount = 0; MusEGlobal::muse->setHeartBeat(); if (audioDevice) { - // Added by Tim. p3.3.6 //_running = true; - //audioDevice->start(); } else { if(false == initJackAudio()) { - // Added by Tim. p3.3.6 //_running = true; - InputList* itl = song->inputs(); for (iAudioInput i = itl->begin(); i != itl->end(); ++i) { //printf("reconnecting input %s\n", (*i)->name().ascii()); @@ -253,25 +246,6 @@ void Audio::stop(bool) bool Audio::sync(int jackState, unsigned frame) { - -// Changed by Tim. p3.3.24 -/* - bool done = true; - if (state == LOOP1) - state = LOOP2; - else { - if (_pos.frame() != frame) { - Pos p(frame, false); - seek(p); - } - state = State(jackState); - if (!_freewheel) - //done = audioPrefetch->seekDone; - done = audioPrefetch->seekDone(); - } - - return done; -*/ bool done = true; if (state == LOOP1) state = LOOP2; @@ -283,8 +257,6 @@ bool Audio::sync(int jackState, unsigned frame) // PLAY -> START_PLAY seek in play state if (state != START_PLAY) { - //Pos p(frame, AL::FRAMES); - // seek(p); Pos p(frame, false); seek(p); if (!_freewheel) @@ -293,10 +265,8 @@ bool Audio::sync(int jackState, unsigned frame) state = START_PLAY; } else { - //if (frame != _seqTime.pos.frame()) { if (frame != _pos.frame()) { // seek during seek - //seek(Pos(frame, AL::FRAMES)); seek(Pos(frame, false)); } done = audioPrefetch->seekDone(); @@ -335,9 +305,8 @@ void Audio::shutdown() void Audio::process(unsigned frames) { - // Disabled by Tim. p3.3.22 // extern int watchAudio; -// ++watchAudio; // make a simple watchdog happy +// ++watchAudio; // make a simple watchdog happy. Disabled. if (!MusEGlobal::checkAudioDevice()) return; if (msg) { @@ -377,8 +346,8 @@ void Audio::process(unsigned frames) startRolling(); } else if (isPlaying() && jackState == STOP) { - // p3.3.43 Make sure to stop bounce and freewheel mode, for example if user presses stop - // in QJackCtl before right-hand marker is reached (which is handled below). + // Make sure to stop bounce and freewheel mode, for example if user presses stop + // in QJackCtl before right-hand marker is reached (which is handled below). p3.3.43 //printf("Audio::process isPlaying() && jackState == STOP\n"); //if (_bounce) //{ @@ -485,15 +454,11 @@ void Audio::process(unsigned frames) //audioDevice->seekTransport(_loopFrame); Pos lp(_loopFrame, false); audioDevice->seekTransport(lp); - - // printf(" process: seek to %d, end %d\n", _loopFrame, loop.frame()); } } - - // p3.3.25 - if(extSyncFlag.value()) + if(extSyncFlag.value()) // p3.3.25 { nextTickPos = curTickPos + midiExtSyncTicks; // Probably not good - interfere with midi thread. @@ -549,7 +514,6 @@ void Audio::process1(unsigned samplePos, unsigned offset, unsigned frames) continue; track = (AudioTrack*)(*it); - // Added by T356. // For audio track types, synths etc. which need some kind of non-audio // (but possibly audio-affecting) processing always, even if their output path // is ultimately unconnected. @@ -560,23 +524,10 @@ void Audio::process1(unsigned samplePos, unsigned offset, unsigned frames) // It should be used for things like midi events, gui events etc. - things which need to // be done BEFORE all the AudioOutput::process() are called below. That does NOT include // audio processing, because THAT is done at the very end of this routine. - // This will also reset the track's processed flag. + // This will also reset the track's processed flag. Tim. track->preProcessAlways(); - - // Removed by T356 - /* - if (track->noOutRoute() && !track->noInRoute() && - track->type() != Track::AUDIO_AUX && track->type() != Track::AUDIO_OUTPUT) { - channels = track->channels(); - float* buffer[channels]; - float data[frames * channels]; - for (int i = 0; i < channels; ++i) - buffer[i] = data + i * frames; - track->copyData(samplePos, channels, frames, buffer); - } - */ - } + // Pre-process the metronome. ((AudioTrack*)metronome)->preProcessAlways(); @@ -584,29 +535,12 @@ void Audio::process1(unsigned samplePos, unsigned offset, unsigned frames) for (ciAudioOutput i = ol->begin(); i != ol->end(); ++i) (*i)->process(samplePos, offset, frames); - // Removed by T356 - /* - AuxList* auxl = song->auxs(); - for (ciAudioAux ia = auxl->begin(); ia != auxl->end(); ++ia) { - track = (AudioTrack*)(*ia); - if (track->noOutRoute()) { - channels = track->channels(); - float* buffer[channels]; - float data[frames * channels]; - for (int i = 0; i < channels; ++i) - buffer[i] = data + i * frames; - track->copyData(samplePos, channels, frames, buffer); - } - } - */ - - // Added by T356. // Were ANY tracks unprocessed as a result of processing all the AudioOutputs, above? // Not just unconnected ones, as previously done, but ones whose output path ultimately leads nowhere. // Those tracks were missed, until this fix. // Do them now. This will animate meters, and 'quietly' process some audio which needs to be done - - // for example synths really need to be processed, 'quietly' or not, otherwise the next time - // processing is 'turned on', if there was a backlog of events while it was off, then they all happen at once. + // for example synths really need to be processed, 'quietly' or not, otherwise the next time processing + // is 'turned on', if there was a backlog of events while it was off, then they all happen at once. Tim. for(ciTrack it = tl->begin(); it != tl->end(); ++it) { if((*it)->isMidiTrack()) @@ -623,9 +557,6 @@ void Audio::process1(unsigned samplePos, unsigned offset, unsigned frames) for (int i = 0; i < channels; ++i) buffer[i] = data + i * frames; //printf("Audio::process1 calling track->copyData for track:%s\n", track->name().toLatin1()); - - // p3.3.38 - //track->copyData(samplePos, channels, frames, buffer); track->copyData(samplePos, channels, -1, -1, frames, buffer); } } @@ -647,7 +578,7 @@ void Audio::processMsg(AudioMsg* msg) case AUDIO_ROUTEREMOVE: removeRoute(msg->sroute, msg->droute); break; - case AUDIO_REMOVEROUTES: // p3.3.55 + case AUDIO_REMOVEROUTES: removeAllRoutes(msg->sroute, msg->droute); break; //case AUDIO_VOL: @@ -670,7 +601,6 @@ void Audio::processMsg(AudioMsg* msg) break; //case AUDIO_SET_PLUGIN_CTRL_VAL: //msg->plugin->track()->setPluginCtrlVal(msg->ival, msg->dval); - // p3.3.43 // msg->snode->setPluginCtrlVal(msg->ival, msg->dval); // break; case AUDIO_SWAP_CONTROLLER_IDX: @@ -721,8 +651,8 @@ void Audio::processMsg(AudioMsg* msg) //printf("Audio::processMsg SEQM_RESET_DEVICES\n"); for (int i = 0; i < MIDI_PORTS; ++i) { - if(!midiPorts[i].device()) continue; // p4.0.15 - midiPorts[i].instrument()->reset(i, song->mtype()); + if(midiPorts[i].device()) + midiPorts[i].instrument()->reset(i, song->mtype()); } break; case SEQM_INIT_DEVICES: @@ -759,7 +689,7 @@ void Audio::processMsg(AudioMsg* msg) case MIDI_SHOW_INSTR_GUI: midiSeq->msgUpdatePollFd(); break; - case MIDI_SHOW_INSTR_NATIVE_GUI: // p4.0.20 + case MIDI_SHOW_INSTR_NATIVE_GUI: midiSeq->msgUpdatePollFd(); break; case SEQM_ADD_TEMPO: @@ -776,12 +706,12 @@ void Audio::processMsg(AudioMsg* msg) frameOffset = syncFrame - samplePos; } break; - case SEQM_ADD_TRACK: - case SEQM_REMOVE_TRACK: - case SEQM_CHANGE_TRACK: - case SEQM_ADD_PART: - case SEQM_REMOVE_PART: - case SEQM_CHANGE_PART: + //case SEQM_ADD_TRACK: + //case SEQM_REMOVE_TRACK: + //case SEQM_CHANGE_TRACK: + //case SEQM_ADD_PART: + //case SEQM_REMOVE_PART: + //case SEQM_CHANGE_PART: case SEQM_SET_TRACK_OUT_CHAN: case SEQM_SET_TRACK_OUT_PORT: case SEQM_REMAP_PORT_DRUM_CTL_EVS: @@ -813,143 +743,26 @@ void Audio::seek(const Pos& p) printf("Audio::seek already there\n"); return; } - - // p3.3.23 - //printf("Audio::seek frame:%d\n", p.frame()); + //printf("Audio::seek frame:%d\n", p.frame()); _pos = p; if (!MusEGlobal::checkAudioDevice()) return; syncFrame = audioDevice->framePos(); frameOffset = syncFrame - _pos.frame(); curTickPos = _pos.tick(); - // p4.0.22 - // Tell midi thread to tell ALSA devices to handle seek. - midiSeq->msgSeek(); - // We are in the audio thread. Directly seek Jack midi devices. + if (curTickPos == 0 && !song->record()) + audio->initDevices(); + for(iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) - { - MidiDevice* md = *i; - if(md->deviceType() == MidiDevice::JACK_MIDI) - md->handleSeek(); - } - - // Moved into MidiDevice::handleSeek - #if 0 - // p3.3.31 - // Don't send if external sync is on. The master, and our sync routing system will take care of that. - if(!extSyncFlag.value()) - { - - for(int port = 0; port < MIDI_PORTS; ++port) - { - MidiPort* mp = &midiPorts[port]; - MidiDevice* dev = mp->device(); - //if(!dev || !mp->syncInfo().MCOut()) - if(!dev || !mp->syncInfo().MRTOut()) - continue; - - // Added by T356: Shall we check for device write open flag to see if it's ok to send?... - // This means obey what the user has chosen for read/write in the midi port config dialog, - // which already takes into account whether the device is writable or not. - //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) - //if(!(dev->openFlags() & 1)) - // continue; - - //int port = dev->midiPort(); - - // By checking for no port here (-1), (and out of bounds), it means - // the device must be assigned to a port for these MMC commands to be sent. - // Without this check, interesting sync things can be done by the user without ever - // assigning any devices to ports ! - //if(port < 0 || port > MIDI_PORTS) - //if(port < -1 || port > MIDI_PORTS) - // continue; - - int beat = (curTickPos * 4) / MusEConfig::config.division; - - bool isPlaying=false; - if(state == PLAY) - isPlaying = true; - - mp->sendStop(); - mp->sendSongpos(beat); - if(isPlaying) - mp->sendContinue(); - } - } - #endif - - /* - if(genMCSync) - { - for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) - { - MidiDevice* dev = (*imd); - if(!dev->syncInfo().MCOut()) - continue; - - // Added by T356: Shall we check for device write open flag to see if it's ok to send?... - // This means obey what the user has chosen for read/write in the midi port config dialog, - // which already takes into account whether the device is writable or not. - //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) - //if(!(dev->openFlags() & 1)) - // continue; - - int port = dev->midiPort(); - - // By checking for no port here (-1), (and out of bounds), it means - // the device must be assigned to a port for these MMC commands to be sent. - // Without this check, interesting sync things can be done by the user without ever - // assigning any devices to ports ! - //if(port < 0 || port > MIDI_PORTS) - if(port < -1 || port > MIDI_PORTS) - continue; - - int beat = (curTickPos * 4) / MusEConfig::config.division; - - bool isPlaying=false; - if(state == PLAY) - isPlaying = true; - - if(port == -1) - // Send straight to the device... Copied from MidiPort. - { - MidiPlayEvent event(0, 0, 0, ME_STOP, 0, 0); - dev->putEvent(event); - - event.setType(ME_SONGPOS); - event.setA(beat); - dev->putEvent(event); - - if(isPlaying) - { - event.setType(ME_CONTINUE); - event.setA(0); - dev->putEvent(event); - } - } - else - // Go through the port... - { - MidiPort* mp = &midiPorts[port]; - - mp->sendStop(); - mp->sendSongpos(beat); - if(isPlaying) - mp->sendContinue(); - } - } - } - */ + (*i)->handleSeek(); //loopPassed = true; // for record loop mode if (state != LOOP2 && !freewheel()) { - // Changed by T356 08/17/08. We need to force prefetch to update, - // to ensure the most recent data. Things can happen to a part - // before play is pressed - such as part muting, part moving etc. - // Without a force, the wrong data was being played. //audioPrefetch->msgSeek(_pos.frame()); + // We need to force prefetch to update, to ensure the most recent data. + // Things can happen to a part before play is pressed - such as part muting, + // part moving etc. Without a force, the wrong data was being played. Tim 08/17/08 audioPrefetch->msgSeek(_pos.frame(), true); } @@ -984,8 +797,6 @@ void Audio::writeTick() void Audio::startRolling() { - // Changed by Tim. p3.3.8 - //startRecordPos = _pos; if (MusEGlobal::debugMsg) printf("startRolling - loopCount=%d, _pos=%d\n", _loopCount, _pos.tick()); @@ -1005,20 +816,9 @@ void Audio::startRolling() state = PLAY; write(sigFd, "1", 1); // Play - // p3.3.31 // Don't send if external sync is on. The master, and our sync routing system will take care of that. if(!extSyncFlag.value()) { - - // Changed by Tim. p3.3.6 - //if (genMMC) - // midiPorts[txSyncPort].sendSysex(mmcDeferredPlayMsg, sizeof(mmcDeferredPlayMsg)); - //if (genMCSync) { - // if (curTickPos) - // midiPorts[txSyncPort].sendContinue(); - // else - // midiPorts[txSyncPort].sendStart(); - // } for(int port = 0; port < MIDI_PORTS; ++port) { MidiPort* mp = &midiPorts[port]; @@ -1033,13 +833,9 @@ void Audio::startRolling() MidiSyncInfo& si = mp->syncInfo(); - //if(genMMC && si.MMCOut()) if(si.MMCOut()) - //mp->sendSysex(mmcDeferredPlayMsg, sizeof(mmcDeferredPlayMsg)); mp->sendMMCDeferredPlay(); - //if(genMCSync && si.MCOut()) - //if(si.MCOut()) if(si.MRTOut()) { if(curTickPos) @@ -1050,68 +846,6 @@ void Audio::startRolling() } } - /* - for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) - { - MidiDevice* dev = (*imd); - - // Shall we check open flags? - //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) - //if(!(dev->openFlags() & 1)) - // continue; - - int port = dev->midiPort(); - - // Without this -1 check, interesting sync things can be done by the user without ever - // assigning any devices to ports ! - //if(port < 0 || port > MIDI_PORTS) - if(port < -1 || port > MIDI_PORTS) - continue; - - MidiSyncInfo& si = dev->syncInfo(); - - if(port == -1) - // Send straight to the device... Copied from MidiPort. - { - if(genMMC && si.MMCOut()) - { - MidiPlayEvent event(0, 0, ME_SYSEX, mmcDeferredPlayMsg, sizeof(mmcDeferredPlayMsg)); - dev->putEvent(event); - } - - if(genMCSync && si.MCOut()) - { - if(curTickPos) - { - MidiPlayEvent event(0, 0, 0, ME_CONTINUE, 0, 0); - dev->putEvent(event); - } - else - { - MidiPlayEvent event(0, 0, 0, ME_START, 0, 0); - dev->putEvent(event); - } - } - } - else - // Go through the port... - { - MidiPort* mp = &midiPorts[port]; - - if(genMMC && si.MMCOut()) - mp->sendSysex(mmcDeferredPlayMsg, sizeof(mmcDeferredPlayMsg)); - - if(genMCSync && si.MCOut()) - { - if(curTickPos) - mp->sendContinue(); - else - mp->sendStart(); - } - } - } - */ - if (MusEGlobal::precountEnableFlag && song->click() && !extSyncFlag.value() @@ -1150,9 +884,7 @@ void Audio::startRolling() if(mp->device() != NULL) { //printf("send enable sustain!!!!!!!! port %d ch %d\n", i,ch); MidiPlayEvent ev(0, i, ch, ME_CONTROLLER, CTRL_SUSTAIN, 127); - - // may cause problems, called from audio thread - mp->device()->playEvents()->add(ev); + mp->device()->addScheduledEvent(ev); // TODO: Not working? Try putEvent } } } @@ -1172,220 +904,14 @@ void Audio::stopRolling() state = STOP; - //playStateExt = false; // not playing // Moved here from MidiSeq::processStop() p4.0.22 - - // p4.0.22 - // Tell midi thread to clear ALSA device notes and stop stuck notes. - midiSeq->msgStop(); - // We are in the audio thread. Directly clear Jack midi device notes and stop stuck notes. + midiSeq->setExternalPlayState(false); // not playing Moved here from MidiSeq::processStop() p4.0.34 + for(iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { MidiDevice* md = *id; - if(md->deviceType() == MidiDevice::JACK_MIDI) - md->handleStop(); + md->handleStop(); } - // Moved into MidiDevice::handleStop() // p4.0.22 - #if 0 //TODO - //--------------------------------------------------- - // reset sustain - //--------------------------------------------------- - - - // clear sustain - for (int i = 0; i < MIDI_PORTS; ++i) { - MidiPort* mp = &midiPorts[i]; - for (int ch = 0; ch < MIDI_CHANNELS; ++ch) { - if (mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) { - if(mp->device()!=NULL) { - //printf("send clear sustain!!!!!!!! port %d ch %d\n", i,ch); - MidiPlayEvent ev(0, i, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); - // may cause problems, called from audio thread - mp->device()->putEvent(ev); - } - } - } - } - #endif - - // Moved into MidiDevice::handleStop() // p4.0.22 - #if 0 - // p3.3.31 - // Don't send if external sync is on. The master, and our sync routing system will take care of that. - if(!extSyncFlag.value()) - { - - // Changed by Tim. p3.3.6 - //MidiPort* syncPort = &midiPorts[txSyncPort]; - //if (genMMC) { - // unsigned char mmcPos[] = { - // 0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, - // 0, 0, 0, 0, 0 - // }; - // int frame = tempomap.tick2frame(curTickPos); - // MTC mtc(double(frame) / double(MusEGlobal::sampleRate)); - // mmcPos[6] = mtc.h() | (mtcType << 5); - // mmcPos[7] = mtc.m(); - // mmcPos[8] = mtc.s(); - // mmcPos[9] = mtc.f(); - // mmcPos[10] = mtc.sf(); - // syncPort->sendSysex(mmcStopMsg, sizeof(mmcStopMsg)); - // syncPort->sendSysex(mmcPos, sizeof(mmcPos)); - // } - //if (genMCSync) { // Midi Clock - // send STOP and - // "set song position pointer" - // syncPort->sendStop(); - // syncPort->sendSongpos(curTickPos * 4 / MusEConfig::config.division); - // } - for(int port = 0; port < MIDI_PORTS; ++port) - { - MidiPort* mp = &midiPorts[port]; - MidiDevice* dev = mp->device(); - if(!dev) - continue; - - // Shall we check open flags? - //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) - //if(!(dev->openFlags() & 1)) - // continue; - - MidiSyncInfo& si = mp->syncInfo(); - - //if(genMMC && si.MMCOut()) - if(si.MMCOut()) - { - //unsigned char mmcPos[] = { - // 0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, - // 0, 0, 0, 0, 0 - // }; - - // p3.3.31 - /* - int frame = tempomap.tick2frame(curTickPos); - MTC mtc(double(frame) / double(MusEGlobal::sampleRate)); - */ - - //mmcPos[6] = mtc.h() | (mtcType << 5); - //mmcPos[7] = mtc.m(); - //mmcPos[8] = mtc.s(); - //mmcPos[9] = mtc.f(); - //mmcPos[10] = mtc.sf(); - - //mp->sendSysex(mmcStopMsg, sizeof(mmcStopMsg)); - mp->sendMMCStop(); - //mp->sendSysex(mmcPos, sizeof(mmcPos)); - - // p3.3.31 - // Added check of option send continue not start. - // Hmm, is this required? Seems to make other devices unhappy. - /* - if(!si.sendContNotStart()) - mp->sendMMCLocate(mtc.h() | (mtcType << 5), - mtc.m(), mtc.s(), mtc.f(), mtc.sf()); - */ - - } - - //if(genMCSync && si.MCOut()) // Midi Clock - //if(si.MCOut()) // Midi Clock - if(si.MRTOut()) // - { - // send STOP and - // "set song position pointer" - mp->sendStop(); - - // p3.3.31 - // Added check of option send continue not start. - // Hmm, is this required? Seems to make other devices unhappy. - /* - if(!si.sendContNotStart()) - mp->sendSongpos(curTickPos * 4 / MusEConfig::config.division); - */ - - } - } - } - #endif - - /* - for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) - { - MidiDevice* dev = (*imd); - - // Shall we check open flags? - //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) - //if(!(dev->openFlags() & 1)) - // continue; - - int port = dev->midiPort(); - - // Without this -1 check, interesting sync things can be done by the user without ever - // assigning any devices to ports ! - //if(port < 0 || port > MIDI_PORTS) - if(port < -1 || port > MIDI_PORTS) - continue; - - MidiSyncInfo& si = dev->syncInfo(); - - MidiPort* mp = 0; - if(port != -1) - mp = &midiPorts[port]; - - if(genMMC && si.MMCOut()) - { - unsigned char mmcPos[] = { - 0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, - 0, 0, 0, 0, 0 - }; - int frame = tempomap.tick2frame(curTickPos); - MTC mtc(double(frame) / double(MusEGlobal::sampleRate)); - mmcPos[6] = mtc.h() | (mtcType << 5); - mmcPos[7] = mtc.m(); - mmcPos[8] = mtc.s(); - mmcPos[9] = mtc.f(); - mmcPos[10] = mtc.sf(); - - if(mp) - // Go through the port... - { - mp->sendSysex(mmcStopMsg, sizeof(mmcStopMsg)); - mp->sendSysex(mmcPos, sizeof(mmcPos)); - } - else - // Send straight to the device... Copied from MidiPort. - { - MidiPlayEvent event(0, 0, ME_SYSEX, mmcStopMsg, sizeof(mmcStopMsg)); - dev->putEvent(event); - - event.setData(mmcPos, sizeof(mmcPos)); - dev->putEvent(event); - } - } - - if(genMCSync && si.MCOut()) // Midi Clock - { - // send STOP and - // "set song position pointer" - if(mp) - // Go through the port... - { - mp->sendStop(); - mp->sendSongpos(curTickPos * 4 / MusEConfig::config.division); - } - else - // Send straight to the device... Copied from MidiPort. - { - MidiPlayEvent event(0, 0, 0, ME_STOP, 0, 0); - dev->putEvent(event); - event.setType(ME_SONGPOS); - event.setA(curTickPos * 4 / MusEConfig::config.division); - dev->putEvent(event); - } - } - } - */ - WaveTrackList* tracks = song->waves(); for (iWaveTrack i = tracks->begin(); i != tracks->end(); ++i) { WaveTrack* track = *i; diff --git a/muse2/muse/audio.h b/muse2/muse/audio.h index 86f38004..03e48e23 100644 --- a/muse2/muse/audio.h +++ b/muse2/muse/audio.h @@ -4,6 +4,7 @@ // $Id: audio.h,v 1.25.2.13 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -239,8 +240,8 @@ class Audio { bool sendMessage(AudioMsg* m, bool doUndo); void msgRemoveRoute(Route, Route); void msgRemoveRoute1(Route, Route); - void msgRemoveRoutes(Route, Route); // p3.3.55 - void msgRemoveRoutes1(Route, Route); // p3.3.55 + void msgRemoveRoutes(Route, Route); + void msgRemoveRoutes1(Route, Route); void msgAddRoute(Route, Route); void msgAddRoute1(Route, Route); void msgAddPlugin(AudioTrack*, int idx, PluginI* plugin); @@ -290,7 +291,8 @@ class Audio { int loopCount() { return _loopCount; } // Number of times we have looped so far unsigned loopFrame() { return _loopFrame; } - int tickPos() const { return curTickPos; } + unsigned tickPos() const { return curTickPos; } + unsigned nextTick() const { return nextTickPos; } int timestamp() const; void processMidi(); unsigned curFrame() const; diff --git a/muse2/muse/driver/alsamidi.cpp b/muse2/muse/driver/alsamidi.cpp index 07688a33..9a62dbdd 100644 --- a/muse2/muse/driver/alsamidi.cpp +++ b/muse2/muse/driver/alsamidi.cpp @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: alsamidi.cpp,v 1.8.2.7 2009/11/19 04:20:33 terminator356 Exp $ // (C) Copyright 2000-2001 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -25,16 +26,18 @@ #include "alsamidi.h" #include "globals.h" #include "midi.h" -#include "mididev.h" +//#include "mididev.h" #include "../midiport.h" #include "../midiseq.h" #include "../midictrl.h" #include "../audio.h" -#include "mpevent.h" +//#include "mpevent.h" //#include "sync.h" #include "utils.h" #include "audiodev.h" #include "xml.h" +#include "part.h" +#include "gconfig.h" static int alsaSeqFdi = -1; static int alsaSeqFdo = -1; @@ -50,6 +53,8 @@ MidiAlsaDevice::MidiAlsaDevice(const snd_seq_addr_t& a, const QString& n) : MidiDevice(n) { adr = a; + stopPending = false; + seekPending = false; init(); } @@ -132,7 +137,7 @@ void MidiAlsaDevice::close() // Allocated on stack, no need to call snd_seq_port_subscribe_free() later. snd_seq_port_subscribe_alloca(&subs); - // Changed by T356. This function appears to be called only by MidiPort::setMidiDevice(), + // This function appears to be called only by MidiPort::setMidiDevice(), // which closes then opens the device. // Because the open flags are set BEFORE setMidiDevice() is called, we must ignore the flags. // @@ -146,7 +151,7 @@ void MidiAlsaDevice::close() // dst 16:0 // only sometimes (not when playing notes), but with jack midi turned on, // we don't get the messages. With jack stopped we get the messages - // no matter if jack midi is turned on or not. + // no matter if jack midi is turned on or not. Tim. //if (_openFlags & 1) { //if (!(_openFlags & 1)) @@ -191,95 +196,27 @@ void MidiAlsaDevice::close() void MidiAlsaDevice::writeRouting(int level, Xml& xml) const { - // p3.3.45 // If this device is not actually in use by the song, do not write any routes. - // This prevents bogus routes from being saved and propagated in the med file. + // This prevents bogus routes from being saved and propagated in the med file. Tim. if(midiPort() == -1) return; QString s; - /* - //if(rwFlags() & 2) // Readable - { - //RouteList* rl = _inRoutes; - //for (ciRoute r = rl->begin(); r != rl->end(); ++r) - for (ciRoute r = _inRoutes.begin(); r != _inRoutes.end(); ++r) - { - // Since an ALSA midi device supports read + write, this is the only way we can tell if this route is using the device as input. - if(r->type == Route::TRACK_ROUTE) - continue; - - if(!r->name().isEmpty()) - { - xml.tag(level++, "Route"); - - //xml.strTag(level, "srcNode", r->name()); - xml.tag(level, "source type=\"%d\" name=\"%s\"/", r->type, r->name().toLatin1().constData()); - - //xml.strTag(level, "dstNode", name()); - xml.tag(level, "dest type=\"%d\" name=\"%s\"/", Route::ALSA_MIDI_ROUTE, name().toLatin1().constData()); - - xml.etag(level--, "Route"); - } - } - } - */ - for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r) { - //if(r->type != Route::TRACK_ROUTE) - //{ - // printf("MidiAlsaDevice::writeRouting Warning out route is not TRACK_ROUTE type\n"); - // continue; - //} - if(!r->name().isEmpty()) { - //xml.tag(level++, "Route"); - s = QT_TRANSLATE_NOOP("@default", "Route"); if(r->channel != -1) s += QString(QT_TRANSLATE_NOOP("@default", " channel=\"%1\"")).arg(r->channel); xml.tag(level++, s.toLatin1().constData()); - - /* - //xml.strTag(level, "srcNode", name()); - if(r->channel != -1) - //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::ALSA_MIDI_ROUTE, r->channel, name().toLatin1().constData()); - //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, r->channel, name().toLatin1().constData()); - xml.tag(level, "source devtype=\"%d\" channel=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, r->channel, name().toLatin1().constData()); - else - //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::ALSA_MIDI_ROUTE, name().toLatin1().constData()); - //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, name().toLatin1().constData()); - */ - //xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, name().toLatin1().constData()); - xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, Xml::xmlString(name()).toLatin1().constData()); - - /* - //xml.strTag(level, "dstNode", r->name()); - if(r->channel != -1) - { - if(r->type == Route::MIDI_DEVICE_ROUTE) - xml.tag(level, "dest devtype=\"%d\" channel=\"%d\" name=\"%s\"/", r->device->deviceType(), r->channel, r->name().toLatin1().constData()); - else - xml.tag(level, "dest type=\"%d\" channel=\"%d\" name=\"%s\"/", r->type, r->channel, r->name().toLatin1().constData()); - } - else - { - if(r->type == Route::MIDI_DEVICE_ROUTE) - xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", r->device->deviceType(), r->name().toLatin1().constData()); - else - xml.tag(level, "dest type=\"%d\" name=\"%s\"/", r->type, r->name().toLatin1().constData()); - } - */ - + xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, Xml::xmlString(name()).toLatin1().constData()); s = QT_TRANSLATE_NOOP("@default", "dest"); if(r->type == Route::MIDI_DEVICE_ROUTE) s += QString(QT_TRANSLATE_NOOP("@default", " devtype=\"%1\"")).arg(r->device->deviceType()); else if(r->type != Route::TRACK_ROUTE) s += QString(QT_TRANSLATE_NOOP("@default", " type=\"%1\"")).arg(r->type); - //s += QString(QT_TRANSLATE_NOOP("@default", " name=\"%1\"/")).arg(r->name()); s += QString(QT_TRANSLATE_NOOP("@default", " name=\"%1\"/")).arg(Xml::xmlString(r->name())); xml.tag(level, s.toLatin1().constData()); @@ -326,12 +263,9 @@ bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e) int a = e.dataA(); int b = e.dataB(); int chn = e.channel(); - // p3.3.37 - //if (a < 0x1000) { // 7 Bit Controller if (a < CTRL_14_OFFSET) { // 7 Bit Controller snd_seq_ev_set_controller(&event, chn, a, b); } - //else if (a < 0x20000) { // 14 bit high resolution controller else if (a < CTRL_RPN_OFFSET) { // 14 bit high resolution controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; @@ -339,7 +273,6 @@ bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e) snd_seq_ev_set_controller(&event, chn, a, b); event.type = SND_SEQ_EVENT_CONTROL14; } - //else if (a < 0x30000) { // RPN 7-Bit Controller else if (a < CTRL_NRPN_OFFSET) { // RPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; @@ -348,7 +281,6 @@ bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e) snd_seq_ev_set_controller(&event, chn, a, b); event.type = SND_SEQ_EVENT_REGPARAM; } - //else if (a < 0x40000) { // NRPN 7-Bit Controller else if (a < CTRL_INTERNAL_OFFSET) { // NRPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; @@ -357,7 +289,6 @@ bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e) snd_seq_ev_set_controller(&event, chn, a, b); event.type = SND_SEQ_EVENT_NONREGPARAM; } - //else if (a < 0x60000) { // RPN14 Controller else if (a < CTRL_NRPN14_OFFSET) { // RPN14 Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; @@ -365,7 +296,6 @@ bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e) snd_seq_ev_set_controller(&event, chn, a, b); event.type = SND_SEQ_EVENT_REGPARAM; } - //else if (a < 0x70000) { // NRPN14 Controller else if (a < CTRL_NONE_OFFSET) { // NRPN14 Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; @@ -465,6 +395,250 @@ bool MidiAlsaDevice::putEvent(snd_seq_event_t* event) return true; } +//--------------------------------------------------------- +// processMidi +// Called from ALSA midi sequencer thread only. +//--------------------------------------------------------- + +void MidiAlsaDevice::processMidi() +{ + bool stop = stopPending; // Snapshots + bool seek = seekPending; // + seekPending = stopPending = false; + // Transfer the stuck notes FIFO to the play events list. + // FIXME It would be faster to have MidiAlsaDevice automatically add the stuck note so that only + // one FIFO would be needed. But that requires passing an extra 'tick' and 'off velocity' in + // addScheduledEvent, which felt too weird. + while(!stuckNotesFifo.isEmpty()) + _stuckNotes.add(stuckNotesFifo.get()); + + bool extsync = extSyncFlag.value(); + //int frameOffset = getFrameOffset(); + //int nextTick = audio->nextTick(); + + // We're in the ALSA midi thread. audio->isPlaying() might not be true during seek right now. + //if(stop || (seek && audio->isPlaying())) + if(stop || seek) + { + //--------------------------------------------------- + // Clear all notes and handle stuck notes + //--------------------------------------------------- + playEventFifo.clear(); + _playEvents.clear(); + //printf("transferring stuck notes\n"); + for(iMPEvent i = _stuckNotes.begin(); i != _stuckNotes.end(); ++i) + { + //printf(" stuck note\n"); + MidiPlayEvent ev = *i; + ev.setTime(0); + _playEvents.add(ev); + } + _stuckNotes.clear(); + } + else + { + // Transfer the play events FIFO to the play events list. + while(!playEventFifo.isEmpty()) + _playEvents.add(playEventFifo.get()); + + /* TODO Handle these more directly than putting them into play events list. + //if(audio->isPlaying()) + { + iMPEvent k; + for (k = _stuckNotes.begin(); k != _stuckNotes.end(); ++k) { + if (k->time() >= nextTick) + break; + MidiPlayEvent ev(*k); + if(extsync) // p3.3.25 + ev.setTime(k->time()); + else + ev.setTime(tempomap.tick2frame(k->time()) + frameOffset); + _playEvents.add(ev); + } + _stuckNotes.erase(_stuckNotes.begin(), k); + } + */ + processStuckNotes(); + } + + /* Instead, done immediately in handleStop and handleSeek using putEvent. + if(stop) + { + // reset sustain... + MidiPort* mp = &midiPorts[_port]; + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) + { + //printf("send clear sustain!!!!!!!! port %d ch %d\n", i,ch); + MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); + _playEvents.add(ev); + } + } + } + if(seek) + { + // Send new contoller values... + for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) + { + MidiCtrlValList* vl = ivl->second; + iMidiCtrlVal imcv = vl->iValue(pos); + if(imcv != vl->end()) { + Part* p = imcv->second.part; + unsigned t = (unsigned)imcv->first; + // Do not add values that are outside of the part. + if(p && t >= p->tick() && t < (p->tick() + p->lenTick()) ) + _playEvents.add(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); + } + } + } */ + + if (_playEvents.empty()) + return; + + int port = midiPort(); + MidiPort* mp = port != -1 ? &midiPorts[port] : 0; + unsigned curFrame = audio->curFrame(); + int tickpos = audio->tickPos(); + + // Play all events up to current frame. + iMPEvent i = _playEvents.begin(); + for (; i != _playEvents.end(); ++i) { + if (i->time() > (extsync ? tickpos : curFrame)) // p3.3.25 Check: Should be nextTickPos? p4.0.34 + break; + if(mp){ + if (mp->sendEvent(*i)) + break; + } + else + if(putMidiEvent(*i)) + break; + } + _playEvents.erase(_playEvents.begin(), i); +} + +//--------------------------------------------------------- +// handleStop +//--------------------------------------------------------- + +void MidiAlsaDevice::handleStop() +{ + // If the device is not in use by a port, don't bother it. + if(_port == -1) + return; + + stopPending = true; // Trigger stop handling in processMidi. + + //--------------------------------------------------- + // reset sustain + //--------------------------------------------------- + + MidiPort* mp = &midiPorts[_port]; + for(int ch = 0; ch < MIDI_CHANNELS; ++ch) + { + if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) + { + //printf("send clear sustain!!!!!!!! port %d ch %d\n", i,ch); + MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); + putMidiEvent(ev); + } + } + + //--------------------------------------------------- + // send midi stop + //--------------------------------------------------- + + // Don't send if external sync is on. The master, and our sync routing system will take care of that. + if(!extSyncFlag.value()) + { + // Shall we check open flags? + //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) + //if(!(dev->openFlags() & 1)) + // return; + + MidiSyncInfo& si = mp->syncInfo(); + if(si.MMCOut()) + mp->sendMMCStop(); + + if(si.MRTOut()) + { + // Send STOP + mp->sendStop(); + + // Added check of option send continue not start. p3.3.31 + // Hmm, is this required? Seems to make other devices unhappy. + // (Could try now that this is in MidiDevice. p4.0.22 ) + //if(!si.sendContNotStart()) + // mp->sendSongpos(audio->tickPos() * 4 / config.division); + } + } +} + +//--------------------------------------------------------- +// handleSeek +//--------------------------------------------------------- + +void MidiAlsaDevice::handleSeek() +{ + // If the device is not in use by a port, don't bother it. + if(_port == -1) + return; + + seekPending = true; // Trigger seek handling in processMidi. + + MidiPort* mp = &midiPorts[_port]; + MidiCtrlValListList* cll = mp->controller(); + int pos = audio->tickPos(); + + //--------------------------------------------------- + // Send new contoller values + //--------------------------------------------------- + + for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) + { + MidiCtrlValList* vl = ivl->second; + iMidiCtrlVal imcv = vl->iValue(pos); + if(imcv != vl->end()) + { + Part* p = imcv->second.part; + unsigned t = (unsigned)imcv->first; + // Do not add values that are outside of the part. + if(p && t >= p->tick() && t < (p->tick() + p->lenTick()) ) + //_playEvents.add(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); + // Hmm, play event list for immediate playback? Try putEvent instead. p4.0.34 + putMidiEvent(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); + } + } + + //--------------------------------------------------- + // Send STOP and "set song position pointer" + //--------------------------------------------------- + + // Don't send if external sync is on. The master, and our sync routing system will take care of that. p3.3.31 + if(!extSyncFlag.value()) + { + if(mp->syncInfo().MRTOut()) + { + // Shall we check for device write open flag to see if it's ok to send?... + // This means obey what the user has chosen for read/write in the midi port config dialog, + // which already takes into account whether the device is writable or not. + //if(!(rwFlags() & 0x1) || !(openFlags() & 1)) + //if(!(openFlags() & 1)) + // continue; + + int beat = (pos * 4) / MusEConfig::config.division; + + //bool isPlaying = (state == PLAY); + bool isPlaying = audio->isPlaying(); // TODO Check this it includes LOOP1 and LOOP2 besides PLAY. p4.0.22 + + mp->sendStop(); + mp->sendSongpos(beat); + if(isPlaying) + mp->sendContinue(); + } + } +} + //--------------------------------------------------------- // initMidiAlsa // return true on error @@ -539,7 +713,6 @@ bool initMidiAlsa() } } - // p3.3.38 //snd_seq_set_client_name(alsaSeq, "MusE Sequencer"); snd_seq_set_client_name(alsaSeq, audioDevice->clientName()); @@ -773,31 +946,6 @@ void alsaProcessMidiInput() return; } - /* - if(curPort == -1) - { - if(mdev == 0) - { - if (MusEGlobal::debugMsg) - { - fprintf(stderr, "no port %d:%d found for received alsa event\n", - ev->source.client, ev->source.port); - } - } - else - { - // Allow the sync detect mechanisms to work, even if device is not assigned to a port. - if(ev->type == SND_SEQ_EVENT_CLOCK) - mdev->syncInfo().trigMCSyncDetect(); - else - if(ev->type == SND_SEQ_EVENT_TICK) - mdev->syncInfo().trigTickDetect(); - } - snd_seq_free_event(ev); - return; - } - */ - event.setType(0); // mark as unused event.setPort(curPort); event.setB(0); @@ -867,7 +1015,6 @@ void alsaProcessMidiInput() break; case SND_SEQ_EVENT_SYSEX: - // TODO: Deal with large sysex, which are broken up into chunks! // For now, do not accept if the first byte is not SYSEX or the last byte is not EOX, // meaning it's a chunk, possibly with more chunks to follow. @@ -916,13 +1063,7 @@ void alsaProcessMidiInput() break; } if(event.type()) - { mdev->recordEvent(event); - // p3.3.26 1/23/10 Moved to MidiDevice now. Anticipating Jack midi support, so don't make it ALSA specific. Tim. - //if(ev->type != SND_SEQ_EVENT_SYSEX) - // Trigger general activity indicator detector. Sysex has no channel, don't trigger. - // midiPorts[curPort].syncInfo().trigActDetect(event.channel()); - } snd_seq_free_event(ev); if (rv == 0) diff --git a/muse2/muse/driver/alsamidi.h b/muse2/muse/driver/alsamidi.h index 01894f8c..13e07ca4 100644 --- a/muse2/muse/driver/alsamidi.h +++ b/muse2/muse/driver/alsamidi.h @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: alsamidi.h,v 1.2 2004/01/14 09:06:43 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -26,6 +27,7 @@ #include #include +#include "mpevent.h" #include "mididev.h" class Xml; @@ -39,6 +41,13 @@ class MidiAlsaDevice : public MidiDevice { snd_seq_addr_t adr; private: + // Special for ALSA midi device: Play event list is processed in the ALSA midi sequencer thread. + // Need this FIFO, to decouple from audio thread which adds events to the list. + MidiFifo playEventFifo; + MidiFifo stuckNotesFifo; + volatile bool stopPending; + volatile bool seekPending; + virtual QString open(); virtual void close(); virtual void processInput() {} @@ -49,17 +58,22 @@ class MidiAlsaDevice : public MidiDevice { virtual bool putMidiEvent(const MidiPlayEvent&); public: - //MidiAlsaDevice() {} // p3.3.55 Removed MidiAlsaDevice(const snd_seq_addr_t&, const QString& name); virtual ~MidiAlsaDevice() {} - //virtual void* clientPort() { return (void*)&adr; } - // p3.3.55 virtual void* inClientPort() { return (void*)&adr; } // For ALSA midi, in/out client ports are the same. virtual void* outClientPort() { return (void*)&adr; } // That is, ALSA midi client ports can be both r/w. virtual void writeRouting(int, Xml&) const; virtual inline int deviceType() const { return ALSA_MIDI; } + // Schedule an event for playback. Returns false if event cannot be delivered. + virtual bool addScheduledEvent(const MidiPlayEvent& ev) { return !playEventFifo.put(ev); } + // Add a stuck note. Returns false if event cannot be delivered. + virtual bool addStuckNote(const MidiPlayEvent& ev) { return !stuckNotesFifo.put(ev); } + // Play all events up to current frame. + virtual void processMidi(); + virtual void handleStop(); + virtual void handleSeek(); }; extern bool initMidiAlsa(); diff --git a/muse2/muse/driver/jackmidi.cpp b/muse2/muse/driver/jackmidi.cpp index 2e790a10..a4577f0b 100644 --- a/muse2/muse/driver/jackmidi.cpp +++ b/muse2/muse/driver/jackmidi.cpp @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: jackmidi.cpp,v 1.1.1.1 2010/01/27 09:06:43 terminator356 Exp $ // (C) Copyright 1999-2010 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -48,128 +49,16 @@ extern unsigned int volatile lastExtMidiSyncTick; -///int jackmidi_pi[2]; -///int jackmidi_po[2]; - -//extern muse_jack_midi_buffer jack_midi_out_data[JACK_MIDI_CHANNELS]; -//extern muse_jack_midi_buffer jack_midi_in_data[JACK_MIDI_CHANNELS]; -///extern jack_port_t *midi_port_in[JACK_MIDI_CHANNELS]; -///extern jack_port_t *midi_port_out[JACK_MIDI_CHANNELS]; - -///MidiJackDevice* gmdev = NULL; - -///int* jackSeq; -//static snd_seq_addr_t musePort; - -//int MidiJackDevice::_nextOutIdNum = 0; -//int MidiJackDevice::_nextInIdNum = 0; - -//int JackMidiPortList::_nextOutIdNum = 0; -//int JackMidiPortList::_nextInIdNum = 0; - -//JackMidiPortList jackMidiClientPorts; - - -/* -//--------------------------------------------------------- -// JackMidiPortList -//--------------------------------------------------------- - -JackMidiPortList::JackMidiPortList() -{ - -} - -JackMidiPortList::~JackMidiPortList() -{ - -} - -iJackMidiPort JackMidiPortList::createClientPort(int flags) // 1 = writable, 2 = readable - do not mix -{ - if(flags & 1) - { - char buf[80]; - snprintf(buf, 80, "muse-jack-midi-out-%d", _nextOutIdNum); - jack_port_t* _client_jackport = (jack_port_t*)audioDevice->registerOutPort(buf, true); - if(_client_jackport == NULL) - { - fprintf(stderr, "JackMidiPortList::createClientPort failed to register jack-midi-out\n"); - //return QString("Could not register jack-midi-out client port"); - return end(); - } - else - { - JackMidiPort jmp(_client_jackport, QString(buf), flags); - _nextOutIdNum++; - return insert(begin(), std::pair(_client_jackport, jmp)); - } - } - else - if(flags & 2) - { - char buf[80]; - snprintf(buf, 80, "muse-jack-midi-in-%d", _nextInIdNum); - jack_port_t* _client_jackport = (jack_port_t*)audioDevice->registerInPort(buf, true); - if(_client_jackport == NULL) - { - fprintf(stderr, "JackMidiPortList::createClientPort failed to register jack-midi-in\n"); - return end(); - } - else - { - JackMidiPort jmp(_client_jackport, QString(buf), flags); - _nextInIdNum++; - return insert(begin(), std::pair(_client_jackport, jmp)); - } - } - return end(); -} - -// Return true if removed. -bool JackMidiPortList::removeClientPort(jack_port_t* port) -{ - iJackMidiPort ijp = find(port); - if(ijp == end()) - return false; - - // Is output? - if(ijp->second._flags & 1) - _nextOutIdNum--; - // Is input? - if(ijp->second._flags & 2) - _nextInIdNum--; - - erase(ijp); - - audioDevice->unregisterPort(port); - - return true; -} -*/ - //--------------------------------------------------------- // MidiJackDevice // in_jack_port or out_jack_port can be null //--------------------------------------------------------- -//MidiJackDevice::MidiJackDevice(const int& a, const QString& n) -//MidiJackDevice::MidiJackDevice(jack_port_t* jack_port, const QString& n) -// p3.3.55 -//MidiJackDevice::MidiJackDevice(jack_port_t* in_jack_port, jack_port_t* out_jack_port, const QString& n) MidiJackDevice::MidiJackDevice(const QString& n) : MidiDevice(n) { - //_client_jackport = 0; - - //_client_jackport = jack_port; - // p3.3.55 - //_in_client_jackport = in_jack_port; - //_out_client_jackport = out_jack_port; _in_client_jackport = NULL; _out_client_jackport = NULL; - - //adr = a; init(); } @@ -179,10 +68,6 @@ MidiJackDevice::~MidiJackDevice() printf("MidiJackDevice::~MidiJackDevice()\n"); #endif - //if(_client_jackport) - // audioDevice->unregisterPort(_client_jackport); - // p3.3.55 - if(audioDevice) { if(_in_client_jackport) @@ -194,69 +79,13 @@ MidiJackDevice::~MidiJackDevice() //close(); } -/* -//--------------------------------------------------------- -// select[RW]fd -//--------------------------------------------------------- - -int MidiJackDevice::selectRfd() -{ - return jackmidi_pi[0]; -} - -int MidiJackDevice::selectWfd() -{ - return jackmidi_po[0]; -} -*/ - //--------------------------------------------------------- // createJackMidiDevice // If name parameter is blank, creates a new (locally) unique one. //--------------------------------------------------------- -//QString MidiJackDevice::createJackMidiDevice(int rwflags) // 1:Writable 2: Readable. Do not mix. -//MidiDevice* MidiJackDevice::createJackMidiDevice(QString name, int rwflags) // 1:Writable 2: Readable. Do not mix. -MidiDevice* MidiJackDevice::createJackMidiDevice(QString name, int rwflags) // p3.3.55 1:Writable 2: Readable 3: Writable + Readable +MidiDevice* MidiJackDevice::createJackMidiDevice(QString name, int rwflags) // 1:Writable 2: Readable 3: Writable + Readable { -/// _openFlags &= _rwFlags; // restrict to available bits - -/// #ifdef JACK_MIDI_DEBUG -/// printf("MidiJackDevice::open %s\n", name.toLatin1().constData()); -/// #endif - - //jack_port_t* jp = jack_port_by_name(_client, name().toLatin1().constData()); -/// jack_port_t* jp = (jack_port_t*)audioDevice->findPort(name().toLatin1().constData()); - -/// if(!jp) -/// { -/// printf("MidiJackDevice::open: Jack midi port %s not found!\n", name().toLatin1().constData()); -/// _writeEnable = false; -/// _readEnable = false; -/// return QString("Jack midi port not found"); -/// } - -/// int pf = jack_port_flags(jp); - - //if(!name.isEmpty()) - //{ - // Does not work. - // if(audioDevice->findPort(name.toLatin1().constData())) - // { - // fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Given port name %s already exists!\n", name.toLatin1().constData()); - // return 0; - // } - //} - - //jack_port_t* client_jackport = NULL; - // p3.3.55 - ///jack_port_t* in_client_jackport = NULL; - ///jack_port_t* out_client_jackport = NULL; - - //char buf[80]; - - - // p3.3.55 int ni = 0; if(name.isEmpty()) { @@ -273,188 +102,7 @@ MidiDevice* MidiJackDevice::createJackMidiDevice(QString name, int rwflags) // p return 0; } - // If Jack port can receive data from us and we actually want to... - //if((pf & JackPortIsInput) && (_openFlags & 1)) - ///if(rwflags & 1) - ///{ - /* p3.3.55 Removed. - if(name.isEmpty()) - { - //snprintf(buf, 80, "muse-jack-midi-out-%d", _nextOutIdNum); - for(int i = 0; ; ++i) - { - //snprintf(buf, 80, "midi-out-%d", i); - name.sprintf("midi-out-%d", i); - - if(!midiDevices.find(name)) - { - // Does not work. - //if(!audioDevice->findPort(buf)) - // break; - //client_jackport = (jack_port_t*)audioDevice->registerOutPort(buf, true); - if(audioDevice->deviceType() == AudioDevice::JACK_AUDIO) // p3.3.52 - { - //client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.toLatin1().constData(), true); - out_client_jackport = (jack_port_t*)audioDevice->registerOutPort((name + QString("_out")).toLatin1().constData(), true); // p3.3.55 - //if(client_jackport) - if(out_client_jackport) // p3.3.55 - break; - } - else - break; - } - - if(i == 65535) - { - fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Can't find unused output port name!\n"); - return 0; - } - } - //name = QString(buf); - } - else - */ - - /* - { - if(audioDevice->deviceType() == AudioDevice::JACK_AUDIO) // p3.3.52 - { - //client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.toLatin1().constData(), true); - out_client_jackport = (jack_port_t*)audioDevice->registerOutPort((name + QString(JACK_MIDI_OUT_PORT_SUFFIX)).toLatin1().constData(), true); // p3.3.55 - //if(!client_jackport) - if(!out_client_jackport) // p3.3.55 - { - //fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed creating output port name %s\n", name.toLatin1().constData()); - fprintf(stderr, "MusE: createJackMidiDevice failed creating output port name %s\n", (name + QString(JACK_MIDI_OUT_PORT_SUFFIX)).toLatin1().constData()); // p3.3.55 - - //return 0; - rwflags &= ~1; // p3.3.55 Remove the output r/w flag, but continue on... - } - } - } - */ - - /* - else - { - client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.toLatin1().constData(), true); - if(!client_jackport) - { - for(int i = 0; ; ++i) - { - snprintf(buf, 80, "midi-out-%d", i); - // Does not work! - //if(!audioDevice->findPort(buf)) - // break; - client_jackport = (jack_port_t*)audioDevice->registerOutPort(buf, true); - if(client_jackport) - break; - - if(i == 65535) - { - fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Can't find unused output port name!\n"); - return 0; - } - } - name = QString(buf); - } - } - */ - - //client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.toLatin1().constData(), true); - //if(client_jackport == NULL) - //{ - // fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed to register jack midi client output port %s\n", name.toLatin1().constData()); - // return 0; - //} - //else - // _nextOutIdNum++; - - ///} - //else // Note docs say it can't be both input and output. // p3.3.55 Removed - - // If Jack port can send data to us and we actually want it... - //if((pf & JackPortIsOutput) && (_openFlags & 2)) - ///if(rwflags & 2) - ///{ - /* p3.3.55 Removed. - if(name.isEmpty()) - { - //snprintf(buf, 80, "muse-jack-midi-in-%d", _nextInIdNum); - for(int i = 0; ; ++i) - { - //snprintf(buf, 80, "midi-in-%d", i); - name.sprintf("midi-in-%d", i); - - if(!midiDevices.find(name)) - { - // Does not work. - //if(!audioDevice->findPort(buf)) - // break; - //client_jackport = (jack_port_t*)audioDevice->registerInPort(buf, true); - if(audioDevice->deviceType() == AudioDevice::JACK_AUDIO) // p3.3.52 - { - //client_jackport = (jack_port_t*)audioDevice->registerInPort(name.toLatin1().constData(), true); - in_client_jackport = (jack_port_t*)audioDevice->registerInPort(name.toLatin1().constData(), true); // p3.3.55 - //if(client_jackport) - if(in_client_jackport) // p3.3.55 - break; - } - else - break; - } - - if(i == 65535) - { - fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Can't find unused input port name!\n"); - return 0; - } - } - //name = QString(buf); - } - else - */ - - /* - { - if(audioDevice->deviceType() == AudioDevice::JACK_AUDIO) // p3.3.52 - { - //client_jackport = (jack_port_t*)audioDevice->registerInPort(name.toLatin1().constData(), true); - in_client_jackport = (jack_port_t*)audioDevice->registerInPort((name + QString(JACK_MIDI_IN_PORT_SUFFIX)).toLatin1().constData(), true); // p3.3.55 - //if(!client_jackport) - if(!in_client_jackport) // p3.3.55 - { - //fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed creating input port name %s\n", name.toLatin1().constData()); - fprintf(stderr, "MusE: createJackMidiDevice failed creating input port name %s\n", (name + QString(JACK_MIDI_IN_PORT_SUFFIX)).toLatin1().constData()); - - //return 0; - rwflags &= ~2; // p3.3.55 Remove the input r/w flag, but continue on... - } - } - } - */ - - //client_jackport = (jack_port_t*)audioDevice->registerInPort(name.toLatin1().constData(), true); - - //if(client_jackport == NULL) - //{ - // fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed to register jack midi client input port %s\n", name.toLatin1().constData()); - //_readEnable = false; - //return QString("Could not register jack-midi-in client port"); - // return 0; - //} - //else - // _nextInIdNum++; - - ///} - - //if(client_jackport == NULL) // p3.3.52 Removed. Allow the device to be created even if Jack isn't running. - // return 0; - - //MidiJackDevice* dev = new MidiJackDevice(client_jackport, name); - //MidiJackDevice* dev = new MidiJackDevice(in_client_jackport, out_client_jackport, name); // p3.3.55 - //MidiJackDevice* dev = new MidiJackDevice(NULL, NULL, name); // p3.3.55 - MidiJackDevice* dev = new MidiJackDevice(name); // p3.3.55 + MidiJackDevice* dev = new MidiJackDevice(name); dev->setrwFlags(rwflags); midiDevices.add(dev); return dev; @@ -471,9 +119,6 @@ void MidiJackDevice::setName(const QString& s) #endif _name = s; - //if(clientPort()) // p3.3.52 Added check. - // audioDevice->setPortName(clientPort(), s.toLatin1().constData()); - // p3.3.55 if(inClientPort()) audioDevice->setPortName(inClientPort(), (s + QString(JACK_MIDI_IN_PORT_SUFFIX)).toLatin1().constData()); if(outClientPort()) @@ -492,65 +137,7 @@ QString MidiJackDevice::open() printf("MidiJackDevice::open %s\n", name().toLatin1().constData()); #endif - /* - //jack_port_t* jp = jack_port_by_name(_client, name().toLatin1().constData()); - jack_port_t* jp = (jack_port_t*)audioDevice->findPort(name().toLatin1().constData()); - - if(!jp) - { - printf("MidiJackDevice::open: Jack midi port %s not found!\n", name().toLatin1().constData()); - _writeEnable = false; - _readEnable = false; - return QString("Jack midi port not found"); - } - - int pf = jack_port_flags(jp); - - // If Jack port can receive data from us and we actually want to... - if((pf & JackPortIsInput) && (_openFlags & 1)) - { - char buf[80]; - snprintf(buf, 80, "muse-jack-midi-out-%d", _nextOutIdNum); - _client_jackport = (jack_port_t*)audioDevice->registerOutPort(buf, true); - if(_client_jackport == NULL) - { - fprintf(stderr, "MidiJackDevice::open failed to register jack-midi-out\n"); - _writeEnable = false; - return QString("Could not register jack-midi-out client port"); - } - else - { - _nextOutIdNum++; - // src, dest - ///audioDevice->connect(_client_jackport, jp); - _writeEnable = true; - } - } - else // Note docs say it can't be both input and output. - // If Jack port can send data to us and we actually want it... - if((pf & JackPortIsOutput) && (_openFlags & 2)) - { - char buf[80]; - snprintf(buf, 80, "muse-jack-midi-in-%d", _nextInIdNum); - _client_jackport = (jack_port_t*)audioDevice->registerInPort(buf, true); - if(_client_jackport == NULL) - { - fprintf(stderr, "MidiJackDevice::open failed to register jack-midi-in\n"); - _readEnable = false; - return QString("Could not register jack-midi-in client port"); - } - else - { - _nextInIdNum++; - ///audioDevice->connect(jp, _client_jackport); - _readEnable = true; - } - } - */ - - QString s; - // p3.3.55 Moved from createJackMidiDevice() if(_openFlags & 1) { if(!_out_client_jackport) @@ -623,9 +210,6 @@ QString MidiJackDevice::open() _in_client_jackport = NULL; } - //if(client_jackport == NULL) // p3.3.52 Removed. Allow the device to be created even if Jack isn't running. - // return 0; - _writeEnable = bool(_openFlags & 1); _readEnable = bool(_openFlags & 2); @@ -642,10 +226,10 @@ void MidiJackDevice::close() printf("MidiJackDevice::close %s\n", name().toLatin1().constData()); #endif - // p3.3.55 TODO: I don't really want to unregister the + // TODO: I don't really want to unregister the // Jack midi ports because then we lose the connections // to Jack every time we click the read/write lights - // or change a port's device. + // or change a port's device. p3.3.55 /* if(_client_jackport) @@ -667,38 +251,6 @@ void MidiJackDevice::close() _writeEnable = false; _readEnable = false; - - /* - //jack_port_t* jp = jack_port_by_name(_client, name().toLatin1().constData()); - jack_port_t* jp = (jack_port_t*)audioDevice->findPort(name().toLatin1().constData()); - - if(!jp) - { - printf("MidiJackDevice::close: Jack midi port %s not found!\n", name().toLatin1().constData()); - _writeEnable = false; - _readEnable = false; - return; - } - - //int pf = jack_port_flags(jp); - - // If Jack port can receive data from us and we actually want to... - //if((pf & JackPortIsInput) && (_openFlags & 1)) - if(jack_port_connected_to(midi_port_out[0], name().toLatin1().constData())) - { - // src, dest -/// audioDevice->disconnect(midi_port_out[0], jp); - _writeEnable = false; - } - else // Note docs say it can't be both input and output. - // If Jack port can send data to us and we actually want it... - //if((pf & JackPortIsOutput) && (_openFlags & 2)) - if(jack_port_connected_to(midi_port_in[0], name().toLatin1().constData())) - { -/// audioDevice->disconnect(jp, midi_port_in[0]); - _readEnable = false; - } - */ } //--------------------------------------------------------- @@ -707,7 +259,6 @@ void MidiJackDevice::close() void MidiJackDevice::writeRouting(int level, Xml& xml) const { - // p3.3.45 // If this device is not actually in use by the song, do not write any routes. // This prevents bogus routes from being saved and propagated in the med file. if(midiPort() == -1) @@ -716,30 +267,17 @@ void MidiJackDevice::writeRouting(int level, Xml& xml) const QString s; if(rwFlags() & 2) // Readable { - //RouteList* rl = _inRoutes; - //for (ciRoute r = rl->begin(); r != rl->end(); ++r) for (ciRoute r = _inRoutes.begin(); r != _inRoutes.end(); ++r) { if(!r->name().isEmpty()) { xml.tag(level++, "Route"); - - //xml.strTag(level, "srcNode", r->name()); - //xml.tag(level, "source type=\"%d\" name=\"%s\"/", r->type, r->name().toLatin1().constData()); s = QT_TRANSLATE_NOOP("@default", "source"); if(r->type != Route::TRACK_ROUTE) s += QString(QT_TRANSLATE_NOOP("@default", " type=\"%1\"")).arg(r->type); - - //s += QString(QT_TRANSLATE_NOOP("@default", " name=\"%1\"/")).arg(r->name()); s += QString(QT_TRANSLATE_NOOP("@default", " name=\"%1\"/")).arg(Xml::xmlString(r->name())); xml.tag(level, s.toLatin1().constData()); - - //xml.strTag(level, "dstNode", name()); - //xml.tag(level, "dest type=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, name().toLatin1().constData()); - //xml.tag(level, "dest type=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, name().toLatin1().constData()); - //xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, name().toLatin1().constData()); xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, Xml::xmlString(name()).toLatin1().constData()); - xml.etag(level--, "Route"); } } @@ -752,49 +290,14 @@ void MidiJackDevice::writeRouting(int level, Xml& xml) const s = QT_TRANSLATE_NOOP("@default", "Route"); if(r->channel != -1) s += QString(QT_TRANSLATE_NOOP("@default", " channel=\"%1\"")).arg(r->channel); - - //xml.tag(level++, "Route"); xml.tag(level++, s.toLatin1().constData()); - - /* - //xml.strTag(level, "srcNode", name()); - if(r->channel != -1) - //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, r->channel, name().toLatin1().constData()); - //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, r->channel, name().toLatin1().constData()); - xml.tag(level, "source devtype=\"%d\" channel=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, r->channel, name().toLatin1().constData()); - else - //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, name().toLatin1().constData()); - //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, name().toLatin1().constData()); - */ - //xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, name().toLatin1().constData()); - xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, Xml::xmlString(name()).toLatin1().constData()); - - /* - //xml.strTag(level, "dstNode", r->name()); - if(r->channel != -1) - { - if(r->type == Route::MIDI_DEVICE_ROUTE) - xml.tag(level, "dest devtype=\"%d\" channel=\"%d\" name=\"%s\"/", r->device->deviceType(), r->channel, r->name().toLatin1().constData()); - else - xml.tag(level, "dest type=\"%d\" channel=\"%d\" name=\"%s\"/", r->type, r->channel, r->name().toLatin1().constData()); - } - else - { - if(r->type == Route::MIDI_DEVICE_ROUTE) - xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", r->device->deviceType(), r->name().toLatin1().constData()); - else - xml.tag(level, "dest type=\"%d\" name=\"%s\"/", r->type, r->name().toLatin1().constData()); - } - */ - + xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, Xml::xmlString(name()).toLatin1().constData()); s = QT_TRANSLATE_NOOP("@default", "dest"); if(r->type == Route::MIDI_DEVICE_ROUTE) s += QString(QT_TRANSLATE_NOOP("@default", " devtype=\"%1\"")).arg(r->device->deviceType()); else if(r->type != Route::TRACK_ROUTE) s += QString(QT_TRANSLATE_NOOP("@default", " type=\"%1\"")).arg(r->type); - - //s += QString(QT_TRANSLATE_NOOP("@default", " name=\"%1\"/")).arg(r->name()); s += QString(QT_TRANSLATE_NOOP("@default", " name=\"%1\"/")).arg(Xml::xmlString(r->name())); xml.tag(level, s.toLatin1().constData()); @@ -802,33 +305,6 @@ void MidiJackDevice::writeRouting(int level, Xml& xml) const xml.etag(level--, "Route"); } } - - /* - else - if(rwFlags() & 1) // Writable - { - //RouteList* rl = _outRoutes; - //for (ciRoute r = rl->begin(); r != rl->end(); ++r) - for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r) - { - if(!r->name().isEmpty()) - { - xml.tag(level++, "Route"); - - //xml.strTag(level, "srcNode", name()); - //if(r->channel != -1) - // xml.tag(level, "srcNode type=\"%d\" channel=\"%d\" name=\"%s\"", Route::JACK_MIDI_ROUTE, r->channel, name().toLatin1().constData()); - //else - xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, name().toLatin1().constData()); - - //xml.strTag(level, "dstNode", r->name()); - xml.tag(level, "dest type=\"%d\" name=\"%s\"/", r->type, r->name().toLatin1().constData()); - - xml.etag(level--, "Route"); - } - } - } - */ } //--------------------------------------------------------- @@ -841,80 +317,8 @@ void MidiJackDevice::writeRouting(int level, Xml& xml) const */ bool MidiJackDevice::putMidiEvent(const MidiPlayEvent& /*event*/) { - /* - int give, channel = event.channel(); - int x; - - if(channel >= JACK_MIDI_CHANNELS) return false; - - // buffer up events, because jack eats them in chunks, if - // the buffer is full, there isn't so much to do, than - // drop the event - - give = jack_midi_out_data[channel].give; - if(jack_midi_out_data[channel].buffer[give*4+3]){ - fprintf(stderr, "WARNING: muse-to-jack midi-buffer is full, channel=%u\n", channel); - return false; - } - // copy event(note-on etc..), pitch and volume - // see http://www.midi.org/techspecs/midimessages.php - switch(event.type()){ - case ME_NOTEOFF: - jack_midi_out_data[channel].buffer[give*4+0] = 0x80; - jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f; - jack_midi_out_data[channel].buffer[give*4+2] = event.dataB() & 0x7f; - break; - case ME_NOTEON: - jack_midi_out_data[channel].buffer[give*4+0] = 0x90; - jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f; - jack_midi_out_data[channel].buffer[give*4+2] = event.dataB() & 0x7f; - break; - case ME_CONTROLLER: - jack_midi_out_data[channel].buffer[give*4+0] = 0xb0; - jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f; - jack_midi_out_data[channel].buffer[give*4+2] = event.dataB() & 0x7f; - break; - case ME_PROGRAM: - jack_midi_out_data[channel].buffer[give*4+0] = 0xc0; - jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f; - jack_midi_out_data[channel].buffer[give*4+2] = 0; - break; - case ME_PITCHBEND: - jack_midi_out_data[channel].buffer[give*4+0] = 0xE0; - // convert muse pitch-bend to midi standard - x = 0x2000 + event.dataA(); - jack_midi_out_data[channel].buffer[give*4+1] = x & 0x7f; - jack_midi_out_data[channel].buffer[give*4+2] = (x >> 8) & 0x7f; - break; - default: - fprintf(stderr, "jack-midi-out %u WARNING: unknown event %x\n", channel, event.type()); - return false; - } - jack_midi_out_data[channel].buffer[give*4+3] = 1; // mark state of this slot - // finally increase give position - give++; - if(give >= JACK_MIDI_BUFFER_SIZE){ - give = 0; - } - jack_midi_out_data[channel].give = give; - return false; - */ - - return false; -} - -/* -//--------------------------------------------------------- -// putEvent -// return false if event is delivered -//--------------------------------------------------------- - -bool MidiJackDevice::putEvent(int* event) -{ - int *y; y = event; return false; } -*/ //--------------------------------------------------------- // recordEvent @@ -1004,9 +408,8 @@ void MidiJackDevice::recordEvent(MidiRecordEvent& event) //if(_recordFifo.put(MidiPlayEvent(event))) // printf("MidiJackDevice::recordEvent: fifo overflow\n"); - // p3.3.38 // Do not bother recording if it is NOT actually being used by a port. - // Because from this point on, process handles things, by selected port. + // Because from this point on, process handles things, by selected port. p3.3.38 if(_port == -1) return; @@ -1025,7 +428,7 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev) MidiRecordEvent event; event.setB(0); - // NOTE: From MusE-2. Not done here in Muse-1 (yet). + // NOTE: From muse_qt4_evolution. Not done here in Muse-2 (yet). // move all events 2*MusEGlobal::segmentSize into the future to get // jitterfree playback // @@ -1034,15 +437,11 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev) // ^ ^ ^ // catch process play // -// const SeqTime* st = audio->seqTime(); - - //unsigned curFrame = st->startFrame() + MusEGlobal::segmentSize; -// unsigned curFrame = st->lastFrameTime; + //int frameOffset = audio->getFrameOffset(); unsigned pos = audio->pos().frame(); - //event.setTime(pos + ev->time); - event.setTime(extSyncFlag.value() ? lastExtMidiSyncTick : (pos + ev->time)); + event.setTime(extSyncFlag.value() ? lastExtMidiSyncTick : (pos + ev->time)); // p3.3.25 event.setChannel(*(ev->buffer) & 0xf); int type = *(ev->buffer) & 0xf0; @@ -1144,13 +543,10 @@ void MidiJackDevice::collectMidiEvents() if(!_readEnable) return; - //if(!_client_jackport) - if(!_in_client_jackport) // p3.3.55 + if(!_in_client_jackport) return; - //void* port_buf = jack_port_get_buffer(_client_jackport, MusEGlobal::segmentSize); - void* port_buf = jack_port_get_buffer(_in_client_jackport, MusEGlobal::segmentSize); // p3.3.55 - + void* port_buf = jack_port_get_buffer(_in_client_jackport, MusEGlobal::segmentSize); jack_midi_event_t event; jack_nframes_t eventCount = jack_midi_get_event_count(port_buf); for (jack_nframes_t i = 0; i < eventCount; ++i) @@ -1172,9 +568,7 @@ void MidiJackDevice::collectMidiEvents() bool MidiJackDevice::putEvent(const MidiPlayEvent& ev) { - //if(!_writeEnable) - if(!_writeEnable || !_out_client_jackport) // p4.0.15 - //return true; + if(!_writeEnable || !_out_client_jackport) return false; #ifdef JACK_MIDI_DEBUG @@ -1193,9 +587,7 @@ bool MidiJackDevice::putEvent(const MidiPlayEvent& ev) // return true if successful //--------------------------------------------------------- -//void JackAudioDevice::putEvent(Port port, const MidiEvent& e) bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) -//bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) { // Perhaps we can find use for this value later, together with the Jack midi MusE port(s). // No big deal if not. Not used for now. @@ -1212,11 +604,9 @@ bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) //if(MusEGlobal::debugMsg) // printf("MidiJackDevice::queueEvent\n"); - //if(!_client_jackport) - if(!_out_client_jackport) // p3.3.55 + if(!_out_client_jackport) return false; - //void* pb = jack_port_get_buffer(_client_jackport, MusEGlobal::segmentSize); - void* pb = jack_port_get_buffer(_out_client_jackport, MusEGlobal::segmentSize); // p3.3.55 + void* pb = jack_port_get_buffer(_out_client_jackport, MusEGlobal::segmentSize); //unsigned frameCounter = ->frameTime(); int frameOffset = audio->getFrameOffset(); @@ -1340,7 +730,7 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event) // Just do this 'standard midi 64T timing thing' for now until we figure out more precise external timings. // Does require relatively short audio buffers, in order to catch the resolution, but buffer <= 256 should be OK... // Tested OK so far with 128. - ///if(extSyncFlag.value()) + //if(extSyncFlag.value()) // p4.0.15 Or, is the event marked to be played immediately? // Nothing to do but stamp the event to be queued for frame 0+. if(t == 0 || extSyncFlag.value()) @@ -1377,17 +767,13 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event) if(event.type() == ME_PITCHBEND) { int v = a + 8192; - // p3.3.44 //printf("MidiJackDevice::processEvent ME_PITCHBEND v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f))) return false; } else if(event.type() == ME_CONTROLLER) { - //int a = event.dataA(); - //int b = event.dataB(); // Perhaps we can find use for this value later, together with the Jack midi MusE port(s). // No big deal if not. Not used for now. //int port = event.port(); @@ -1407,9 +793,7 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event) if(a == CTRL_PITCH) { int v = b + 8192; - // p3.3.44 //printf("MidiJackDevice::processEvent CTRL_PITCH v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); - if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f))) return false; } @@ -1420,8 +804,6 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event) int hb = (b >> 16) & 0xff; int lb = (b >> 8) & 0xff; int pr = b & 0x7f; - - // p3.3.44 //printf("MidiJackDevice::processEvent CTRL_PROGRAM time:%d type:%d ch:%d A:%d B:%d hb:%d lb:%d pr:%d\n", // event.time(), event.type(), event.channel(), event.dataA(), event.dataB(), hb, lb, pr); @@ -1440,7 +822,7 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event) // } } - else if (a == CTRL_MASTER_VOLUME) // Enabled p4.0.15 Tim. + else if (a == CTRL_MASTER_VOLUME) { unsigned char sysex[] = { 0x7f, 0x7f, 0x04, 0x01, 0x00, 0x00 @@ -1596,19 +978,16 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event) } //--------------------------------------------------------- -// processMidi called from audio process only. +// processMidi +// Called from audio thread only. //--------------------------------------------------------- void MidiJackDevice::processMidi() { - //if(!_client_jackport) - //if(!_out_client_jackport) // p3.3.55 - // return; + processStuckNotes(); - //void* port_buf = jack_port_get_buffer(_client_jackport, MusEGlobal::segmentSize); - //void* port_buf = jack_port_get_buffer(_out_client_jackport, MusEGlobal::segmentSize); // p3.3.55 void* port_buf = 0; - if(_out_client_jackport && _writeEnable) // p4.0.15 + if(_out_client_jackport && _writeEnable) { port_buf = jack_port_get_buffer(_out_client_jackport, MusEGlobal::segmentSize); jack_midi_clear_buffer(port_buf); @@ -1616,45 +995,24 @@ void MidiJackDevice::processMidi() while(!eventFifo.isEmpty()) { - ///MidiPlayEvent e(eventFifo.get()); - MidiPlayEvent e(eventFifo.peek()); // p4.0.15 - - ///int evTime = e.time(); - // Is event marked to be played immediately? p4.0.15 Moved into processEvent(). - ///if(evTime == 0) - ///{ - // Nothing to do but stamp the event to be queued for frame 0+. - //e.setTime(frameOffset + pos); - /// e.setTime(audio->getFrameOffset() + audio->pos().frame()); - ///} - - //#ifdef JACK_MIDI_DEBUG - //printf("MidiJackDevice::processMidi eventFifo time:%d type:%d ch:%d A:%d B:%d\n", e.time(), e.type(), e.channel(), e.dataA(), e.dataB()); - //#endif - - //el->insert(eventFifo.get()); - //el->insert(e); - ///processEvent(e); - // p4.0.15 Try to process only until full, keep rest for next cycle. If no out client port or no write enable, eat up events. + MidiPlayEvent e(eventFifo.peek()); + // Try to process only until full, keep rest for next cycle. If no out client port or no write enable, eat up events. p4.0.15 if(port_buf && !processEvent(e)) return; // Give up. The Jack buffer is full. Nothing left to do. eventFifo.remove(); // Successfully processed event. Remove it from FIFO. } - MPEventList* el = playEvents(); - if(el->empty()) + if(_playEvents.empty()) { //printf("MidiJackDevice::processMidi play events empty\n"); return; } - //printf("MidiJackDevice::processMidi play events:\n"); - ///iMPEvent i = nextPlayEvent(); - iMPEvent i = el->begin(); // p4.0.15 - for(; i != el->end(); ++i) + iMPEvent i = _playEvents.begin(); + for(; i != _playEvents.end(); ++i) { //printf("MidiJackDevice::processMidi playEvent time:%d type:%d ch:%d A:%d B:%d\n", i->time(), i->type(), i->channel(), i->dataA(), i->dataB()); - // p3.3.39 Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values. + // Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values. // Same code as in MidiPort::sendEvent() if(_port != -1) { @@ -1672,12 +1030,10 @@ void MidiJackDevice::processMidi() if(i->type() == ME_PITCHBEND) { //printf("MidiJackDevice::processMidi playEvents ME_PITCHBEND time:%d type:%d ch:%d A:%d B:%d\n", (*i).time(), (*i).type(), (*i).channel(), (*i).dataA(), (*i).dataB()); - int da = mp->limitValToInstrCtlRange(CTRL_PITCH, i->dataA()); if(!mp->setHwCtrlState(i->channel(), CTRL_PITCH, da)) continue; //mp->setHwCtrlState(i->channel(), CTRL_PITCH, da); - //(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f)); } else @@ -1689,25 +1045,11 @@ void MidiJackDevice::processMidi() } } - ///processEvent(*i); - // p4.0.15 Try to process only until full, keep rest for next cycle. If no out client port or no write enable, eat up events. - if(port_buf && !processEvent(*i)) - { - //setNextPlayEvent(i); - //return; + // Try to process only until full, keep rest for next cycle. If no out client port or no write enable, eat up events. p4.0.15 + if(port_buf && !processEvent(*i)) break; - } } - - ///setNextPlayEvent(i); - // p4.0.15 We are done with these events. Let us erase them here instead of Audio::processMidi. - // That way we can simply set the next play event to the beginning. - // This also allows other events to be inserted without the problems caused by the next play event - // being at the 'end' iterator and not being *easily* set to some new place beginning of the newer insertions. - // The way that MPEventList sorts made it difficult to predict where the iterator of the first newly inserted items was. - // The erasure in Audio::processMidi was missing some events because of that. - el->erase(el->begin(), i); - // setNextPlayEvent(el->begin()); // Removed p4.0.15 Tim. + _playEvents.erase(_playEvents.begin(), i); } @@ -1718,139 +1060,6 @@ void MidiJackDevice::processMidi() bool initMidiJack() { - /* - int adr = 0; - - memset(jack_midi_out_data, 0, JACK_MIDI_CHANNELS * sizeof(muse_jack_midi_buffer)); - memset(jack_midi_in_data, 0, JACK_MIDI_CHANNELS * sizeof(muse_jack_midi_buffer)); - - MidiJackDevice* dev = new MidiJackDevice(adr, QString("jack-midi")); - dev->setrwFlags(3); // set read and write flags - - if(pipe(jackmidi_pi) < 0){ - fprintf(stderr, "cant create midi-jack input pipe\n"); - } - if(pipe(jackmidi_po) < 0){ - fprintf(stderr, "cant create midi-jack output pipe\n"); - } - - midiDevices.add(dev); - - gmdev = dev; // proclaim the global jack-midi instance - - //jackScanMidiPorts(); - */ - return false; } -/* -struct JackPort { - int adr; - //char* name; - QString name; - int flags; - //JackPort(int a, const char* s, int f) { - JackPort(int a, const QString& s, int f) { - adr = a; - //name = strdup(s); - name = QString(s); - flags = f; - } - }; - - -static std::list portList; - -//--------------------------------------------------------- -// jackScanMidiPorts -//--------------------------------------------------------- - -void jackScanMidiPorts() -{ - int adr; - const char* name; - - portList.clear(); - adr = 0; - name = strdup("namex"); - portList.push_back(JackPort(adr, name, 0)); - // - // check for devices to add - // - for (std::list::iterator k = portList.begin(); k != portList.end(); ++k) { - iMidiDevice i = midiDevices.begin(); - for (;i != midiDevices.end(); ++i) { - //MidiJackDevice* d = dynamic_cast(*i); - break; - //if (d == 0) continue; - //if ((k->adr.client == d->adr.client) && (k->adr.port == d->adr.port)) { - // break; - //} - } - if (i == midiDevices.end()) { - // add device - MidiJackDevice* dev = new MidiJackDevice(k->adr, QString(k->name)); - dev->setrwFlags(k->flags); - midiDevices.add(dev); - } - } -} -*/ - -/* -//--------------------------------------------------------- -// processInput -//--------------------------------------------------------- -static void handle_jack_midi_in(int channel) -{ - MidiRecordEvent event; - int t,n,v; - t = jack_midi_in_data[channel].buffer[0]; - n = jack_midi_in_data[channel].buffer[1]; - v = jack_midi_in_data[channel].buffer[2]; - - event.setType(0); // mark as unused - event.setPort(gmdev->midiPort()); - event.setB(0); - - if(t == 0x90){ // note on - fprintf(stderr, "jackProcessMidiInput note-on\n"); - event.setChannel(channel); - event.setType(ME_NOTEON); - event.setA(n); - event.setB(v); - }else if (t == 0x80){ // note off - fprintf(stderr, "jackProcessMidiInput note-off\n"); - event.setChannel(channel); - event.setType(ME_NOTEOFF); - event.setA(n); - event.setB(v); - }else{ - fprintf(stderr, "WARNING: unknown midi-in on channel %d: %x,%x,%x\n", - channel, t, n, v); - return; - } - if(event.type()){ - gmdev->recordEvent(event); - midiPorts[gmdev->midiPort()].syncInfo().trigActDetect(event.channel()); - } -} - -void MidiJackDevice::processInput() -{ - char buf; - int i,s; - read(gmdev->selectRfd(), &buf, 1); - - s = 1; - for(i = 0; i < JACK_MIDI_CHANNELS; i++){ - if(jack_midi_in_data[i].buffer[3]){ - s = 0; - handle_jack_midi_in(i); - jack_midi_in_data[i].buffer[3] = 0; - } - } -} - -*/ diff --git a/muse2/muse/driver/jackmidi.h b/muse2/muse/driver/jackmidi.h index 2c027c00..e79e9288 100644 --- a/muse2/muse/driver/jackmidi.h +++ b/muse2/muse/driver/jackmidi.h @@ -3,6 +3,7 @@ // Linux Music Editor // $Id: jackmidi.h,v 1.1.1.1 2010/01/27 09:06:43 terminator356 Exp $ // (C) Copyright 1999-2010 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -40,81 +41,17 @@ class MidiPlayEvent; //class RouteList; class Xml; -// Turn on to show multiple devices, work in progress, -// not working fully yet, can't seem to connect... -//#define JACK_MIDI_SHOW_MULTIPLE_DEVICES - // It appears one client port per remote port will be necessary. // Jack doesn't seem to like manipulation of non-local ports buffers. //#define JACK_MIDI_USE_MULTIPLE_CLIENT_PORTS -/* jack-midi channels */ -//#define JACK_MIDI_CHANNELS 32 - -/* jack-midi buffer size */ -//#define JACK_MIDI_BUFFER_SIZE 32 - -/* -typedef struct { - int give; - int take; - // 32 parallel midi events, where each event contains three - // midi-bytes and one busy-byte - char buffer[4 * JACK_MIDI_BUFFER_SIZE]; -} muse_jack_midi_buffer; -*/ - -/* -struct JackMidiPort -{ - jack_port_t* _jackPort; - QString _name; - int _flags; // 1 = writable, 2 = readable - do not mix - JackMidiPort(jack_port_t* jp, const QString& s, int f) - { - _jackPort = jp; - _name = QString(s); - _flags = f; - } -}; - -typedef std::map >::iterator iJackMidiPort; -typedef std::map >::const_iterator ciJackMidiPort; - -class JackMidiPortList : public std::map > -{ - private: - static int _nextOutIdNum; - static int _nextInIdNum; - - public: - JackMidiPortList(); - ~JackMidiPortList(); - iJackMidiPort createClientPort(int flags); - bool removeClientPort(jack_port_t* port); -}; - -extern JackMidiPortList jackMidiClientPorts; -*/ - //--------------------------------------------------------- // MidiJackDevice //--------------------------------------------------------- class MidiJackDevice : public MidiDevice { - public: - //int adr; - private: - // fifo for midi events sent from gui - // direct to midi port: - //MidiFifo eventFifo; // Moved into MidiDevice p4.0.15 - - //static int _nextOutIdNum; - //static int _nextInIdNum; - //jack_port_t* _client_jackport; - // p3.3.55 jack_port_t* _in_client_jackport; jack_port_t* _out_client_jackport; @@ -134,49 +71,27 @@ class MidiJackDevice : public MidiDevice { void eventReceived(jack_midi_event_t*); public: - //MidiJackDevice() {} // p3.3.55 Removed. - //MidiJackDevice(const int&, const QString& name); - - //MidiJackDevice(jack_port_t* jack_port, const QString& name); - //MidiJackDevice(jack_port_t* in_jack_port, jack_port_t* out_jack_port, const QString& name); // p3.3.55 In or out port can be null. MidiJackDevice(const QString& name); - //static MidiDevice* createJackMidiDevice(QString /*name*/, int /*rwflags*/); // 1:Writable 2: Readable. Do not mix. - static MidiDevice* createJackMidiDevice(QString name = "", int rwflags = 3); // p3.3.55 1:Writable 2: Readable 3: Writable + Readable - + static MidiDevice* createJackMidiDevice(QString name = "", int rwflags = 3); // 1:Writable 2: Readable 3: Writable + Readable virtual inline int deviceType() const { return JACK_MIDI; } - virtual void setName(const QString&); virtual void processMidi(); virtual ~MidiJackDevice(); - //virtual int selectRfd(); - //virtual int selectWfd(); - //virtual void processInput(); virtual void recordEvent(MidiRecordEvent&); virtual bool putEvent(const MidiPlayEvent&); virtual void collectMidiEvents(); - //virtual jack_port_t* jackPort() { return _jackport; } - //virtual jack_port_t* clientJackPort() { return _client_jackport; } - - //virtual void* clientPort() { return (void*)_client_jackport; } - // p3.3.55 virtual void* inClientPort() { return (void*) _in_client_jackport; } virtual void* outClientPort() { return (void*) _out_client_jackport; } - //RouteList* routes() { return &_routes; } - //bool noRoute() const { return _routes.empty(); } virtual void writeRouting(int, Xml&) const; }; extern bool initMidiJack(); -//extern int jackSelectRfd(); -//extern int jackSelectWfd(); -//extern void jackProcessMidiInput(); -//extern void jackScanMidiPorts(); #endif diff --git a/muse2/muse/instruments/minstrument.cpp b/muse2/muse/instruments/minstrument.cpp index dfdf94c6..20bee129 100644 --- a/muse2/muse/instruments/minstrument.cpp +++ b/muse2/muse/instruments/minstrument.cpp @@ -559,6 +559,7 @@ void MidiInstrument::reset(int portNo, MType) // These loops send 2048 events, which is more than our FIFO (or Jack buffer) can handle! p4.0.15 Tim. // Nope, instead, increased FIFO sizes to accommodate. //port->device()->playEvents()->add(ev); + //port->device()->addScheduledEvent(ev); } } } diff --git a/muse2/muse/midi.cpp b/muse2/muse/midi.cpp index 42f5bed7..39642348 100644 --- a/muse2/muse/midi.cpp +++ b/muse2/muse/midi.cpp @@ -4,6 +4,7 @@ // $Id: midi.cpp,v 1.43.2.22 2009/11/09 20:28:28 terminator356 Exp $ // // (C) Copyright 1999/2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -216,9 +217,7 @@ void buildMidiEventList(EventList* del, const MPEventList* el, MidiTrack* track, || ((ev.channel() == track->outChannel()) && (ev.port() == track->outPort())))) continue; unsigned tick = ev.time(); - // Added by Tim. p3.3.8 - // Added by T356. if(doLoops) { if(tick >= song->lPos().tick() && tick < song->rPos().tick()) @@ -475,13 +474,10 @@ void buildMidiEventList(EventList* del, const MPEventList* el, MidiTrack* track, // read NoteOn events and remove corresponding NoteOffs //--------------------------------------------------- - // Added by Tim. p3.3.8 - // Loop removed by flo for (iEvent i = mel.begin(); i != mel.end(); ++i) { Event ev = i->second; if (ev.isNote()) { if (!ev.isNoteOff()) { - // Added by Tim. p3.3.8 // If the event length is not zero, it means the event and its // note on/off have already been taken care of. So ignore it. @@ -520,10 +516,10 @@ void buildMidiEventList(EventList* del, const MPEventList* el, MidiTrack* track, if (k==i) //this will never happen, because i->second has to be a NOTE ON, //while k has to be a NOTE OFF. but in case something changes: - printf("ERROR: THIS SHOULD NEVER HAPPEN: k==i in midi.cpp:buildMidiEventList()\n"); + printf("ERROR: THIS SHOULD NEVER HAPPEN: k==i in midi.cpp:buildMidiEventList()\n"); else - mel.erase(k); - + mel.erase(k); + i = mel.begin(); // p4.0.34 continue; } } @@ -690,8 +686,6 @@ void Audio::collectEvents(MidiTrack* track, unsigned int cts, unsigned int nts) int defaultPort = port; MidiDevice* md = midiPorts[port].device(); - MPEventList* playEvents = md->playEvents(); - MPEventList* stuckNotes = md->stuckNotes(); PartList* pl = track->parts(); for (iPart p = pl->begin(); p != pl->end(); ++p) { @@ -714,7 +708,7 @@ void Audio::collectEvents(MidiTrack* track, unsigned int cts, unsigned int nts) continue; unsigned stick = (offset > cts) ? 0 : cts - offset; unsigned etick = nts - offset; - // By T356. Do not play events which are past the end of this part. + // Do not play events which are past the end of this part. if(etick > partLen) continue; @@ -776,33 +770,26 @@ void Audio::collectEvents(MidiTrack* track, unsigned int cts, unsigned int nts) int veloOff = ev.veloOff(); if (port == defaultPort) { - //printf("Adding event normally: frame=%d port=%d channel=%d pitch=%d velo=%d\n",frame, port, channel, pitch, velo); - - // p3.3.25 // If syncing to external midi sync, we cannot use the tempo map. - // Therefore we cannot get sub-tick resolution. Just use ticks instead of frames. + // Therefore we cannot get sub-tick resolution. Just use ticks instead of frames. p3.3.25 if(extSyncFlag.value()) - playEvents->add(MidiPlayEvent(tick, port, channel, 0x90, pitch, velo)); + md->addScheduledEvent(MidiPlayEvent(tick, port, channel, ME_NOTEON, pitch, velo)); else - - playEvents->add(MidiPlayEvent(frame, port, channel, 0x90, pitch, velo)); + md->addScheduledEvent(MidiPlayEvent(frame, port, channel, ME_NOTEON, pitch, velo)); - stuckNotes->add(MidiPlayEvent(tick + len, port, channel, - veloOff ? 0x80 : 0x90, pitch, veloOff)); + md->addStuckNote(MidiPlayEvent(tick + len, port, channel, + veloOff ? ME_NOTEOFF : ME_NOTEON, pitch, veloOff)); } else { //Handle events to different port than standard. MidiDevice* mdAlt = midiPorts[port].device(); if (mdAlt) { - - // p3.3.25 - if(extSyncFlag.value()) - mdAlt->playEvents()->add(MidiPlayEvent(tick, port, channel, 0x90, pitch, velo)); + if(extSyncFlag.value()) // p3.3.25 + mdAlt->addScheduledEvent(MidiPlayEvent(tick, port, channel, ME_NOTEON, pitch, velo)); else + mdAlt->addScheduledEvent(MidiPlayEvent(frame, port, channel, ME_NOTEON, pitch, velo)); - mdAlt->playEvents()->add(MidiPlayEvent(frame, port, channel, 0x90, pitch, velo)); - - mdAlt->stuckNotes()->add(MidiPlayEvent(tick + len, port, channel, - veloOff ? 0x80 : 0x90, pitch, veloOff)); + mdAlt->addStuckNote(MidiPlayEvent(tick + len, port, channel, + veloOff ? ME_NOTEOFF : ME_NOTEON, pitch, veloOff)); } } @@ -811,7 +798,6 @@ void Audio::collectEvents(MidiTrack* track, unsigned int cts, unsigned int nts) } break; - // Added by T356. case Controller: { if (track->type() == Track::DRUM) @@ -829,37 +815,30 @@ void Audio::collectEvents(MidiTrack* track, unsigned int cts, unsigned int nts) MidiDevice* mdAlt = midiPorts[port].device(); if(mdAlt) { - // p3.3.25 // If syncing to external midi sync, we cannot use the tempo map. - // Therefore we cannot get sub-tick resolution. Just use ticks instead of frames. + // Therefore we cannot get sub-tick resolution. Just use ticks instead of frames. p3.3.25 if(extSyncFlag.value()) - mdAlt->playEvents()->add(MidiPlayEvent(tick, port, channel, + mdAlt->addScheduledEvent(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, ctl | pitch, ev.dataB())); else - mdAlt->playEvents()->add(MidiPlayEvent(frame, port, channel, + mdAlt->addScheduledEvent(MidiPlayEvent(frame, port, channel, ME_CONTROLLER, ctl | pitch, ev.dataB())); - } break; } } - // p3.3.25 - if(extSyncFlag.value()) - playEvents->add(MidiPlayEvent(tick, port, channel, ev)); + if(extSyncFlag.value()) // p3.3.25 + md->addScheduledEvent(MidiPlayEvent(tick, port, channel, ev)); else - - playEvents->add(MidiPlayEvent(frame, port, channel, ev)); + md->addScheduledEvent(MidiPlayEvent(frame, port, channel, ev)); } break; - - + default: - // p3.3.25 - if(extSyncFlag.value()) - playEvents->add(MidiPlayEvent(tick, port, channel, ev)); + if(extSyncFlag.value()) // p3.3.25 + md->addScheduledEvent(MidiPlayEvent(tick, port, channel, ev)); else - - playEvents->add(MidiPlayEvent(frame, port, channel, ev)); + md->addScheduledEvent(MidiPlayEvent(frame, port, channel, ev)); break; } @@ -885,30 +864,6 @@ void Audio::processMidi() for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { MidiDevice* md = *id; - // - // erase already played events: - // - ///MPEventList* playEvents = md->playEvents(); - ///iMPEvent nextPlayEvent = md->nextPlayEvent(); - - //if(md->playEvents()->size()) - //{ - //printf("Audio::processMidi before erase md play events size:%d nextPlayEvent isEnd:%d\n", md->playEvents()->size(), md->nextPlayEvent() == md->playEvents()->end()); - // printf("Audio::processMidi md play events size:%d first item:\n", md->playEvents()->size()); - // md->playEvents()->begin()->dump(); - //} - - // p4.0.15 Tim. Moved into MidiJackDevice::processMidi (for Jack), and MidiSeq::processTimerTick (for ALSA). - // Why do this here? Instead, let each device erase their just-played events. - ///playEvents->erase(playEvents->begin(), nextPlayEvent); - - //if(playEvents->size()) - //{ - // printf("Audio::processMidi after erase md play events size:%d nextPlayEvent isEnd:%d events:\n", playEvents->size(), nextPlayEvent == playEvents->end()); - //for(iMPEvent ie = playEvents->begin(); ie != playEvents->end(); ++ie) - // ie->dump(); - //} - // klumsy hack for synti devices: if(md->isSynti()) { @@ -920,10 +875,6 @@ void Audio::processMidi() } } - // Is it a Jack midi device? - //MidiJackDevice* mjd = dynamic_cast(md); - //if(mjd) - // mjd->collectMidiEvents(); md->collectMidiEvents(); // Take snapshots of the current sizes of the recording fifos, @@ -931,33 +882,14 @@ void Audio::processMidi() md->beforeProcess(); } - // p4.0.15 Tim. Moved into SynthI::getData. - // Why do this here? Instead, let each device erase their just-played events. - ///MPEventList* playEvents = metronome->playEvents(); - ///iMPEvent nextPlayEvent = metronome->nextPlayEvent(); - ///playEvents->erase(playEvents->begin(), nextPlayEvent); - - // p3.3.25 bool extsync = extSyncFlag.value(); - for (iMidiTrack t = song->midis()->begin(); t != song->midis()->end(); ++t) { MidiTrack* track = *t; int port = track->outPort(); MidiDevice* md = midiPorts[port].device(); - - // Changed by Tim. p3.3.8 - //if(md == 0) - // continue; - //MPEventList* playEvents = md->playEvents(); - //if (playEvents == 0) - // continue; - //if (!track->isMute()) - MPEventList* playEvents = 0; if(md) { - playEvents = md->playEvents(); - // only add track events if the track is unmuted if(!track->isMute()) { @@ -971,66 +903,26 @@ void Audio::processMidi() // if (track->recordFlag()) { - //int portMask = track->inPortMask(); - // p3.3.38 Removed - //unsigned int portMask = track->inPortMask(); - //int channelMask = track->inChannelMask(); - MPEventList* rl = track->mpevents(); MidiPort* tport = &midiPorts[port]; - - // p3.3.38 - //for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) - //{ RouteList* irl = track->inRoutes(); for(ciRoute r = irl->begin(); r != irl->end(); ++r) { - //if(!r->isValid() || (r->type != Route::ALSA_MIDI_ROUTE && r->type != Route::JACK_MIDI_ROUTE)) - //if(!r->isValid() || (r->type != Route::MIDI_DEVICE_ROUTE)) if(!r->isValid() || (r->type != Route::MIDI_PORT_ROUTE)) // p3.3.49 continue; - - int devport = r->midiPort; // p3.3.49 + int devport = r->midiPort; // if (devport == -1) - continue; - - //MidiDevice* dev = *id; - //MidiDevice* dev = r->device; - MidiDevice* dev = midiPorts[devport].device(); // p3.3.49 + continue; + MidiDevice* dev = midiPorts[devport].device(); // if(!dev) continue; - - - // p3.3.50 Removed - //int channel = r->channel; - // NOTE: TODO: Special for input device sysex 'channel' marked as -1, ** IF we end up going with that method **. - // This would mean having a separate 'System' channel listed in the routing popups. - // The other alternative is to accept sysex from a device as long as ANY regular channel is routed from it, - // this does not require a 'System' channel listed in the routing popups. - // But that requires more code below... Done. - //if(channel == -1) - //channel = MIDI_CHANNELS; // Special channel '17' - // continue; - - //int devport = dev->midiPort(); - - // record only from ports marked in portMask: - //if (devport == -1 || !(portMask & (1 << devport))) - //if (devport == -1) - // continue; - - //MREventList* el = dev->recordEvents(); - //MidiFifo& rf = dev->recordEvents(); - - - int channelMask = r->channel; // p3.3.50 + int channelMask = r->channel; if(channelMask == -1 || channelMask == 0) continue; - for(int channel = 0; channel < MIDI_CHANNELS; ++channel) // p3.3.50 + for(int channel = 0; channel < MIDI_CHANNELS; ++channel) { if(!(channelMask & (1 << channel))) continue; - if(!dev->sysexFIFOProcessed()) { // Set to the sysex fifo at first. @@ -1041,323 +933,192 @@ void Audio::processMidi() for(int i = 0; i < count; ++i) { MidiPlayEvent event(rf.peek(i)); - - //unsigned time = event.time() + segmentSize*(segmentCount-1); - //unsigned time = event.time() + (extsync ? MusEConfig::config.division/24 : segmentSize*(segmentCount-1)); - //unsigned time = extsync ? curTickPos : (event.time() + segmentSize*(segmentCount-1)); - //event.setTime(time); - //if(!extsync) - // event.setTime(event.time() + segmentSize*(segmentCount-1)); - event.setPort(port); - // dont't echo controller changes back to software // synthesizer: if(!dev->isSynti() && md && track->recEcho()) - playEvents->add(event); - - // If syncing externally the event time is already in units of ticks, set above. + md->addScheduledEvent(event); + // If syncing externally the event time is already in units of ticks, set above. p3.3.25 if(!extsync) - { - //time = tempomap.frame2tick(event.time()); - //event.setTime(time); // set tick time event.setTime(tempomap.frame2tick(event.time())); // set tick time - } - if(recording) rl->add(event); } - dev->setSysexFIFOProcessed(true); } - // Set to the sysex fifo at first. - ///MidiFifo& rf = dev->recordEvents(MIDI_CHANNELS); - // Get the frozen snapshot of the size. - ///int count = dev->tmpRecordCount(MIDI_CHANNELS); - - // Iterate once for sysex fifo (if needed), once for channel fifos. - ///for(int sei = 0; sei < 2; ++sei) + MidiRecFifo& rf = dev->recordEvents(channel); + int count = dev->tmpRecordCount(channel); + for(int i = 0; i < count; ++i) { - // If on first pass, do sysex fifo. - /* - if(sei == 0) - { - // Ignore any further channel routes on this device if already done here. - if(dev->sysexFIFOProcessed()) - continue; - // Go ahead and set this now. - dev->setSysexFIFOProcessed(true); - // Allow it to fall through with the sysex fifo and count... - } - else - { - // We're on the second pass, do channel fifos. - rf = dev->recordEvents(channel); - // Get the frozen snapshot of the size. - count = dev->tmpRecordCount(channel); - } - */ - - MidiRecFifo& rf = dev->recordEvents(channel); - int count = dev->tmpRecordCount(channel); - - //for (iMREvent ie = el->begin(); ie != el->end(); ++ie) - for(int i = 0; i < count; ++i) - { - MidiPlayEvent event(rf.peek(i)); - - //int channel = ie->channel(); - ///int channel = event.channel(); - - int defaultPort = devport; - ///if (!(channelMask & (1 << channel))) - ///{ - /// continue; - ///} - - //MidiPlayEvent event(*ie); - int drumRecPitch=0; //prevent compiler warning: variable used without initialization - MidiController *mc = 0; - int ctl = 0; - - //Hmmm, hehhh... - // TODO: Clean up a bit around here when it comes to separate events for rec & for playback. - // But not before 0.7 (ml) - - int prePitch = 0, preVelo = 0; - - event.setChannel(track->outChannel()); - - if (event.isNote() || event.isNoteOff()) + MidiPlayEvent event(rf.peek(i)); + int defaultPort = devport; + int drumRecPitch=0; //prevent compiler warning: variable used without initialization + MidiController *mc = 0; + int ctl = 0; + + //Hmmm, hehhh... + // TODO: Clean up a bit around here when it comes to separate events for rec & for playback. + // But not before 0.7 (ml) + + int prePitch = 0, preVelo = 0; + + event.setChannel(track->outChannel()); + + if (event.isNote() || event.isNoteOff()) + { + // + // apply track values + // + + //Apply drum inkey: + if (track->type() == Track::DRUM) + { + int pitch = event.dataA(); + //Map note that is played according to drumInmap + drumRecPitch = drumMap[(unsigned int)drumInmap[pitch]].enote; + devport = drumMap[(unsigned int)drumInmap[pitch]].port; + event.setPort(devport); + channel = drumMap[(unsigned int)drumInmap[pitch]].channel; + event.setA(drumMap[(unsigned int)drumInmap[pitch]].anote); + event.setChannel(channel); + } + else + { //Track transpose if non-drum + prePitch = event.dataA(); + int pitch = prePitch + track->transposition; + if (pitch > 127) + pitch = 127; + if (pitch < 0) + pitch = 0; + event.setA(pitch); + } + + if (!event.isNoteOff()) + { + preVelo = event.dataB(); + int velo = preVelo + track->velocity; + velo = (velo * track->compression) / 100; + if (velo > 127) + velo = 127; + if (velo < 1) + velo = 1; + event.setB(velo); + } + } + else + if(event.type() == ME_CONTROLLER) + { + if(track->type() == Track::DRUM) { - // - // apply track values - // - - //Apply drum inkey: - if (track->type() == Track::DRUM) - { - int pitch = event.dataA(); - //Map note that is played according to drumInmap - drumRecPitch = drumMap[(unsigned int)drumInmap[pitch]].enote; - devport = drumMap[(unsigned int)drumInmap[pitch]].port; - event.setPort(devport); - channel = drumMap[(unsigned int)drumInmap[pitch]].channel; - event.setA(drumMap[(unsigned int)drumInmap[pitch]].anote); - event.setChannel(channel); - } - else - { //Track transpose if non-drum - prePitch = event.dataA(); - int pitch = prePitch + track->transposition; - if (pitch > 127) - pitch = 127; - if (pitch < 0) - pitch = 0; - event.setA(pitch); + ctl = event.dataA(); + // Regardless of what port the event came from, is it a drum controller event + // according to the track port's instrument? + mc = tport->drumController(ctl); + if(mc) + { + int pitch = ctl & 0x7f; + ctl &= ~0xff; + int dmindex = drumInmap[pitch] & 0x7f; + //Map note that is played according to drumInmap + drumRecPitch = drumMap[dmindex].enote; + devport = drumMap[dmindex].port; + event.setPort(devport); + channel = drumMap[dmindex].channel; + event.setA(ctl | drumMap[dmindex].anote); + event.setChannel(channel); + } + } + } + + // MusE uses a fixed clocks per quarternote of 24. + // At standard 384 ticks per quarternote for example, + // 384/24=16 for a division of 16 sub-frames (16 MusE 'ticks'). + // If ext sync, events are now time-stamped with last tick in MidiDevice::recordEvent(). p3.3.35 + // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice. + + // dont't echo controller changes back to software + // synthesizer: + + if (!dev->isSynti()) + { + //Check if we're outputting to another port than default: + if (devport == defaultPort) { + event.setPort(port); + if(md && track->recEcho()) + //playEvents->add(event); + md->addScheduledEvent(event); } - - if (!event.isNoteOff()) - { - preVelo = event.dataB(); - int velo = preVelo + track->velocity; - velo = (velo * track->compression) / 100; - if (velo > 127) - velo = 127; - if (velo < 1) - velo = 1; - event.setB(velo); + else { + // Hmm, this appears to work, but... Will this induce trouble with md->setNextPlayEvent?? + MidiDevice* mdAlt = midiPorts[devport].device(); + if(mdAlt && track->recEcho()) + //mdAlt->playEvents()->add(event); + mdAlt->addScheduledEvent(event); } - } - // Added by T356. - else - if(event.type() == ME_CONTROLLER) - { - if(track->type() == Track::DRUM) - { - ctl = event.dataA(); - // Regardless of what port the event came from, is it a drum controller event - // according to the track port's instrument? - mc = tport->drumController(ctl); - if(mc) + // Shall we activate meters even while rec echo is off? Sure, why not... + if(event.isNote() && event.dataB() > track->activity()) + track->setActivity(event.dataB()); + } + + // If syncing externally the event time is already in units of ticks, set above. p3.3.25 + if(!extsync) + event.setTime(tempomap.frame2tick(event.time())); // set tick time + + // Special handling of events stored in rec-lists. a bit hACKish. TODO: Clean up (after 0.7)! :-/ (ml) + if (recording) + { + // In these next steps, it is essential to set the recorded event's port + // to the track port so buildMidiEventList will accept it. Even though + // the port may have no device "". + // + if (track->type() == Track::DRUM) { - int pitch = ctl & 0x7f; - ctl &= ~0xff; - int dmindex = drumInmap[pitch] & 0x7f; - //Map note that is played according to drumInmap - drumRecPitch = drumMap[dmindex].enote; - devport = drumMap[dmindex].port; - event.setPort(devport); - channel = drumMap[dmindex].channel; - event.setA(ctl | drumMap[dmindex].anote); - event.setChannel(channel); - } - } - } - - // p3.3.25 - // MusE uses a fixed clocks per quarternote of 24. - // At standard 384 ticks per quarternote for example, - // 384/24=16 for a division of 16 sub-frames (16 MusE 'ticks'). - // That is what we'll use if syncing externally. - //unsigned time = event.time() + segmentSize*(segmentCount-1); - //unsigned time = event.time() + (extsync ? MusEConfig::config.division/24 : segmentSize*(segmentCount-1)); - // p3.3.34 - // Oops, use the current tick. - //unsigned time = extsync ? curTickPos : (event.time() + segmentSize*(segmentCount-1)); - //event.setTime(time); - // p3.3.35 - // If ext sync, events are now time-stamped with last tick in MidiDevice::recordEvent(). - // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice. - // p3.3.36 - //if(!extsync) - // event.setTime(event.time() + segmentSize*(segmentCount-1)); - - // dont't echo controller changes back to software - // synthesizer: - - if (!dev->isSynti()) - { - //Check if we're outputting to another port than default: - if (devport == defaultPort) { - event.setPort(port); - if(md && track->recEcho()) - playEvents->add(event); - } - else { - // Hmm, this appears to work, but... Will this induce trouble with md->setNextPlayEvent?? - MidiDevice* mdAlt = midiPorts[devport].device(); - if(mdAlt && track->recEcho()) - mdAlt->playEvents()->add(event); - } - // Shall we activate meters even while rec echo is off? Sure, why not... - if(event.isNote() && event.dataB() > track->activity()) - track->setActivity(event.dataB()); - } - - // p3.3.25 - // If syncing externally the event time is already in units of ticks, set above. - if(!extsync) - { - // p3.3.35 - //time = tempomap.frame2tick(event.time()); - //event.setTime(time); // set tick time - event.setTime(tempomap.frame2tick(event.time())); // set tick time - } - - // Special handling of events stored in rec-lists. a bit hACKish. TODO: Clean up (after 0.7)! :-/ (ml) - if (recording) - { - // In these next steps, it is essential to set the recorded event's port - // to the track port so buildMidiEventList will accept it. Even though - // the port may have no device "". - // - if (track->type() == Track::DRUM) - { - // Is it a drum controller event? - if(mc) - { - MidiPlayEvent drumRecEvent = event; - drumRecEvent.setA(ctl | drumRecPitch); - // In this case, preVelo is simply the controller value. - drumRecEvent.setB(preVelo); - drumRecEvent.setPort(port); //rec-event to current port - drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel - rl->add(drumRecEvent); - } - else - { - - MidiPlayEvent drumRecEvent = event; - drumRecEvent.setA(drumRecPitch); - drumRecEvent.setB(preVelo); - // Changed by T356. - // Tested: Events were not being recorded for a drum map entry pointing to a - // different port. This must have been wrong - buildMidiEventList would ignore this. - //drumRecEvent.setPort(devport); - drumRecEvent.setPort(port); //rec-event to current port - - drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel - rl->add(drumRecEvent); - } + // Is it a drum controller event? + if(mc) + { + MidiPlayEvent drumRecEvent = event; + drumRecEvent.setA(ctl | drumRecPitch); + // In this case, preVelo is simply the controller value. + drumRecEvent.setB(preVelo); + drumRecEvent.setPort(port); //rec-event to current port + drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel + rl->add(drumRecEvent); } - else + else { - // Restore record-pitch to non-transposed value since we don't want the note transposed twice next - MidiPlayEvent recEvent = event; - if (prePitch) - recEvent.setA(prePitch); - if (preVelo) - recEvent.setB(preVelo); - recEvent.setPort(port); - recEvent.setChannel(track->outChannel()); - - rl->add(recEvent); - } - } + MidiPlayEvent drumRecEvent = event; + drumRecEvent.setA(drumRecPitch); + drumRecEvent.setB(preVelo); + // Tested: Events were not being recorded for a drum map entry pointing to a + // different port. This must have been wrong - buildMidiEventList would ignore this. Tim. + //drumRecEvent.setPort(devport); + drumRecEvent.setPort(port); //rec-event to current port + + drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel + rl->add(drumRecEvent); + } + } + else + { + // Restore record-pitch to non-transposed value since we don't want the note transposed twice next + MidiPlayEvent recEvent = event; + if (prePitch) + recEvent.setA(prePitch); + if (preVelo) + recEvent.setB(preVelo); + recEvent.setPort(port); + recEvent.setChannel(track->outChannel()); + + rl->add(recEvent); + } + } } - } } } } - // Added by Tim. p3.3.8 - // Removed p4.0.15 - ///if(md) - /// md->setNextPlayEvent(playEvents->begin()); - //if(md) - //{ - // if(md->nextPlayEvent() != playEvents->begin()) - // printf("Audio::processMidi md->nextPlayEvent() != playEvents->begin()\n"); - //} } - // - // clear all recorded events in midiDevices - // process stuck notes - // - for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { - MidiDevice* md = *id; - - ///md->recordEvents()->clear(); - // By T356. Done processing this rec buffer, now flip to the other one. - ///md->flipRecBuffer(); - // We are done with the 'frozen' recording fifos, remove the events. - md->afterProcess(); - - MPEventList* stuckNotes = md->stuckNotes(); - MPEventList* playEvents = md->playEvents(); - - iMPEvent k; - for (k = stuckNotes->begin(); k != stuckNotes->end(); ++k) { - if (k->time() >= nextTickPos) - break; - MidiPlayEvent ev(*k); - - // p3.3.25 - //int frame = tempomap.tick2frame(k->time()) + frameOffset; - if(extsync) - { - ev.setTime(k->time()); - } - else - { - int frame = tempomap.tick2frame(k->time()) + frameOffset; - ev.setTime(frame); - } - - // p3.3.25 - //ev.setTime(frame); - - playEvents->add(ev); - } - stuckNotes->erase(stuckNotes->begin(), k); - // Removed p4.0.15 Tim. - ///md->setNextPlayEvent(playEvents->begin()); - //if(md->nextPlayEvent() != playEvents->begin()) - // printf("Audio::processMidi clear notes: md->nextPlayEvent() != playEvents->begin()\n"); - } - //--------------------------------------------------- // insert metronome clicks //--------------------------------------------------- @@ -1366,35 +1127,21 @@ void Audio::processMidi() if (MusEGlobal::midiClickFlag) md = midiPorts[MusEGlobal::clickPort].device(); if (song->click() && (isPlaying() || state == PRECOUNT)) { - MPEventList* playEvents = 0; - MPEventList* stuckNotes = 0; - if (md) { - playEvents = md->playEvents(); - stuckNotes = md->stuckNotes(); - } int bar, beat; unsigned tick; bool isMeasure = false; while (midiClick < nextTickPos) { if (isPlaying()) { - ///sigmap.tickValues(midiClick, &bar, &beat, &tick); AL::sigmap.tickValues(midiClick, &bar, &beat, &tick); isMeasure = beat == 0; } else if (state == PRECOUNT) { isMeasure = (clickno % clicksMeasure) == 0; } - // p3.3.25 //int frame = tempomap.tick2frame(midiClick) + frameOffset; - int evtime = extsync ? midiClick : tempomap.tick2frame(midiClick) + frameOffset; + int evtime = extsync ? midiClick : tempomap.tick2frame(midiClick) + frameOffset; // p3.3.25 - // p3.3.25 - //MidiPlayEvent ev(frame, MusEGlobal::clickPort, MusEGlobal::clickChan, ME_NOTEON, - MidiPlayEvent ev(evtime, MusEGlobal::clickPort, MusEGlobal::clickChan, ME_NOTEON, - MusEGlobal::beatClickNote, MusEGlobal::beatClickVelo); - if (md) { - // p3.3.25 //MidiPlayEvent ev(frame, MusEGlobal::clickPort, MusEGlobal::clickChan, ME_NOTEON, MidiPlayEvent ev(evtime, MusEGlobal::clickPort, MusEGlobal::clickChan, ME_NOTEON, MusEGlobal::beatClickNote, MusEGlobal::beatClickVelo); @@ -1403,32 +1150,20 @@ void Audio::processMidi() ev.setA(MusEGlobal::measureClickNote); ev.setB(MusEGlobal::measureClickVelo); } - playEvents->add(ev); - } - if (MusEGlobal::audioClickFlag) { - // p3.3.25 - //MidiPlayEvent ev1(frame, 0, 0, ME_NOTEON, 0, 0); - MidiPlayEvent ev1(evtime, 0, 0, ME_NOTEON, 0, 0); + md->addScheduledEvent(ev); - ev1.setA(isMeasure ? 0 : 1); - metronome->playEvents()->add(ev1); - } - if (md) { ev.setB(0); - // p3.3.25 - // Removed. Why was this here? - //frame = tempomap.tick2frame(midiClick+20) + frameOffset; - // - // Does it mean this should be changed too? - // No, stuck notes are in units of ticks, not frames like (normal, non-external) play events... ev.setTime(midiClick+10); - - if (md) - stuckNotes->add(ev); + md->addStuckNote(ev); + } + if (MusEGlobal::audioClickFlag) { + //MidiPlayEvent ev1(frame, 0, 0, ME_NOTEON, 0, 0); + MidiPlayEvent ev(evtime, 0, 0, ME_NOTEON, 0, 0); + ev.setA(isMeasure ? 0 : 1); + metronome->addScheduledEvent(ev); + // Built-in metronome synth does not use stuck notes... } - if (isPlaying()) - ///midiClick = sigmap.bar2tick(bar, beat+1, 0); midiClick = AL::sigmap.bar2tick(bar, beat+1, 0); else if (state == PRECOUNT) { midiClick += ticksBeat; @@ -1438,94 +1173,21 @@ void Audio::processMidi() state = START_PLAY; } } - // Removed p4.0.15 Tim. - ///if (md) - /// md->setNextPlayEvent(playEvents->begin()); - //if(md) - //{ - // if(md->nextPlayEvent() != playEvents->begin()) - // printf("Audio::processMidi metronome: md->nextPlayEvent() != playEvents->begin()\n"); - //} - - // Removed p4.0.15 Tim. - ///if (MusEGlobal::audioClickFlag) - /// metronome->setNextPlayEvent(metronome->playEvents()->begin()); - //if(MusEGlobal::audioClickFlag) - //{ - // if(metronome->nextPlayEvent() != metronome->playEvents()->begin()) - // printf("Audio::processMidi metronome: metronome->nextPlayEvent() != metronome->playEvents->begin()\n"); - //} - - } - - if (state == STOP) { - //--------------------------------------------------- - // end all notes - //--------------------------------------------------- - - for (iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) { - MidiDevice* md = *imd; - MPEventList* playEvents = md->playEvents(); - MPEventList* stuckNotes = md->stuckNotes(); - for (iMPEvent k = stuckNotes->begin(); k != stuckNotes->end(); ++k) { - MidiPlayEvent ev(*k); - ev.setTime(0); // play now - playEvents->add(ev); - } - stuckNotes->clear(); - } } - - - // p3.3.36 - //int tickpos = audio->tickPos(); - //bool extsync = extSyncFlag.value(); + // - // Special for Jack midi devices: Play all Jack midi events up to curFrame. + // Play all midi events up to curFrame. // for(iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { - //MidiDevice* md = *id; - // Is it a Jack midi device? - //MidiJackDevice* mjd = dynamic_cast(md); - //if(!mjd) - // continue; - //mjd->processMidi(); - (*id)->processMidi(); + // We are done with the 'frozen' recording fifos, remove the events. + (*id)->afterProcess(); // p4.0.34 - /* - int port = md->midiPort(); - MidiPort* mp = port != -1 ? &midiPorts[port] : 0; - MPEventList* el = md->playEvents(); - if (el->empty()) - continue; - iMPEvent i = md->nextPlayEvent(); - for(; i != el->end(); ++i) - { - // If syncing to external midi sync, we cannot use the tempo map. - // Therefore we cannot get sub-tick resolution. Just use ticks instead of frames. - //if(i->time() > curFrame) - if(i->time() > (extsync ? tickpos : curFrame)) - { - //printf(" curT %d frame %d\n", i->time(), curFrame); - break; // skip this event - } - - if(mp) - { - if(mp->sendEvent(*i)) - break; - } - else - { - if(md->putEvent(*i)) - break; - } - } - md->setNextPlayEvent(i); - */ + // ALSA devices handled by another thread. + if((*id)->deviceType() != MidiDevice::ALSA_MIDI) + //if((*id)->deviceType() == MidiDevice::JACK_MIDI) + (*id)->processMidi(); } - midiBusy=false; } diff --git a/muse2/muse/midi.h b/muse2/muse/midi.h index 2bfe695b..f137d7e5 100644 --- a/muse2/muse/midi.h +++ b/muse2/muse/midi.h @@ -4,6 +4,7 @@ // $Id: midi.h,v 1.4.2.2 2009/11/09 20:28:28 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -86,7 +87,6 @@ QString midiMetaName(int); class EventList; class MPEventList; class MidiTrack; -//extern void buildMidiEventList(EventList* mel, const MPEventList* el, MidiTrack* track, int division, bool); extern void buildMidiEventList(EventList* mel, const MPEventList* el, MidiTrack* track, int division, bool /*addSysexMeta*/, bool /*doLoops*/); // extern bool checkSysex(MidiTrack* track, unsigned int len, unsigned char* buf); diff --git a/muse2/muse/mididev.cpp b/muse2/muse/mididev.cpp index a97ac1fb..a06303e1 100644 --- a/muse2/muse/mididev.cpp +++ b/muse2/muse/mididev.cpp @@ -4,6 +4,7 @@ // $Id: mididev.cpp,v 1.10.2.6 2009/11/05 03:14:35 terminator356 Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -595,6 +596,34 @@ bool MidiDevice::putEvent(const MidiPlayEvent& ev) return putMidiEvent(ev); } +//--------------------------------------------------------- +// processStuckNotes +//--------------------------------------------------------- + +void MidiDevice::processStuckNotes() +{ + // Must be playing for valid nextTickPos, right? But wasn't checked in Audio::processMidi(). + // audio->isPlaying() might not be true during seek right now. + //if(audio->isPlaying()) + { + bool extsync = extSyncFlag.value(); + int frameOffset = audio->getFrameOffset(); + unsigned nextTick = audio->nextTick(); + iMPEvent k; + for (k = _stuckNotes.begin(); k != _stuckNotes.end(); ++k) { + if (k->time() >= nextTick) + break; + MidiPlayEvent ev(*k); + if(extsync) // p3.3.25 + ev.setTime(k->time()); + else + ev.setTime(tempomap.tick2frame(k->time()) + frameOffset); + _playEvents.add(ev); + } + _stuckNotes.erase(_stuckNotes.begin(), k); + } +} + //--------------------------------------------------------- // handleStop //--------------------------------------------------------- diff --git a/muse2/muse/mididev.h b/muse2/muse/mididev.h index 00e4ab65..cdb69ea2 100644 --- a/muse2/muse/mididev.h +++ b/muse2/muse/mididev.h @@ -4,6 +4,7 @@ // $Id: mididev.h,v 1.3.2.4 2009/04/04 01:49:50 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) +// (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -41,8 +42,8 @@ class Xml; //--------------------------------------------------------- class MidiDevice { - MPEventList _stuckNotes; - MPEventList _playEvents; + //MPEventList _stuckNotes; + //MPEventList _playEvents; // Removed p4.0.15 Tim. //iMPEvent _nextPlayEvent; @@ -71,6 +72,9 @@ class MidiDevice { //bool _sysexWritingChunks; bool _sysexReadingChunks; + MPEventList _stuckNotes; + MPEventList _playEvents; + // Fifo for midi events sent from gui direct to midi port: MidiFifo eventFifo; // p4.0.15 @@ -84,6 +88,7 @@ class MidiDevice { void init(); virtual bool putMidiEvent(const MidiPlayEvent&) = 0; + virtual void processStuckNotes(); public: enum { ALSA_MIDI=0, JACK_MIDI=1, SYNTH_MIDI=2 }; @@ -130,21 +135,24 @@ class MidiDevice { virtual void recordEvent(MidiRecordEvent&); + // Schedule an event for playback. Returns false if event cannot be delivered. + virtual bool addScheduledEvent(const MidiPlayEvent& ev) { _playEvents.add(ev); return true; } + // Add a stuck note. Returns false if event cannot be delivered. + virtual bool addStuckNote(const MidiPlayEvent& ev) { _stuckNotes.add(ev); return true; } + // Put an event for immediate playback. virtual bool putEvent(const MidiPlayEvent&); // This method will try to putEvent 'tries' times, waiting 'delayUs' microseconds between tries. // Since it waits, it should not be used in RT or other time-sensitive threads. p4.0.15 bool putEventWithRetry(const MidiPlayEvent&, int /*tries*/ = 2, long /*delayUs*/ = 50000); // 2 tries, 50 mS by default. - // p4.0.22 - virtual void handleStop(); + virtual void handleStop(); // p4.0.22 virtual void handleSeek(); - // For Jack-based devices - called in Jack audio process callback virtual void collectMidiEvents() {} virtual void processMidi() {} - MPEventList* stuckNotes() { return &_stuckNotes; } - MPEventList* playEvents() { return &_playEvents; } + //MPEventList* stuckNotes() { return &_stuckNotes; } + //MPEventList* playEvents() { return &_playEvents; } ///MREventList* recordEvents(); ///void flipRecBuffer() { _recBufFlipped = _recBufFlipped ? false : true; } diff --git a/muse2/muse/midiseq.cpp b/muse2/muse/midiseq.cpp index c2f3c7bd..2f1174cd 100644 --- a/muse2/muse/midiseq.cpp +++ b/muse2/muse/midiseq.cpp @@ -74,12 +74,15 @@ void MidiSeq::processMsg(const ThreadMsg* m) //case MS_PROCESS: // audio->processMidi(); // break; - case SEQM_SEEK: - processSeek(); - break; - case MS_STOP: - processStop(); - break; + + // Removed p4.0.34 + //case SEQM_SEEK: + // processSeek(); + // break; + //case MS_STOP: + // processStop(); + // break; + case MS_SET_RTC: MusEGlobal::doSetuid(); setRtcTicks(); @@ -88,6 +91,8 @@ void MidiSeq::processMsg(const ThreadMsg* m) case MS_UPDATE_POLL_FD: updatePollFd(); break; + // Moved into Song::processMsg p4.0.34 + /* case SEQM_ADD_TRACK: song->insertTrack2(msg->track, msg->ival); updatePollFd(); @@ -110,6 +115,7 @@ void MidiSeq::processMsg(const ThreadMsg* m) //song->cmdChangePart((Part*)msg->p1, (Part*)msg->p2); song->cmdChangePart((Part*)msg->p1, (Part*)msg->p2, msg->a, msg->b); break; + */ case SEQM_SET_TRACK_OUT_CHAN: { MidiTrack* track = (MidiTrack*)(msg->p1); @@ -141,6 +147,8 @@ void MidiSeq::processMsg(const ThreadMsg* m) } } +#if 0 +// Removed p4.0.34 //--------------------------------------------------------- // processStop //--------------------------------------------------------- @@ -148,8 +156,8 @@ void MidiSeq::processMsg(const ThreadMsg* m) void MidiSeq::processStop() { // p3.3.28 - // TODO Try to move this into Audio::stopRolling(). p4.0.22 - playStateExt = false; // not playing + // TODO Try to move this into Audio::stopRolling(). p4.0.22 // Done p4.0.34 + //playStateExt = false; // not playing // // clear Alsa midi device notes and stop stuck notes @@ -157,10 +165,10 @@ void MidiSeq::processStop() // for(iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { - MidiDevice* md = *id; - if(md->deviceType() == MidiDevice::JACK_MIDI) // p4.0.22 - continue; - md->handleStop(); // p4.0.22 + //MidiDevice* md = *id; + // Only ALSA devices are handled by this thread. + if((*id)->deviceType() == MidiDevice::ALSA_MIDI) // p4.0.22 + (*id)->handleStop(); // p4.0.22 /* if (md->midiPort() == -1) continue; @@ -178,18 +186,20 @@ void MidiSeq::processStop() */ } } +#endif +#if 0 +// Removed p4.0.34 //--------------------------------------------------------- // processSeek //--------------------------------------------------------- void MidiSeq::processSeek() { - int pos = audio->tickPos(); - - // TODO Try to move this into audio::seek(). p4.0.22 - if (pos == 0 && !song->record()) - audio->initDevices(); + //int pos = audio->tickPos(); + // TODO Try to move this into audio::seek(). p4.0.22 Done p4.0.34 + //if (pos == 0 && !song->record()) + // audio->initDevices(); //--------------------------------------------------- // set all controller @@ -197,13 +207,10 @@ void MidiSeq::processSeek() for (iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) { - MidiDevice* md = *i; - // - // Jack midi devices are handled in Audio::seek() - // - if(md->deviceType() == MidiDevice::JACK_MIDI) // p4.0.22 - continue; - md->handleSeek(); // p4.0.22 + //MidiDevice* md = *i; + // Only ALSA devices are handled by this thread. + if((*i)->deviceType() == MidiDevice::ALSA_MIDI) // p4.0.22 + (*i)->handleSeek(); // p4.0.22 /* int port = md->midiPort(); if (port == -1) @@ -254,6 +261,7 @@ void MidiSeq::processSeek() */ } } +#endif //--------------------------------------------------------- // MidiSeq @@ -427,7 +435,7 @@ void MidiSeq::updatePollFd() for (iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) { MidiDevice* dev = *imd; int port = dev->midiPort(); - const QString name = dev->name(); + //const QString name = dev->name(); if (port == -1) continue; if ((dev->rwFlags() & 0x2) || (extSyncFlag.value() @@ -725,20 +733,26 @@ void MidiSeq::processTimerTick() // } // p3.3.25 - int tickpos = audio->tickPos(); - bool extsync = extSyncFlag.value(); + //int tickpos = audio->tickPos(); + //bool extsync = extSyncFlag.value(); // // play all events upto curFrame // for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { - MidiDevice* md = *id; + //MidiDevice* md = *id; // Is it a Jack midi device? They are iterated in Audio::processMidi. p3.3.36 //MidiJackDevice* mjd = dynamic_cast(md); //if(mjd) - if(md->deviceType() == MidiDevice::JACK_MIDI) - continue; - if(md->isSynti()) // syntis are handled by audio thread - continue; + //if(md->deviceType() == MidiDevice::JACK_MIDI) + // continue; + //if(md->isSynti()) // syntis are handled by audio thread + // continue; + // Only ALSA midi devices are handled by this thread. + if((*id)->deviceType() == MidiDevice::ALSA_MIDI) + (*id)->processMidi(); + + // Moved into MidiAlsaDevice. p4.0.34 + /* int port = md->midiPort(); MidiPort* mp = port != -1 ? &midiPorts[port] : 0; MPEventList* el = md->playEvents(); @@ -776,6 +790,8 @@ void MidiSeq::processTimerTick() // The erasure in Audio::processMidi was missing some events because of that. el->erase(el->begin(), i); //md->setNextPlayEvent(el->begin()); // Removed p4.0.15 + */ + } } @@ -813,8 +829,8 @@ void MidiSeq::msgSetMidiDevice(MidiPort* port, MidiDevice* device) // This does not appear to be used anymore. Was called in Audio::process1, now Audio::processMidi is called directly. p4.0.15 Tim. //void MidiSeq::msgProcess() { msgMsg(MS_PROCESS); } -void MidiSeq::msgSeek() { msgMsg(SEQM_SEEK); } -void MidiSeq::msgStop() { msgMsg(MS_STOP); } +//void MidiSeq::msgSeek() { msgMsg(SEQM_SEEK); } // Removed p4.0.34 +//void MidiSeq::msgStop() { msgMsg(MS_STOP); } // void MidiSeq::msgSetRtc() { msgMsg(MS_SET_RTC); } void MidiSeq::msgUpdatePollFd() { msgMsg(MS_UPDATE_POLL_FD); } diff --git a/muse2/muse/midiseq.h b/muse2/muse/midiseq.h index 0cf85114..1e6ebf2a 100644 --- a/muse2/muse/midiseq.h +++ b/muse2/muse/midiseq.h @@ -91,6 +91,8 @@ class MidiSeq : public Thread { virtual void threadStop(); virtual void threadStart(void*); + bool externalPlayState() const { return playStateExt; } + void setExternalPlayState(bool v) { playStateExt = v; } void realtimeSystemInput(int, int); void mtcInputQuarter(int, unsigned char); void setSongPosition(int, int); @@ -101,9 +103,9 @@ class MidiSeq : public Thread { void nonRealtimeSystemSysex(int, const unsigned char*, int); void msgMsg(int id); - void msgProcess(); - void msgSeek(); - void msgStop(); + //void msgProcess(); + //void msgSeek(); + //void msgStop(); void msgSetRtc(); void msgUpdatePollFd(); void msgAddSynthI(SynthI* synth); diff --git a/muse2/muse/mpevent.h b/muse2/muse/mpevent.h index 6e9a1f90..4d957adf 100644 --- a/muse2/muse/mpevent.h +++ b/muse2/muse/mpevent.h @@ -154,8 +154,8 @@ class MidiPlayEvent : public MEvent { typedef std::multiset, audioRTalloc > MPEL; struct MPEventList : public MPEL { - //void add(const MidiPlayEvent& ev) { MPEL::insert(ev); } - iterator add(const MidiPlayEvent& ev) { return MPEL::insert(ev); } // p4.0.15 We need the iterator. + void add(const MidiPlayEvent& ev) { MPEL::insert(ev); } + //iterator add(const MidiPlayEvent& ev) { return MPEL::insert(ev); } // p4.0.15 We need the iterator. }; typedef MPEventList::iterator iMPEvent; diff --git a/muse2/muse/song.cpp b/muse2/muse/song.cpp index 65bfec13..bec5aabc 100644 --- a/muse2/muse/song.cpp +++ b/muse2/muse/song.cpp @@ -1982,6 +1982,28 @@ void Song::processMsg(AudioMsg* msg) updateFlags = SC_EVENT_MODIFIED; break; + // Moved here from MidiSeq::processMsg p4.0.34 + case SEQM_ADD_TRACK: + insertTrack2(msg->track, msg->ival); + break; + case SEQM_REMOVE_TRACK: + //removeTrack2(msg->track); + cmdRemoveTrack(msg->track); + break; + case SEQM_CHANGE_TRACK: + changeTrack((Track*)(msg->p1), (Track*)(msg->p2)); + break; + case SEQM_ADD_PART: + cmdAddPart((Part*)msg->p1); + break; + case SEQM_REMOVE_PART: + cmdRemovePart((Part*)msg->p1); + break; + case SEQM_CHANGE_PART: + //cmdChangePart((Part*)msg->p1, (Part*)msg->p2); + cmdChangePart((Part*)msg->p1, (Part*)msg->p2, msg->a, msg->b); + break; + case SEQM_ADD_TEMPO: //printf("processMsg (SEQM_ADD_TEMPO) UndoOp::AddTempo. adding tempo at: %d with tempo=%d\n", msg->a, msg->b); addUndo(UndoOp(UndoOp::AddTempo, msg->a, msg->b)); diff --git a/muse2/muse/synth.cpp b/muse2/muse/synth.cpp index 5435f8ce..ae54cd15 100644 --- a/muse2/muse/synth.cpp +++ b/muse2/muse/synth.cpp @@ -322,6 +322,15 @@ bool SynthI::putEvent(const MidiPlayEvent& ev) return false; } +//--------------------------------------------------------- +// processMidi +//--------------------------------------------------------- + +void SynthI::processMidi() +{ + processStuckNotes(); +} + //--------------------------------------------------------- // setName //--------------------------------------------------------- @@ -962,12 +971,13 @@ void SynthI::preProcessAlways() if(off()) { // Clear any accumulated play events. - playEvents()->clear(); + //playEvents()->clear(); + _playEvents.clear(); // Eat up any fifo events. //while(!eventFifo.isEmpty()) // eventFifo.get(); - eventFifo.clear(); // p4.0.21 Duh, clear is the same but faster AND safer, right? - } + eventFifo.clear(); // Clear is the same but faster AND safer, right? + } } void MessSynthIF::preProcessAlways() @@ -987,12 +997,15 @@ bool SynthI::getData(unsigned pos, int ports, unsigned n, float** buffer) int p = midiPort(); MidiPort* mp = (p != -1) ? &midiPorts[p] : 0; - MPEventList* el = playEvents(); + //MPEventList* el = playEvents(); ///iMPEvent ie = nextPlayEvent(); - iMPEvent ie = el->begin(); // p4.0.15 Tim. + //iMPEvent ie = el->begin(); // p4.0.15 Tim. + iMPEvent ie = _playEvents.begin(); + - ie = _sif->getData(mp, el, ie, pos, ports, n, buffer); + //ie = _sif->getData(mp, el, ie, pos, ports, n, buffer); + ie = _sif->getData(mp, &_playEvents, ie, pos, ports, n, buffer); ///setNextPlayEvent(ie); // p4.0.15 We are done with these events. Let us erase them here instead of Audio::processMidi. @@ -1001,7 +1014,8 @@ bool SynthI::getData(unsigned pos, int ports, unsigned n, float** buffer) // being at the 'end' iterator and not being *easily* set to some new place beginning of the newer insertions. // The way that MPEventList sorts made it difficult to predict where the iterator of the first newly inserted items was. // The erasure in Audio::processMidi was missing some events because of that. - el->erase(el->begin(), ie); + //el->erase(el->begin(), ie); + _playEvents.erase(_playEvents.begin(), ie); // setNextPlayEvent(el->begin()); // Removed p4.0.15 return true; diff --git a/muse2/muse/synth.h b/muse2/muse/synth.h index a76d798d..fc442027 100644 --- a/muse2/muse/synth.h +++ b/muse2/muse/synth.h @@ -292,6 +292,7 @@ class SynthI : public AudioTrack, public MidiDevice, } bool putEvent(const MidiPlayEvent& ev); + virtual void processMidi(); MidiPlayEvent receiveEvent() { return _sif->receiveEvent(); } int eventsPending() const { return _sif->eventsPending(); } -- cgit v1.2.3