diff options
author | Tim E. Real <termtech@rogers.com> | 2013-05-08 08:52:52 +0000 |
---|---|---|
committer | Tim E. Real <termtech@rogers.com> | 2013-05-08 08:52:52 +0000 |
commit | 2c1f2f49d2ac878c13f9c59d86166a62bbd7573d (patch) | |
tree | 96bb20b3f43823fa51cf49cfa25fae49f73882ce /muse2/muse | |
parent | 8a491e19e6520f2680bf51aec1ca7e2070ccaec7 (diff) |
Yet another MAJOR audio engine and plugin/synth process chain re-write.
And much more, see ChangeLog, May 8 2013.
Diffstat (limited to 'muse2/muse')
41 files changed, 3364 insertions, 2349 deletions
diff --git a/muse2/muse/app.cpp b/muse2/muse/app.cpp index e0212a82..ff0f7b8d 100644 --- a/muse2/muse/app.cpp +++ b/muse2/muse/app.cpp @@ -1281,7 +1281,9 @@ void MusE::loadProjectFile1(const QString& name, bool songTemplate, bool doReadM MusEGlobal::punchinAction->setChecked(MusEGlobal::song->punchin()); MusEGlobal::punchoutAction->setChecked(MusEGlobal::song->punchout()); MusEGlobal::loopAction->setChecked(MusEGlobal::song->loop()); - MusEGlobal::song->update(); + // Inform the rest of the app the song changed, with all flags MINUS + // these flags which are already sent in the call to MusE::read() above: + MusEGlobal::song->update(~SC_TRACK_INSERTED); MusEGlobal::song->updatePos(); arrangerView->clipboardChanged(); // enable/disable "Paste" arrangerView->selectionChanged(); // enable/disable "Copy" & "Paste" diff --git a/muse2/muse/arranger/arranger.cpp b/muse2/muse/arranger/arranger.cpp index 89872bdf..31ee0451 100644 --- a/muse2/muse/arranger/arranger.cpp +++ b/muse2/muse/arranger/arranger.cpp @@ -741,8 +741,8 @@ void Arranger::writeStatus(int level, MusECore::Xml& xml) xml.intTag(level, "info", ib->isChecked()); split->writeStatus(level, xml); - xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "xmag", hscroll->mag()); + xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "ypos", vscroll->value()); xml.etag(level, "arranger"); } @@ -808,10 +808,8 @@ void Arranger::readStatus(MusECore::Xml& xml) split->readStatus(xml); else if (tag == "xmag") hscroll->setMag(xml.parseInt()); - else if (tag == "xpos") { - int hpos = xml.parseInt(); - hscroll->setPos(hpos); - } + else if (tag == "xpos") + hscroll->setPos(xml.parseInt()); else if (tag == "ypos") vscroll->setValue(xml.parseInt()); else diff --git a/muse2/muse/arranger/pcanvas.cpp b/muse2/muse/arranger/pcanvas.cpp index bcbb3e58..646a9382 100644 --- a/muse2/muse/arranger/pcanvas.cpp +++ b/muse2/muse/arranger/pcanvas.cpp @@ -1025,6 +1025,8 @@ bool PartCanvas::mousePress(QMouseEvent* event) foreach(int frame, automation.currentCtrlFrameList) MusEGlobal::audio->msgEraseACEvent((MusECore::AudioTrack*)automation.currentTrack, automation.currentCtrlList->id(), frame); + // User probably would like to hear results so make sure controller is enabled. + ((MusECore::AudioTrack*)automation.currentTrack)->enableController(automation.currentCtrlList->id(), true); } } else { @@ -3877,7 +3879,7 @@ void PartCanvas::processAutomationMovements(QPoint pos, bool addPoint) int frame = MusEGlobal::tempomap.tick2frame(pos.x()); // FIXME Inefficient to add with wait here, then remove and add with wait again below. Tim. MusEGlobal::audio->msgAddACEvent((MusECore::AudioTrack*)automation.currentTrack, automation.currentCtrlList->id(), frame, 1.0 /*dummy value */); - + MusECore::iCtrl ic=automation.currentCtrlList->begin(); for (; ic !=automation.currentCtrlList->end(); ++ic) { MusECore::CtrlVal &cv = ic->second; @@ -3956,6 +3958,9 @@ void PartCanvas::processAutomationMovements(QPoint pos, bool addPoint) MusEGlobal::audio->msgChangeACEvent((MusECore::AudioTrack*)automation.currentTrack, automation.currentCtrlList->id(), icc->second.frame, newFrame, cvval); else MusEGlobal::audio->msgAddACEvent((MusECore::AudioTrack*)automation.currentTrack, automation.currentCtrlList->id(), newFrame, cvval); + + // User probably would like to hear results so make sure controller is enabled. + ((MusECore::AudioTrack*)automation.currentTrack)->enableController(automation.currentCtrlList->id(), true); } } diff --git a/muse2/muse/audio.cpp b/muse2/muse/audio.cpp index 29d6971e..0c507fa2 100644 --- a/muse2/muse/audio.cpp +++ b/muse2/muse/audio.cpp @@ -353,6 +353,7 @@ void Audio::process(unsigned frames) if (state == START_PLAY && jackState == PLAY) { _loopCount = 0; + MusEGlobal::song->reenableTouchedControllers(); startRolling(); if (_bounce) write(sigFd, "f", 1); @@ -376,6 +377,7 @@ void Audio::process(unsigned frames) } else if (state == STOP && jackState == PLAY) { _loopCount = 0; + MusEGlobal::song->reenableTouchedControllers(); startRolling(); } else if (state == LOOP1 && jackState == PLAY) diff --git a/muse2/muse/audiotrack.cpp b/muse2/muse/audiotrack.cpp index 44f72750..550725a0 100644 --- a/muse2/muse/audiotrack.cpp +++ b/muse2/muse/audiotrack.cpp @@ -4,6 +4,7 @@ // $Id: audiotrack.cpp,v 1.14.2.21 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2004 Werner Schweer (ws@seh.de) +// (C) Copyright 2013 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 @@ -43,6 +44,7 @@ #include "app.h" #include "controlfifo.h" #include "fastlog.h" +#include "gconfig.h" namespace MusECore { @@ -94,6 +96,122 @@ void cacheJackRouteNames() */ //--------------------------------------------------------- +// init_buffers +//--------------------------------------------------------- + +void AudioTrack::initBuffers() +{ + int chans = _totalOutChannels; + // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. + if(chans < MAX_CHANNELS) + chans = MAX_CHANNELS; + if(!outBuffers) + { + outBuffers = new float*[chans]; + for(int i = 0; i < chans; ++i) + { + int rv = posix_memalign((void**)&outBuffers[i], 16, sizeof(float) * MusEGlobal::segmentSize); + if(rv != 0) + { + fprintf(stderr, "ERROR: AudioTrack::init_buffers: posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + } + } + for(int i = 0; i < chans; ++i) + { + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + outBuffers[i][q] = MusEGlobal::denormalBias; + } + else + memset(outBuffers[i], 0, sizeof(float) * MusEGlobal::segmentSize); + } + + if(!outBuffersExtraMix) + { + outBuffersExtraMix = new float*[MAX_CHANNELS]; + for(int i = 0; i < MAX_CHANNELS; ++i) + { + int rv = posix_memalign((void**)&outBuffersExtraMix[i], 16, sizeof(float) * MusEGlobal::segmentSize); + if(rv != 0) + { + fprintf(stderr, "ERROR: AudioTrack::init_buffers: posix_memalign outBuffersMonoMix returned error:%d. Aborting!\n", rv); + abort(); + } + } + } + for(int i = 0; i < MAX_CHANNELS; ++i) + { + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + outBuffersExtraMix[i][q] = MusEGlobal::denormalBias; + } + else + memset(outBuffersExtraMix[i], 0, sizeof(float) * MusEGlobal::segmentSize); + } + + if(!audioInSilenceBuf) + { + int rv = posix_memalign((void**)&audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); + if(rv != 0) + { + fprintf(stderr, "ERROR: AudioTrack::init_buffers: posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + audioInSilenceBuf[q] = MusEGlobal::denormalBias; + } + else + memset(audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); + } + + if(!audioOutDummyBuf) + { + int rv = posix_memalign((void**)&audioOutDummyBuf, 16, sizeof(float) * MusEGlobal::segmentSize); + if(rv != 0) + { + fprintf(stderr, "ERROR: AudioTrack::init_buffers: posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + audioOutDummyBuf[q] = MusEGlobal::denormalBias; + } + else + memset(audioOutDummyBuf, 0, sizeof(float) * MusEGlobal::segmentSize); + } + + if(!_controls && _controlPorts != 0) + { + _controls = new Port[_controlPorts]; + ciCtrlList icl = _controller.begin(); + for(unsigned long k = 0; k < _controlPorts; ++k) + { + float val = 0.0; + if(icl != _controller.end()) + { + // Since the list is sorted by id, if no match is found just let k catch up to the id. + if((unsigned long)icl->second->id() == k) + { + val = icl->second->getDefault(); + ++icl; + } + } + _controls[k].idx = k; + _controls[k].val = val; + _controls[k].tmpVal = val; + _controls[k].enCtrl = true; + } + } +} + +//--------------------------------------------------------- // AudioTrack //--------------------------------------------------------- @@ -106,32 +224,32 @@ AudioTrack::AudioTrack(TrackType t) _prefader = false; _efxPipe = new Pipeline(); recFileNumber = 1; - //_recFile = 0; //unneeded, _recFile's ctor does this _channels = 0; _automationType = AUTO_OFF; setChannels(2); + addController(new CtrlList(AC_VOLUME,"Volume",0.001,3.163 /* roughly 10 db */, VAL_LOG)); addController(new CtrlList(AC_PAN, "Pan", -1.0, 1.0, VAL_LINEAR)); addController(new CtrlList(AC_MUTE,"Mute",0.0,1.0, VAL_LINEAR, true /*dont show in arranger */)); + _controlPorts = 3; + + _curVolume = 0.0; + _curVol1 = 0.0; + _curVol2 = 0.0; - // for a lot of considerations and failures, see revision 1402 or earlier (flo) - _totalOutChannels = MAX_CHANNELS; - outBuffers = new float*[_totalOutChannels]; - for (int i = 0; i < _totalOutChannels; ++i) - { - int rv = posix_memalign((void**)&outBuffers[i], 16, sizeof(float) * MusEGlobal::segmentSize); - if(rv != 0) - { - fprintf(stderr, "ERROR: AudioTrack ctor: posix_memalign returned error:%d. Aborting!\n", rv); - abort(); - } - } + _controls = 0; + outBuffers = 0; + outBuffersExtraMix = 0; + audioInSilenceBuf = 0; + audioOutDummyBuf = 0; + _totalOutChannels = MAX_CHANNELS; + // This is only set by multi-channel syntis... _totalInChannels = 0; - bufferPos = INT_MAX; - + initBuffers(); + setVolume(1.0); _gain = 1.0; } @@ -144,17 +262,27 @@ AudioTrack::AudioTrack(const AudioTrack& t, int flags) _efxPipe = new Pipeline(); // Start off with a new pipeline. recFileNumber = 1; + addController(new CtrlList(AC_VOLUME,"Volume",0.001,3.163 /* roughly 10 db */, VAL_LOG)); + addController(new CtrlList(AC_PAN, "Pan", -1.0, 1.0, VAL_LINEAR)); + addController(new CtrlList(AC_MUTE,"Mute",0.0,1.0, VAL_LINEAR, true /*dont show in arranger */)); + _controlPorts = 3; + + _curVolume = 0.0; + _curVol1 = 0.0; + _curVol2 = 0.0; + // Don't allocate outBuffers here. Let internal_assign() call setTotalOutChannels to set them up. + _controls = 0; outBuffers = 0; + outBuffersExtraMix = 0; + audioInSilenceBuf = 0; + audioOutDummyBuf = 0; _totalOutChannels = 0; // This is only set by multi-channel syntis... _totalInChannels = 0; - bufferPos = INT_MAX; - _recFile = NULL; - _gain = t._gain; internal_assign(t, flags | ASSIGN_PROPERTIES); } @@ -171,54 +299,169 @@ void AudioTrack::internal_assign(const Track& t, int flags) _prefader = at._prefader; _auxSend = at._auxSend; _automationType = at._automationType; - + _gain = at._gain; + if(!(flags & ASSIGN_STD_CTRLS)) { - _controller.clearDelete(); - for(ciCtrlList icl = at._controller.begin(); icl != at._controller.end(); ++icl) + // Copy the standard controller block... + ciCtrlList icl = at._controller.begin(); + ciCtrlList icl_this = _controller.begin(); + ciCtrlList icl_end = at._controller.lower_bound(AC_PLUGIN_CTL_BASE); + ciCtrlList icl_this_end = _controller.lower_bound(AC_PLUGIN_CTL_BASE); + int id, id_this; + CtrlList* cl, *cl_this; + while(icl != icl_end && icl_this != icl_this_end) + { + cl = icl->second; + cl_this = icl_this->second; + id = cl->id(); + id_this = cl_this->id(); + if(id < id_this) + ++icl; // Let id catch up to this id. + else if(id > id_this) + ++icl_this; // Let this id catch up to id. + else + { + // Match found. Copy properties but not values. + cl_this->assign(*cl, CtrlList::ASSIGN_PROPERTIES); + ++icl; + ++icl_this; + } + } + + // Copy the special synth controller block... + const int synth_id = (int)genACnum(MAX_PLUGINS, 0); // The beginning of the special synth controller block. + const int synth_id_end = synth_id + AC_PLUGIN_CTL_BASE; // The end of the special block. + icl = at._controller.lower_bound(synth_id); + icl_this = _controller.lower_bound(synth_id); + icl_end = at._controller.lower_bound(synth_id_end); + icl_this_end = _controller.lower_bound(synth_id_end); + while(icl != icl_end && icl_this != icl_this_end) { - CtrlList* cl = icl->second; - // Copy all built-in controllers (id below AC_PLUGIN_CTL_BASE), but not plugin controllers. - if(cl->id() >= AC_PLUGIN_CTL_BASE) - continue; - CtrlList* new_cl = new CtrlList(); - new_cl->assign(*cl, CtrlList::ASSIGN_PROPERTIES); // Don't copy values. - addController(new_cl); + cl = icl->second; + cl_this = icl_this->second; + id = cl->id(); + id_this = cl_this->id(); + if(id < id_this) + ++icl; // Let id catch up to this id. + else if(id > id_this) + ++icl_this; // Let this id catch up to id. + else + { + // Match found. Copy properties but not values. + cl_this->assign(*cl, CtrlList::ASSIGN_PROPERTIES); + ++icl; + ++icl_this; + } } } - // This will set up or reallocate the outBuffers. + // This will set up or reallocate the outBuffers. _controlPorts must be valid by now. setTotalOutChannels(at._totalOutChannels); // This is only set by multi-channel syntis... setTotalInChannels(at._totalInChannels); - + + // FIXME: setChannels also called in setTotalOutChannels above, causing redundant efxpipe setChannels. setChannels(at.channels()); // Set track channels (max 2). + + unsigned long cp = _controlPorts; + if(at._controlPorts < cp) + cp = at._controlPorts; + for(unsigned long k = 0; k < cp; ++k) + _controls[k] = at._controls[k]; // Assign the structures. } if(flags & ASSIGN_PLUGINS) { delete _efxPipe; - _efxPipe = new Pipeline(*(at._efxPipe)); // Make copies of the plugins. + _efxPipe = new Pipeline(*(at._efxPipe), this); // Make copies of the plugins. } if(flags & (ASSIGN_STD_CTRLS | ASSIGN_PLUGIN_CTRLS)) { - _controller.clearDelete(); - for(ciCtrlList icl = at._controller.begin(); icl != at._controller.end(); ++icl) + const int synth_id = (int)genACnum(MAX_PLUGINS, 0); // The beginning of the special synth controller block. + const int synth_id_end = synth_id + AC_PLUGIN_CTL_BASE; // The end of the special block. + ciCtrlList icl, icl_end, icl_this, icl_this_end; + int id, id_this; + CtrlList* cl, *cl_this; + + if(flags & ASSIGN_STD_CTRLS) { - CtrlList* cl = icl->second; - // Discern between built-in controllers (id below AC_PLUGIN_CTL_BASE), and plugin controllers. - if(cl->id() >= AC_PLUGIN_CTL_BASE) + // Copy the standard controller block... + icl = at._controller.begin(); + icl_this = _controller.begin(); + icl_end = at._controller.lower_bound(AC_PLUGIN_CTL_BASE); + icl_this_end = _controller.lower_bound(AC_PLUGIN_CTL_BASE); + while(icl != icl_end && icl_this != icl_this_end) { - if(!(flags & ASSIGN_PLUGIN_CTRLS)) - continue; + cl = icl->second; + cl_this = icl_this->second; + id = cl->id(); + id_this = cl_this->id(); + if(id < id_this) + ++icl; // Let id catch up to this id. + else if(id > id_this) + ++icl_this; // Let this id catch up to id. + else + { + // Match found. Copy properties and values. + cl_this->assign(*cl, CtrlList::ASSIGN_PROPERTIES | CtrlList::ASSIGN_VALUES); + ++icl; + ++icl_this; + } } - else if(!(flags & ASSIGN_STD_CTRLS)) - continue; - CtrlList* new_cl = new CtrlList(*cl); // Let copy constructor handle the rest. Copy values. - addController(new_cl); + // Copy the special synth controller block... + icl = at._controller.lower_bound(synth_id); + icl_this = _controller.lower_bound(synth_id); + icl_end = at._controller.lower_bound(synth_id_end); + icl_this_end = _controller.lower_bound(synth_id_end); + while(icl != icl_end && icl_this != icl_this_end) + { + cl = icl->second; + cl_this = icl_this->second; + id = cl->id(); + id_this = cl_this->id(); + if(id < id_this) + ++icl; // Let id catch up to this id. + else if(id > id_this) + ++icl_this; // Let this id catch up to id. + else + { + // Match found. Copy properties and values. + cl_this->assign(*cl, CtrlList::ASSIGN_PROPERTIES | CtrlList::ASSIGN_VALUES); + ++icl; + ++icl_this; + } + } + } + + if(flags & ASSIGN_PLUGIN_CTRLS) + { + // Copy all plugin controller blocks... + icl = at._controller.lower_bound(AC_PLUGIN_CTL_BASE); + icl_this = _controller.lower_bound(AC_PLUGIN_CTL_BASE); + icl_end = at._controller.lower_bound(synth_id); + icl_this_end = _controller.lower_bound(synth_id); + while(icl != icl_end && icl_this != icl_this_end) + { + cl = icl->second; + cl_this = icl_this->second; + id = cl->id(); + id_this = cl_this->id(); + if(id < id_this) + ++icl; // Let id catch up to this id. + else if(id > id_this) + ++icl_this; // Let this id catch up to id. + else + { + // Match found. Copy properties and values. + cl_this->assign(*cl, CtrlList::ASSIGN_PROPERTIES | CtrlList::ASSIGN_VALUES); + ++icl; + ++icl_this; + } + } } } @@ -277,6 +520,22 @@ void AudioTrack::assign(const Track& t, int flags) AudioTrack::~AudioTrack() { delete _efxPipe; + + if(audioInSilenceBuf) + free(audioInSilenceBuf); + + if(audioOutDummyBuf) + free(audioOutDummyBuf); + + if(outBuffersExtraMix) + { + for(int i = 0; i < MAX_CHANNELS; ++i) + { + if(outBuffersExtraMix[i]) + free(outBuffersExtraMix[i]); + } + delete[] outBuffersExtraMix; + } int chans = _totalOutChannels; // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. @@ -291,7 +550,10 @@ AudioTrack::~AudioTrack() } delete[] outBuffers; } - + + if(_controls) + delete[] _controls; + _controller.clearDelete(); } @@ -348,13 +610,22 @@ void AudioTrack::addPlugin(PluginI* plugin, int idx) } } efxPipe()->insert(plugin, idx); - if (plugin) + setupPlugin(plugin, idx); +} + +//--------------------------------------------------------- +// setupPlugin +//--------------------------------------------------------- + +void AudioTrack::setupPlugin(PluginI* plugin, int idx) +{ + if (plugin) { plugin->setID(idx); plugin->setTrack(this); - + int controller = plugin->parameters(); - for (int i = 0; i < controller; ++i) + for (int i = 0; i < controller; ++i) { int id = genACnum(idx, i); const char* name = plugin->paramName(i); @@ -770,7 +1041,7 @@ void AudioTrack::changeACEvent(int id, int frame, int newframe, double newval) double AudioTrack::volume() const { return _controller.value(AC_VOLUME, MusEGlobal::audio->curFramePos(), - !MusEGlobal::automation || automationType() == AUTO_OFF || !_volumeEnCtrl || !_volumeEn2Ctrl); + !MusEGlobal::automation || automationType() == AUTO_OFF || !_controls[AC_VOLUME].enCtrl); } //--------------------------------------------------------- @@ -795,7 +1066,7 @@ void AudioTrack::setVolume(double val) double AudioTrack::pan() const { return _controller.value(AC_PAN, MusEGlobal::audio->curFramePos(), - !MusEGlobal::automation || automationType() == AUTO_OFF || !_panEnCtrl || !_panEn2Ctrl); + !MusEGlobal::automation || automationType() == AUTO_OFF || !_controls[AC_PAN].enCtrl); } //--------------------------------------------------------- @@ -836,26 +1107,17 @@ void AudioTrack::setGain(double val) double AudioTrack::pluginCtrlVal(int ctlID) const { - bool en_1 = true, en_2 = true; + bool en = true; if(ctlID < AC_PLUGIN_CTL_BASE) { - if(ctlID == AC_VOLUME) - { - en_1 = _volumeEnCtrl; - en_2 = _volumeEn2Ctrl; - } - else - if(ctlID == AC_PAN) - { - en_1 = _panEnCtrl; - en_2 = _panEn2Ctrl; - } + if((unsigned long)ctlID < _controlPorts) + en = _controls[ctlID].enCtrl; } else { if(ctlID < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special synth controller block. { - _efxPipe->controllersEnabled(ctlID, &en_1, &en_2); + en = _efxPipe->controllerEnabled(ctlID); } else { @@ -866,15 +1128,14 @@ double AudioTrack::pluginCtrlVal(int ctlID) const if(sif) { int in_ctrl_idx = ctlID & AC_PLUGIN_CTL_ID_MASK; - en_1 = sif->controllerEnabled(in_ctrl_idx); - en_2 = sif->controllerEnabled2(in_ctrl_idx); + en = sif->controllerEnabled(in_ctrl_idx); } } } } return _controller.value(ctlID, MusEGlobal::audio->curFramePos(), - !MusEGlobal::automation || automationType() == AUTO_OFF || !en_1 || !en_2); + !MusEGlobal::automation || automationType() == AUTO_OFF || !en); } //--------------------------------------------------------- @@ -897,12 +1158,22 @@ void AudioTrack::setPluginCtrlVal(int param, double val) bool AudioTrack::addScheduledControlEvent(int track_ctrl_id, float val, unsigned frame) { - if(track_ctrl_id < AC_PLUGIN_CTL_BASE) // FIXME: These controllers (three so far - vol, pan, mute) have no vari-run-length support. + if(track_ctrl_id < AC_PLUGIN_CTL_BASE) { - iCtrlList icl = _controller.find(track_ctrl_id); - if(icl == _controller.end()) + // Send these controllers directly to the track's own FIFO. + ControlEvent ce; + ce.unique = false; + ce.fromGui = false; + ce.idx = track_ctrl_id; + ce.value = val; + // Time-stamp the event. timestamp() is circular, which is making it impossible to deal with 'modulo' events which + // slip in 'under the wire' before processing the ring buffers. So try this linear timestamp instead: + ce.frame = frame; + if(_controlFifo.put(ce)) + { + fprintf(stderr, "AudioTrack::addScheduledControlEvent: fifo overflow: in control number:%d\n", track_ctrl_id); return true; - icl->second->setCurVal(val); + } return false; } else @@ -937,11 +1208,8 @@ void AudioTrack::enableController(int track_ctrl_id, bool en) { if(track_ctrl_id < AC_PLUGIN_CTL_BASE) { - if(track_ctrl_id == AC_VOLUME) - enableVolumeController(en); - else - if(track_ctrl_id == AC_PAN) - enablePanController(en); + if((unsigned long)track_ctrl_id < _controlPorts) + _controls[track_ctrl_id].enCtrl = en; } else { @@ -964,31 +1232,22 @@ void AudioTrack::enableController(int track_ctrl_id, bool en) } //--------------------------------------------------------- -// controllersEnabled +// controllerEnabled //--------------------------------------------------------- -void AudioTrack::controllersEnabled(int track_ctrl_id, bool* en1, bool* en2) const +bool AudioTrack::controllerEnabled(int track_ctrl_id) const { - bool en_1 = true, en_2 = true; if(track_ctrl_id < AC_PLUGIN_CTL_BASE) { - if(track_ctrl_id == AC_VOLUME) - { - en_1 = _volumeEnCtrl; - en_2 = _volumeEn2Ctrl; - } - else - if(track_ctrl_id == AC_PAN) - { - en_1 = _panEnCtrl; - en_2 = _panEn2Ctrl; - } + if((unsigned long)track_ctrl_id < _controlPorts) + return _controls[track_ctrl_id].enCtrl; + return false; } else { if(track_ctrl_id < (int)genACnum(MAX_PLUGINS, 0)) // The beginning of the special synth controller block. { - _efxPipe->controllersEnabled(track_ctrl_id, &en_1, &en_2); + return _efxPipe->controllerEnabled(track_ctrl_id); } else { @@ -999,19 +1258,46 @@ void AudioTrack::controllersEnabled(int track_ctrl_id, bool* en1, bool* en2) con if(sif) { int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK; - en_1 = sif->controllerEnabled(in_ctrl_idx); - en_2 = sif->controllerEnabled2(in_ctrl_idx); + return sif->controllerEnabled(in_ctrl_idx); } } } } - - if(en1) - *en1 = en_1; - if(en2) - *en2 = en_2; + return false; } +//--------------------------------------------------------- +// enableAllControllers +// Enable all track and plugin controllers, and synth controllers if applicable. +//--------------------------------------------------------- + +void AudioTrack::enableAllControllers() +{ + // Enable track controllers: + for(unsigned long i = 0; i < _controlPorts; ++i) + _controls[i].enCtrl = true; + + // Enable plugin controllers: + Pipeline *pl = efxPipe(); + PluginI *p; + for(iPluginI i = pl->begin(); i != pl->end(); ++i) + { + p = *i; + if(!p) + continue; + p->enableAllControllers(true); + } + + // Enable synth controllers: + if(type() == AUDIO_SOFTSYNTH) + { + const SynthI* synth = static_cast<const SynthI*>(this); + SynthIF* sif = synth->sif(); + if(sif) + sif->enableAllControllers(true); + } +} + void AudioTrack::recordAutomation(int n, double v) { if(!MusEGlobal::automation) @@ -1739,6 +2025,13 @@ AudioAux::AudioAux() fprintf(stderr, "ERROR: AudioAux ctor: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + buffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(buffer[i], 0, sizeof(float) * MusEGlobal::segmentSize); } else buffer[i] = 0; @@ -1759,6 +2052,13 @@ AudioAux::AudioAux(const AudioAux& t, int flags) fprintf(stderr, "ERROR: AudioAux ctor: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + buffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(buffer[i], 0, sizeof(float) * MusEGlobal::segmentSize); } else buffer[i] = 0; @@ -1858,6 +2158,13 @@ void AudioAux::setChannels(int n) fprintf(stderr, "ERROR: AudioAux::setChannels: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + buffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(buffer[i], 0, sizeof(float) * MusEGlobal::segmentSize); } } else if(n < channels()) diff --git a/muse2/muse/ctrl.cpp b/muse2/muse/ctrl.cpp index 5ffaf99f..f32d1978 100644 --- a/muse2/muse/ctrl.cpp +++ b/muse2/muse/ctrl.cpp @@ -6,7 +6,7 @@ // controller handling for mixer automation // // (C) Copyright 2003 Werner Schweer (ws@seh.de) -// (C) Copyright 2011-2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2011-2013 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 @@ -359,6 +359,13 @@ CtrlList::CtrlList(int id, QString name, double min, double max, CtrlValueType v initColor(id); } +CtrlList::CtrlList(const CtrlList& l, int flags) +{ + _id = l._id; + _valueType = l._valueType; + assign(l, flags | ASSIGN_PROPERTIES); +} + //--------------------------------------------------------- // assign //--------------------------------------------------------- @@ -367,14 +374,12 @@ void CtrlList::assign(const CtrlList& l, int flags) { if(flags & ASSIGN_PROPERTIES) { - _id = l._id; _default = l._default; _curVal = l._curVal; _mode = l._mode; _name = l._name; _min = l._min; _max = l._max; - _valueType = l._valueType; _dontShow = l._dontShow; _displayColor = l._displayColor; _visible = l._visible; @@ -388,6 +393,129 @@ void CtrlList::assign(const CtrlList& l, int flags) } //--------------------------------------------------------- +// getInterpolation +// Fills CtrlInterpolate struct for given frame. +// cur_val_only means read the current 'manual' value, not from the list even if it is not empty. +// CtrlInterpolate member eFrame can be -1 meaning no next value (wide-open, endless). +//--------------------------------------------------------- + +void CtrlList::getInterpolation(int frame, bool cur_val_only, CtrlInterpolate* interp) +{ + interp->eStop = false; // During processing, control FIFO ring buffers will set this true. + + if(cur_val_only || empty()) + { + interp->sFrame = 0; + interp->eFrame = -1; + interp->sVal = _curVal; + interp->eVal = _curVal; + interp->doInterp = false; + return; + } + ciCtrl i = upper_bound(frame); // get the index after current frame + if (i == end()) // if we are past all items just return the last value + { + --i; + interp->sFrame = 0; + interp->eFrame = -1; + interp->sVal = i->second.val; + interp->eVal = i->second.val; + interp->doInterp = false; + return; + } + else if(_mode == DISCRETE) + { + if(i == begin()) + { + interp->sFrame = 0; + interp->eFrame = i->second.frame; + interp->sVal = i->second.val; + interp->eVal = i->second.val; + interp->doInterp = false; + } + else + { + interp->eFrame = i->second.frame; + interp->eVal = i->second.val; + --i; + interp->sFrame = i->second.frame; + interp->sVal = i->second.val; + interp->doInterp = false; + } + } + else // INTERPOLATE + { + if(i == begin()) + { + interp->sFrame = 0; + interp->eFrame = i->second.frame; + interp->sVal = i->second.val; + interp->eVal = i->second.val; + interp->doInterp = false; + } + else + { + interp->eFrame = i->second.frame; + interp->eVal = i->second.val; + --i; + interp->sFrame = i->second.frame; + interp->sVal = i->second.val; + interp->doInterp = (interp->eVal != interp->sVal && interp->eFrame > interp->sFrame); + } + } +} + +//--------------------------------------------------------- +// interpolate +// Returns interpolated value at given frame, from a CtrlInterpolate struct. +// For speed, no checking is done for frame = frame2, val1 = val2 or even CtrlInterpolate::doInterp. +// Those are to be taken care of before calling this routine. See getInterpolation(). +//--------------------------------------------------------- + +double CtrlList::interpolate(int frame, const CtrlInterpolate& interp) +{ + int frame1 = interp.sFrame; + int frame2 = interp.eFrame; + double val1 = interp.sVal; + double val2 = interp.eVal; + if(frame >= frame2) // frame2 can also be -1 + { + if(_valueType == VAL_LOG) + { + const double min = exp10(MusEGlobal::config.minSlider / 20.0); // TODO Try fastexp10 + if(val2 < min) + val2 = min; + } + return val2; + } + if(frame <= frame1) + { + if(_valueType == VAL_LOG) + { + const double min = exp10(MusEGlobal::config.minSlider / 20.0); // TODO Try fastexp10 + if(val1 < min) + val1 = min; + } + return val1; + } + + if(_valueType == VAL_LOG) + { + val1 = 20.0*fast_log10(val1); + if (val1 < MusEGlobal::config.minSlider) + val1=MusEGlobal::config.minSlider; + val2 = 20.0*fast_log10(val2); + if (val2 < MusEGlobal::config.minSlider) + val2=MusEGlobal::config.minSlider; + } + val2 -= val1; + val1 += (double(frame - frame1) * val2) / double(frame2 - frame1); + if (_valueType == VAL_LOG) + val1 = exp10(val1/20.0); + return val1; +} + +//--------------------------------------------------------- // value // Returns value at frame. // cur_val_only means read the current 'manual' value, not from the list even if it is not empty. diff --git a/muse2/muse/ctrl.h b/muse2/muse/ctrl.h index cfc1ef62..9e84d133 100644 --- a/muse2/muse/ctrl.h +++ b/muse2/muse/ctrl.h @@ -6,7 +6,7 @@ // controller for mixer automation // // (C) Copyright 2003-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011-2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2011-2013 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 @@ -51,6 +51,32 @@ enum CtrlValueType { VAL_LOG, VAL_LINEAR, VAL_INT, VAL_BOOL }; enum CtrlRecValueType { ARVT_VAL, ARVT_START, ARVT_STOP }; //--------------------------------------------------------- +// CtrlInterpolate +// Controller interpolation values. +// For speed: Can be filled once by CtrlList::getInterpolation(), +// then passed repeatedly to CtrlList::interpolate(). +//--------------------------------------------------------- + +struct CtrlInterpolate { + int sFrame; // Starting frame. Always valid. Can be less than any first CtrlList item's frame, or zero ! + double sVal; // Value at starting frame. + int eFrame; // Ending frame. Can be -1 meaning endless. + double eVal; // Value at ending frame, or sVal if eFrame is -1. + bool eStop; // Whether to stop refreshing this struct from CtrlList upon eFrame. Control FIFO ring buffers + // set this true and replace eFrame and eVal. Upon the next run slice, if eStop is set, eval + // should be copied to sVal, eFrame to sFrame, doInterp cleared, and eFrame set to some frame or -1. + bool doInterp; // Whether to actually interpolate whenever this struct is passed to CtrlList::interpolate(). + CtrlInterpolate(int sframe = 0, int eframe = -1, double sval = 0.0, double eval = 0.0, bool end_stop = false, bool do_interpolate = false) { + sFrame = sframe; + sVal = sval; + eFrame = eframe; + eVal = eval; + eStop = end_stop; + doInterp = do_interpolate; + } + }; + +//--------------------------------------------------------- // CtrlVal // controller "event" //--------------------------------------------------------- @@ -159,6 +185,7 @@ class CtrlList : public std::map<int, CtrlVal, std::less<int> > { CtrlList(bool dontShow=false); CtrlList(int id, bool dontShow=false); CtrlList(int id, QString name, double min, double max, CtrlValueType v, bool dontShow=false); + CtrlList(const CtrlList& l, int flags); void assign(const CtrlList& l, int flags); void swap(CtrlList&); @@ -190,7 +217,9 @@ class CtrlList : public std::map<int, CtrlVal, std::less<int> > { } CtrlValueType valueType() const { return _valueType; } void setValueType(CtrlValueType t) { _valueType = t; } - + void getInterpolation(int frame, bool cur_val_only, CtrlInterpolate* interp); + double interpolate(int frame, const CtrlInterpolate& interp); + double value(int frame, bool cur_val_only = false, int* nextFrame = NULL) const; void add(int frame, double value); void del(int frame); diff --git a/muse2/muse/driver/audiodev.h b/muse2/muse/driver/audiodev.h index 08d6a9f0..d060b138 100644 --- a/muse2/muse/driver/audiodev.h +++ b/muse2/muse/driver/audiodev.h @@ -75,6 +75,7 @@ class AudioDevice { virtual void setPortName(void* p, const char* n) = 0; virtual void* findPort(const char* name) = 0; virtual QString portName(void* port) = 0; + virtual unsigned int portLatency(void* port, bool capture) const = 0; virtual int getState() = 0; virtual unsigned getCurFrame() const = 0; virtual bool isRealtime() = 0; diff --git a/muse2/muse/driver/dummyaudio.cpp b/muse2/muse/driver/dummyaudio.cpp index 172638b4..4e37bc33 100644 --- a/muse2/muse/driver/dummyaudio.cpp +++ b/muse2/muse/driver/dummyaudio.cpp @@ -135,6 +135,7 @@ class DummyAudioDevice : public AudioDevice { virtual QString portName(void*) { return QString("mops"); } + virtual unsigned int portLatency(void* /*port*/, bool /*capture*/) const { return 0; } virtual int getState() { // if(DEBUG_DUMMY) // printf("DummyAudioDevice::getState %d\n", state); @@ -224,17 +225,22 @@ DummyAudioDevice* dummyAudio = 0; DummyAudioDevice::DummyAudioDevice() { - // Added by Tim. p3.3.15 - // p3.3.30 - //posix_memalign((void**)&buffer, 16, sizeof(float) * dummyFrames); - - int rv = posix_memalign((void**)&buffer, 16, sizeof(float) * MusEGlobal::config.dummyAudioBufSize); + MusEGlobal::sampleRate = MusEGlobal::config.dummyAudioSampleRate; + MusEGlobal::segmentSize = MusEGlobal::config.dummyAudioBufSize; + int rv = posix_memalign((void**)&buffer, 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: DummyAudioDevice ctor: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } - + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + buffer[q] = MusEGlobal::denormalBias; + } + else + memset(buffer, 0, sizeof(float) * MusEGlobal::segmentSize); + dummyThread = 0; realtimeFlag = false; seekflag = false; @@ -310,9 +316,8 @@ static void* dummyLoop(void* ptr) // p3.3.30 //MusEGlobal::sampleRate = 25600; - MusEGlobal::sampleRate = MusEGlobal::config.dummyAudioSampleRate; //MusEGlobal::segmentSize = dummyFrames; - MusEGlobal::segmentSize = MusEGlobal::config.dummyAudioBufSize; +// MusEGlobal::segmentSize = MusEGlobal::config.dummyAudioBufSize; #if 0 //unsigned int tickRate = MusEGlobal::sampleRate / dummyFrames; unsigned int tickRate = MusEGlobal::sampleRate / MusEGlobal::segmentSize; diff --git a/muse2/muse/driver/jack.cpp b/muse2/muse/driver/jack.cpp index 3deaf160..b1321cbc 100644 --- a/muse2/muse/driver/jack.cpp +++ b/muse2/muse/driver/jack.cpp @@ -1557,6 +1557,51 @@ QString JackAudioDevice::portName(void* port) } //--------------------------------------------------------- +// portLatency +// If capture is true get the capture latency, +// otherwise get the playback latency. +//--------------------------------------------------------- + +unsigned int JackAudioDevice::portLatency(void* port, bool capture) const +{ + // TODO: Experimental code... Finish it... + + if(!checkJackClient(_client) || !port) + return 0; + + QString s(jack_port_name((jack_port_t*)port)); + //fprintf(stderr, "Jack::portName %p %s\n", port, s.toLatin1().constData()); + + jack_latency_range_t p_range; + jack_port_get_latency_range((jack_port_t*)port, JackPlaybackLatency, &p_range); + //fprintf(stderr, "JackAudioDevice::portLatency playback min:%u max:%u\n", p_range.min, p_range.max); + if(p_range.max != p_range.min) + { + //fprintf(stderr, "JackAudioDevice::portLatency min:%u != max:%u\n", p_range.min, p_range.max); + //return (p_range.max - p_range.min) / 2; + } + + jack_latency_range_t c_range; + jack_port_get_latency_range((jack_port_t*)port, JackCaptureLatency, &c_range); + //fprintf(stderr, "JackAudioDevice::portLatency capture min:%u max:%u\n", c_range.min, c_range.max); + if(c_range.max != c_range.min) + { + //fprintf(stderr, "JackAudioDevice::portLatency capture min:%u != max:%u\n", c_range.min, c_range.max); + + // TODO TEST Decide which method to use. Although, it is arbitrary. + //return (c_range.max - c_range.min) / 2; + //return c_range.max; + } + + if(capture) + //return (c_range.max - c_range.min) / 2; + return c_range.max; + + //return (p_range.max - p_range.min) / 2; + return p_range.max; +} + +//--------------------------------------------------------- // unregisterPort //--------------------------------------------------------- diff --git a/muse2/muse/driver/jackaudio.h b/muse2/muse/driver/jackaudio.h index aab60d88..fd47de07 100644 --- a/muse2/muse/driver/jackaudio.h +++ b/muse2/muse/driver/jackaudio.h @@ -96,6 +96,7 @@ class JackAudioDevice : public AudioDevice { virtual void setPortName(void* p, const char* n) { jack_port_set_name((jack_port_t*)p, n); } virtual void* findPort(const char* name); virtual QString portName(void* port); + virtual unsigned int portLatency(void* port, bool capture) const; virtual int getState(); virtual unsigned int getCurFrame() const; virtual bool isRealtime() { return jack_is_realtime(_client); } diff --git a/muse2/muse/dssihost.cpp b/muse2/muse/dssihost.cpp index 6b55c7e7..00956279 100644 --- a/muse2/muse/dssihost.cpp +++ b/muse2/muse/dssihost.cpp @@ -4,7 +4,7 @@ // $Id: dssihost.cpp,v 1.15.2.16 2009/12/15 03:39:58 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2013 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License @@ -327,7 +327,6 @@ SynthIF* DssiSynth::createSIF(SynthI* synti) iIdx.clear(); oIdx.clear(); rpIdx.clear(); - iUsedIdx.clear(); midiCtl2PortMap.clear(); port2MidiCtlMap.clear(); @@ -349,7 +348,6 @@ SynthIF* DssiSynth::createSIF(SynthI* synti) { ++_inports; iIdx.push_back(k); - iUsedIdx.push_back(false); // Start out with all false. } else if (LADSPA_IS_PORT_OUTPUT(pd)) { @@ -455,12 +453,42 @@ void DssiSynthIF::showGui(bool v) } //--------------------------------------------------------- +// getGeometry +//--------------------------------------------------------- + +void DssiSynthIF::getGeometry(int*x, int*y, int*w, int*h) const +{ + if(!_gui) + { + *x=0;*y=0;*w=0;*h=0; + return; + } + + *x = _gui->x(); + *y = _gui->y(); + *w = _gui->width(); + *h = _gui->height(); +} + +//--------------------------------------------------------- +// setGeometry +//--------------------------------------------------------- + +void DssiSynthIF::setGeometry(int x, int y, int w, int h) +{ + if(!_gui) + return; + + _gui->setGeometry(x, y, w, h); +} + +//--------------------------------------------------------- // receiveEvent //--------------------------------------------------------- -MusECore::MidiPlayEvent DssiSynthIF::receiveEvent() +MidiPlayEvent DssiSynthIF::receiveEvent() { - return MusECore::MidiPlayEvent(); + return MidiPlayEvent(); } //--------------------------------------------------------- @@ -473,10 +501,10 @@ bool DssiSynthIF::init(DssiSynth* s) printf("DssiSynthIF::init\n"); #endif - synth = s; - const DSSI_Descriptor* dssi = synth->dssi; + _synth = s; + const DSSI_Descriptor* dssi = _synth->dssi; const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; - handle = ld->instantiate(ld, MusEGlobal::sampleRate); + _handle = ld->instantiate(ld, MusEGlobal::sampleRate); #ifdef OSC_SUPPORT _oscif.oscSetSynthIF(this); @@ -484,67 +512,86 @@ bool DssiSynthIF::init(DssiSynth* s) queryPrograms(); - int inports = synth->_inports; + int inports = _synth->_inports; if(inports != 0) { - int rv = posix_memalign((void**)&audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); + int rv = posix_memalign((void**)&_audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: DssiSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } - memset(audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + _audioInSilenceBuf[q] = MusEGlobal::denormalBias; + } + else + memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); - audioInBuffers = new float*[inports]; + _audioInBuffers = new float*[inports]; for(int k = 0; k < inports; ++k) { - int rv = posix_memalign((void**)&audioInBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize); + int rv = posix_memalign((void**)&_audioInBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: DssiSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } - memset(audioInBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); - ld->connect_port(handle, synth->iIdx[k], audioInBuffers[k]); + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + _audioInBuffers[k][q] = MusEGlobal::denormalBias; + } + else + memset(_audioInBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); + _iUsedIdx.push_back(false); // Start out with all false. + ld->connect_port(_handle, _synth->iIdx[k], _audioInBuffers[k]); } } - int outports = synth->_outports; + int outports = _synth->_outports; if(outports != 0) { - audioOutBuffers = new float*[outports]; + _audioOutBuffers = new float*[outports]; for(int k = 0; k < outports; ++k) { - int rv = posix_memalign((void**)&audioOutBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize); + int rv = posix_memalign((void**)&_audioOutBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: DssiSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } - memset(audioOutBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); - ld->connect_port(handle, synth->oIdx[k], audioOutBuffers[k]); + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + _audioOutBuffers[k][q] = MusEGlobal::denormalBias; + } + else + memset(_audioOutBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); + ld->connect_port(_handle, _synth->oIdx[k], _audioOutBuffers[k]); } } - int controlPorts = synth->_controlInPorts; - int controlOutPorts = synth->_controlOutPorts; + int controlPorts = _synth->_controlInPorts; + int controlOutPorts = _synth->_controlOutPorts; if(controlPorts != 0) - controls = new Port[controlPorts]; + _controls = new Port[controlPorts]; else - controls = 0; + _controls = 0; if(controlOutPorts != 0) - controlsOut = new Port[controlOutPorts]; + _controlsOut = new Port[controlOutPorts]; else - controlsOut = 0; + _controlsOut = 0; - synth->midiCtl2PortMap.clear(); - synth->port2MidiCtlMap.clear(); + _synth->midiCtl2PortMap.clear(); + _synth->port2MidiCtlMap.clear(); int cip = 0; int cop = 0; - for (unsigned long k = 0; k < synth->_portCount; ++k) + for (unsigned long k = 0; k < _synth->_portCount; ++k) { LADSPA_PortDescriptor pd = ld->PortDescriptors[k]; @@ -556,13 +603,12 @@ bool DssiSynthIF::init(DssiSynth* s) { if (LADSPA_IS_PORT_INPUT(pd)) { - controls[cip].idx = k; + _controls[cip].idx = k; float val; ladspaDefaultValue(ld, k, &val); - controls[cip].val = val; - controls[cip].tmpVal = val; - controls[cip].enCtrl = true; - controls[cip].en2Ctrl = true; + _controls[cip].val = val; + _controls[cip].tmpVal = val; + _controls[cip].enCtrl = true; #ifdef DSSI_DEBUG printf("DssiSynthIF::init control port:%d port idx:%lu name:%s\n", cip, k, ld->PortNames[k]); @@ -573,7 +619,7 @@ bool DssiSynthIF::init(DssiSynth* s) int ctlnum = DSSI_NONE; if(dssi->get_midi_controller_for_port) - ctlnum = dssi->get_midi_controller_for_port(handle, k); + ctlnum = dssi->get_midi_controller_for_port(_handle, k); // No controller number? Try to give it a unique one... if(ctlnum == DSSI_NONE) @@ -585,7 +631,7 @@ bool DssiSynthIF::init(DssiSynth* s) // If CC Controller7 is chosen we must make sure to use only non-common numbers. An already limited range // of 127 now becomes narrower. See the cool document midi-controllers.txt in the DSSI source for a // nice roundup of numbers and how to choose them and how they relate to synths and DSSI synths etc. ! - ctlnum = MusECore::CTRL_NRPN14_OFFSET + 0x2000 + cip; + ctlnum = CTRL_NRPN14_OFFSET + 0x2000 + cip; } else { @@ -610,7 +656,7 @@ bool DssiSynthIF::init(DssiSynth* s) printf("DssiSynthIF::init is NRPN control\n"); #endif - ctlnum = DSSI_NRPN_NUMBER(c) + MusECore::CTRL_NRPN14_OFFSET; + ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; } } @@ -620,8 +666,8 @@ bool DssiSynthIF::init(DssiSynth* s) #endif // We have a controller number! Insert it and the DSSI port number into both maps. - synth->midiCtl2PortMap.insert(std::pair<int, int>(ctlnum, cip)); - synth->port2MidiCtlMap.insert(std::pair<int, int>(cip, ctlnum)); + _synth->midiCtl2PortMap.insert(std::pair<int, int>(ctlnum, cip)); + _synth->port2MidiCtlMap.insert(std::pair<int, int>(cip, ctlnum)); // Support a special block for dssi synth ladspa controllers. // Put the ID at a special block after plugins (far after). @@ -630,42 +676,41 @@ bool DssiSynthIF::init(DssiSynth* s) float min, max; ladspaControlRange(ld, k, &min, &max); CtrlList* cl; - CtrlListList* cll = ((MusECore::AudioTrack*)synti)->controller(); + CtrlListList* cll = track()->controller(); iCtrlList icl = cll->find(id); if (icl == cll->end()) { cl = new CtrlList(id); cll->add(cl); - cl->setCurVal(controls[cip].val); + cl->setCurVal(_controls[cip].val); } else { cl = icl->second; - controls[cip].val = cl->curVal(); + _controls[cip].val = cl->curVal(); } cl->setRange(min, max); cl->setName(QString(name)); cl->setValueType(ladspaCtrlValueType(ld, k)); cl->setMode(ladspaCtrlMode(ld, k)); - ld->connect_port(handle, k, &controls[cip].val); + ld->connect_port(_handle, k, &_controls[cip].val); ++cip; } else if (LADSPA_IS_PORT_OUTPUT(pd)) { - controlsOut[cop].idx = k; - controlsOut[cop].val = 0.0; - controlsOut[cop].tmpVal = 0.0; - controlsOut[cop].enCtrl = false; - controlsOut[cop].en2Ctrl = false; + _controlsOut[cop].idx = k; + _controlsOut[cop].val = 0.0; + _controlsOut[cop].tmpVal = 0.0; + _controlsOut[cop].enCtrl = false; #ifdef DSSI_DEBUG printf("DssiSynthIF::init control output port:%d port idx:%lu name:%s\n", cop, k, ld->PortNames[k]); #endif // Control outs are not handled but still must be connected to something. - ld->connect_port(handle, k, &controlsOut[cop].val); + ld->connect_port(_handle, k, &_controlsOut[cop].val); ++cop; } @@ -677,7 +722,7 @@ bool DssiSynthIF::init(DssiSynth* s) // Set current configuration values. if(dssi->configure) { - char *rv = dssi->configure(handle, DSSI_PROJECT_DIRECTORY_KEY, + char *rv = dssi->configure(_handle, DSSI_PROJECT_DIRECTORY_KEY, MusEGlobal::museProject.toLatin1().constData()); //MusEGlobal::song->projectPath() if(rv) @@ -689,7 +734,7 @@ bool DssiSynthIF::init(DssiSynth* s) for(ciStringParamMap r = synti->_stringParamMap.begin(); r != synti->_stringParamMap.end(); ++r) { rv = 0; - rv = dssi->configure(handle, r->first.c_str(), r->second.c_str()); + rv = dssi->configure(_handle, r->first.c_str(), r->second.c_str()); if(rv) { fprintf(stderr, "MusE: Warning: plugin config key: %s value: %s \"%s\"\n", r->first.c_str(), r->second.c_str(), rv); @@ -700,7 +745,7 @@ bool DssiSynthIF::init(DssiSynth* s) // Set current program. if(dssi->select_program) - doSelectProgram(handle, synti->_curBankL, synti->_curProgram); + doSelectProgram(_handle, synti->_curBankL, synti->_curProgram); // // For stored initial control values, let SynthI::initInstance() take care of that via ::setParameter(). @@ -719,13 +764,13 @@ DssiSynthIF::DssiSynthIF(SynthI* s) #ifdef DSSI_DEBUG printf("DssiSynthIF::DssiSynthIF\n"); #endif - synth = 0; - handle = NULL; - controls = 0; - controlsOut = 0; - audioInBuffers = 0; - audioInSilenceBuf = 0; - audioOutBuffers = 0; + _synth = 0; + _handle = NULL; + _controls = 0; + _controlsOut = 0; + _audioInBuffers = 0; + _audioInSilenceBuf = 0; + _audioOutBuffers = 0; } //--------------------------------------------------------- @@ -742,19 +787,19 @@ DssiSynthIF::~DssiSynthIF() _oscif.oscSetSynthIF(NULL); #endif - if(synth) + if(_synth) { #ifdef DSSI_DEBUG printf("DssiSynthIF::~DssiSynthIF synth:%p\n", synth); #endif - if(synth->dssi) + if(_synth->dssi) { #ifdef DSSI_DEBUG printf("DssiSynthIF::~DssiSynthIF synth->dssi:%p\n", synth->dssi); #endif - if(synth->dssi->LADSPA_Plugin) + if(_synth->dssi->LADSPA_Plugin) { #ifdef DSSI_DEBUG printf("DssiSynthIF::~DssiSynthIFsynth->dssi->LADSPA_Plugin:%p\n", synth->dssi->LADSPA_Plugin); @@ -763,9 +808,9 @@ DssiSynthIF::~DssiSynthIF() } } - if(synth && synth->dssi && synth->dssi->LADSPA_Plugin) + if(_synth && _synth->dssi && _synth->dssi->LADSPA_Plugin) { - const DSSI_Descriptor* dssi = synth->dssi; + const DSSI_Descriptor* dssi = _synth->dssi; const LADSPA_Descriptor* descr = dssi->LADSPA_Plugin; #ifdef DSSI_DEBUG @@ -778,37 +823,37 @@ DssiSynthIF::~DssiSynthIF() printf("DssiSynthIF::~DssiSynthIF calling cleanup function\n"); #endif - descr->cleanup(handle); + descr->cleanup(_handle); } } - if(audioInBuffers) + if(_audioInBuffers) { - for(unsigned long i = 0; i < synth->_inports; ++i) + for(unsigned long i = 0; i < _synth->_inports; ++i) { - if(audioInBuffers[i]) - free(audioInBuffers[i]); + if(_audioInBuffers[i]) + free(_audioInBuffers[i]); } - delete[] audioInBuffers; + delete[] _audioInBuffers; } - if(audioInSilenceBuf) - free(audioInSilenceBuf); + if(_audioInSilenceBuf) + free(_audioInSilenceBuf); - if(audioOutBuffers) + if(_audioOutBuffers) { - for(unsigned long i = 0; i < synth->_outports; ++i) + for(unsigned long i = 0; i < _synth->_outports; ++i) { - if(audioOutBuffers[i]) - free(audioOutBuffers[i]); + if(_audioOutBuffers[i]) + free(_audioOutBuffers[i]); } - delete[] audioOutBuffers; + delete[] _audioOutBuffers; } - if(controls) - delete[] controls; + if(_controls) + delete[] _controls; - if(controlsOut) - delete[] controlsOut; + if(_controlsOut) + delete[] _controlsOut; } int DssiSynthIF::oldMidiStateHeader(const unsigned char** data) const @@ -824,16 +869,16 @@ int DssiSynthIF::oldMidiStateHeader(const unsigned char** data) const float DssiSynthIF::getParameter(unsigned long n) const { - if(n >= synth->_controlInPorts) + if(n >= _synth->_controlInPorts) { - printf("DssiSynthIF::getParameter param number %lu out of range of ports:%lu\n", n, synth->_controlInPorts); + printf("DssiSynthIF::getParameter param number %lu out of range of ports:%lu\n", n, _synth->_controlInPorts); return 0.0; } - if(!controls) + if(!_controls) return 0.0; - return controls[n].val; + return _controls[n].val; } //--------------------------------------------------------- // getParameter @@ -841,16 +886,16 @@ float DssiSynthIF::getParameter(unsigned long n) const float DssiSynthIF::getParameterOut(unsigned long n) const { - if(n >= synth->_controlOutPorts) + if(n >= _synth->_controlOutPorts) { - printf("DssiSynthIF::getParameterOut param number %lu out of range of ports:%lu\n", n, synth->_controlOutPorts); + printf("DssiSynthIF::getParameterOut param number %lu out of range of ports:%lu\n", n, _synth->_controlOutPorts); return 0.0; } - if(!controlsOut) + if(!_controlsOut) return 0.0; - return controlsOut[n].val; + return _controlsOut[n].val; } //--------------------------------------------------------- @@ -1006,9 +1051,9 @@ void DssiSynthIF::write(int level, Xml& xml) const */ // Store controls as parameters... - for(unsigned long c = 0; c < synth->_controlInPorts; ++c) + for(unsigned long c = 0; c < _synth->_controlInPorts; ++c) { - float f = controls[c].val; + float f = _controls[c].val; xml.floatTag(level, "param", f); } } @@ -1027,9 +1072,9 @@ void DssiSynthIF::preProcessAlways() // Return true if event pointer filled. //-------------------------------------------------------- -bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t* event) +bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event) { - const DSSI_Descriptor* dssi = synth->dssi; + const DSSI_Descriptor* dssi = _synth->dssi; int chn = e.channel(); int a = e.dataA(); @@ -1050,9 +1095,9 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t switch(e.type()) { - case MusECore::ME_NOTEON: + case ME_NOTEON: #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_NOTEON\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_NOTEON\n"); #endif snd_seq_ev_clear(event); @@ -1062,19 +1107,19 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t else snd_seq_ev_set_noteoff(event, chn, a, 0); break; - case MusECore::ME_NOTEOFF: + case ME_NOTEOFF: #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_NOTEOFF\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_NOTEOFF\n"); #endif snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; snd_seq_ev_set_noteoff(event, chn, a, 0); break; - case MusECore::ME_PROGRAM: + case ME_PROGRAM: { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_PROGRAM\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_PROGRAM\n"); #endif int bank = (a >> 8) & 0xff; @@ -1084,25 +1129,25 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t synti->_curProgram = prog; if(dssi->select_program) - doSelectProgram(handle, bank, prog); + doSelectProgram(_handle, bank, prog); // Event pointer not filled. Return false. return false; } break; - case MusECore::ME_CONTROLLER: + case ME_CONTROLLER: { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_CONTROLLER\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER\n"); #endif if((a == 0) || (a == 32)) return false; - if(a == MusECore::CTRL_PROGRAM) + if(a == CTRL_PROGRAM) { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_PROGRAM\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PROGRAM\n"); #endif int bank = (b >> 8) & 0xff; @@ -1113,16 +1158,16 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t synti->_curProgram = prog; if(dssi->select_program) - doSelectProgram(handle, bank, prog); + doSelectProgram(_handle, bank, prog); // Event pointer not filled. Return false. return false; } - if(a == MusECore::CTRL_PITCH) + if(a == CTRL_PITCH) { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_PITCH\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PITCH\n"); #endif snd_seq_ev_clear(event); @@ -1132,10 +1177,10 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t return true; } - if(a == MusECore::CTRL_AFTERTOUCH) + if(a == CTRL_AFTERTOUCH) { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_AFTERTOUCH\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_AFTERTOUCH\n"); #endif snd_seq_ev_clear(event); @@ -1145,10 +1190,10 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t return true; } - if((a | 0xff) == MusECore::CTRL_POLYAFTER) + if((a | 0xff) == CTRL_POLYAFTER) { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_POLYAFTER\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_POLYAFTER\n"); #endif snd_seq_ev_clear(event); @@ -1160,14 +1205,14 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; - MusECore::ciMidiCtl2LadspaPort ip = synth->midiCtl2PortMap.find(a); + ciMidiCtl2LadspaPort ip = _synth->midiCtl2PortMap.find(a); // Is it just a regular midi controller, not mapped to a LADSPA port (either by the plugin or by us)? // NOTE: There's no way to tell which of these controllers is supported by the plugin. // For example sustain footpedal or pitch bend may be supported, but not mapped to any LADSPA port. - if(ip == synth->midiCtl2PortMap.end()) + if(ip == _synth->midiCtl2PortMap.end()) { int ctlnum = a; - if(MusECore::midiControllerType(a) != MusECore::MidiController::Controller7) + if(midiControllerType(a) != MidiController::Controller7) return false; // Event pointer not filled. Return false. else { @@ -1189,20 +1234,20 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t } unsigned long k = ip->second; - unsigned long i = controls[k].idx; + unsigned long i = _controls[k].idx; int ctlnum = DSSI_NONE; if(dssi->get_midi_controller_for_port) - ctlnum = dssi->get_midi_controller_for_port(handle, i); + ctlnum = dssi->get_midi_controller_for_port(_handle, i); // No midi controller for the ladspa port? Send to ladspa control. if(ctlnum == DSSI_NONE) { // Sanity check. - if(k > synth->_controlInPorts) + if(k > _synth->_controlInPorts) return false; // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. - ctlnum = k + (MusECore::CTRL_NRPN14_OFFSET + 0x2000); + ctlnum = k + (CTRL_NRPN14_OFFSET + 0x2000); } else { @@ -1228,7 +1273,7 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t else if(DSSI_IS_NRPN(ctlnum)) { - ctlnum = DSSI_NRPN_NUMBER(c) + MusECore::CTRL_NRPN14_OFFSET; + ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; #ifdef DSSI_DEBUG printf("DssiSynthIF::processEvent is NRPN ctlnum:%x(h) %d(d)\n", ctlnum, ctlnum); @@ -1244,7 +1289,7 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t #endif // Set the ladspa port value. - controls[k].val = val; + _controls[k].val = val; // Need to update the automation value, otherwise it overwrites later with the last automation value. if(id() != -1) @@ -1255,25 +1300,25 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t return false; } break; - case MusECore::ME_PITCHBEND: + case ME_PITCHBEND: snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; snd_seq_ev_set_pitchbend(event, chn, a); break; - case MusECore::ME_AFTERTOUCH: + case ME_AFTERTOUCH: snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; snd_seq_ev_set_chanpress(event, chn, a); break; - case MusECore::ME_POLYAFTER: + case ME_POLYAFTER: snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; snd_seq_ev_set_keypress(event, chn, a & 0x7f, b & 0x7f); break; - case MusECore::ME_SYSEX: + case ME_SYSEX: { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_SYSEX\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_SYSEX\n"); #endif const unsigned char* data = e.data(); @@ -1311,7 +1356,7 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t if (QString((const char*)e.data()).startsWith("PARAMSAVE")) { #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::processEvent midi event is MusECore::ME_SYSEX PARAMSAVE\n"); + fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_SYSEX PARAMSAVE\n"); #endif unsigned long dlen = e.len() - 9; // Minus "PARAMSAVE" @@ -1362,7 +1407,7 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t //else { // NOTE: There is a limit on the size of a sysex. Got this: - // "DssiSynthIF::processEvent midi event is MusECore::ME_SYSEX" + // "DssiSynthIF::processEvent midi event is ME_SYSEX" // "WARNING: MIDI event of type ? decoded to 367 bytes, discarding" // That might be ALSA doing that. snd_seq_ev_clear(event); @@ -1385,411 +1430,478 @@ bool DssiSynthIF::processEvent(const MusECore::MidiPlayEvent& e, snd_seq_event_t //--------------------------------------------------------- // getData +// If ports is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- -MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MPEventList* el, MusECore::iMPEvent start_event, unsigned pos, int ports, unsigned nframes, float** buffer) +iMPEvent DssiSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent start_event, unsigned pos, int ports, unsigned nframes, float** buffer) { - // We may not be using nevents all at once - this will be just the maximum. - unsigned long nevents = el->size() + synti->eventFifo.getSize(); - snd_seq_event_t events[nevents]; - - int frameOffset = MusEGlobal::audio->getFrameOffset(); - unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); - - #ifdef DSSI_DEBUG_PROCESS - fprintf(stderr, "DssiSynthIF::getData: pos:%u ports:%d nframes:%u syncFrame:%lu nevents:%lu\n", pos, ports, nframes, syncFrame, nevents); + // We may not be using ev_buf_sz all at once - this will be just the maximum. + const unsigned long ev_buf_sz = el->size() + synti->eventFifo.getSize(); + snd_seq_event_t events[ev_buf_sz]; + + const int frameOffset = MusEGlobal::audio->getFrameOffset(); + const unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); + + #ifdef DSSI_DEBUG_PROCESS + fprintf(stderr, "DssiSynthIF::getData: pos:%u ports:%d nframes:%u syncFrame:%lu ev_buf_sz:%lu\n", pos, ports, nframes, syncFrame, ev_buf_sz); #endif - + // All ports must be connected to something! - unsigned long nop, k; - - nop = ((unsigned long) ports) > synth->_outports ? synth->_outports : ((unsigned long) ports); - - const DSSI_Descriptor* dssi = synth->dssi; + const unsigned long nop = ((unsigned long) ports) > _synth->_outports ? _synth->_outports : ((unsigned long) ports); + + const DSSI_Descriptor* dssi = _synth->dssi; const LADSPA_Descriptor* descr = dssi->LADSPA_Plugin; unsigned long sample = 0; - - // To remember the last retrieved value of each AudioTrack controller. - //float prev_ctrl_values[synth->_controlInPorts]; - - // NOTE Tested: Variable run-lengths worked superbly for LADSPA and DSSI synths. But DSSI-VST definitely - // does NOT like changing sample run length. It crashes the plugin and Wine (but MusE keeps running!). - // Furthermore, it resizes the shared memory (mmap, remap) upon each run length DIFFERENT from the last. + + // NOTE Tested: Variable run-lengths worked superbly for LADSPA and DSSI synths. But DSSI-VST definitely + // does NOT like changing sample run length. It crashes the plugin and Wine (but MusE keeps running!). + // Furthermore, it resizes the shared memory (mmap, remap) upon each run length DIFFERENT from the last. // And all of this done through client-server communications. It doesn't seem designed for this technique. // - // So we could support an alternate technique: A fixed control processing rate, in number of samples. + // So we could support an alternate technique: A fixed control processing rate, in number of samples. // - // Allow user to choose either a fixed rate or these 'packets' for LADSPA and DSSI plugins/synths, + // Allow user to choose either a fixed rate or these 'packets' for LADSPA and DSSI plugins/synths, // but make fixed-rate MANDATORY for DSSI-VST plugins and synths. - // + // // Or K.I.S.S - Just use fixed rates only, but allow it to be changed. I'm worried about libraries and // plugins other than DSSI-VST. What if they need the fixed-rate, too? // How to tell, and manage it all...? // But this 'packet' method sure seems to work nicely so far, so we'll throw it in... // // Must make this detectable for dssi vst synths, just like the plugins' in-place blacklist. - const bool usefixedrate = synth->_isDssiVst; // Try this. (was: true) - // TODO Make this number a global setting. - // Note for dssi-vst this MUST equal MusEGlobal::audio period. It doesn't like broken-up runs (it stutters), + const bool usefixedrate = _synth->_isDssiVst; + // Note for dssi-vst this MUST equal MusEGlobal::audio period. It doesn't like broken-up runs (it stutters), // even with fixed sizes. Could be a Wine + Jack thing, wanting a full Jack buffer's length. - unsigned long fixedsize = nframes; // was: 2048 - // For now, the fixed size is clamped to the MusEGlobal::audio buffer size. // TODO: We could later add slower processing over several cycles - - // so that users can select a small MusEGlobal::audio period but a larger control period. - if(fixedsize > nframes) - fixedsize = nframes; - - unsigned long min_per = MusEGlobal::config.minControlProcessPeriod; // Must be power of 2 ! - if(min_per > nframes) - min_per = nframes; - - #ifdef DSSI_DEBUG_PROCESS + // so that users can select a small MusEGlobal::audio period but a larger control period. + const unsigned long min_per = (usefixedrate || MusEGlobal::config.minControlProcessPeriod > nframes) ? nframes : MusEGlobal::config.minControlProcessPeriod; + const unsigned long min_per_mask = min_per-1; // min_per must be power of 2 + + AudioTrack* atrack = track(); + const AutomationType at = atrack->automationType(); + const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; + const unsigned long in_ctrls = _synth->inControls(); + CtrlListList* cll = atrack->controller(); + ciCtrlList icl_first; + const int plug_id = id(); + if(plug_id != -1 && ports != 0) // Don't bother if not 'running'. + icl_first = cll->lower_bound(genACnum(plug_id, 0)); + + #ifdef DSSI_DEBUG_PROCESS fprintf(stderr, "DssiSynthIF::getData: Handling inputs...\n"); #endif - + // Handle inputs... - if(!((MusECore::AudioTrack*)synti)->noInRoute()) + if(ports != 0) // Don't bother if not 'running'. { - RouteList* irl = ((MusECore::AudioTrack*)synti)->inRoutes(); - iRoute i = irl->begin(); - if(!i->track->isMidiTrack()) + if(!atrack->noInRoute()) { - int ch = i->channel == -1 ? 0 : i->channel; - int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; - int chs = i->channels == -1 ? 0 : i->channels; - - if((unsigned)ch < synth->_inports && (unsigned)(ch + chs) <= synth->_inports) - { - int h = remch + chs; - for(int j = remch; j < h; ++j) - synth->iUsedIdx[j] = true; - - ((MusECore::AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &audioInBuffers[remch]); - } - } - - ++i; - for(; i != irl->end(); ++i) - { - if(i->track->isMidiTrack()) - continue; + RouteList* irl = atrack->inRoutes(); + iRoute i = irl->begin(); + if(!i->track->isMidiTrack()) + { + const int ch = i->channel == -1 ? 0 : i->channel; + const int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; + const int chs = i->channels == -1 ? 0 : i->channels; - int ch = i->channel == -1 ? 0 : i->channel; - int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; - int chs = i->channels == -1 ? 0 : i->channels; - - if((unsigned)ch < synth->_inports && (unsigned)(ch + chs) <= synth->_inports) - { - bool u1 = synth->iUsedIdx[remch]; - if(chs >= 2) + if((unsigned)ch < _synth->inPorts() && (unsigned)(ch + chs) <= _synth->inPorts()) { - bool u2 = synth->iUsedIdx[remch + 1]; - if(u1 && u2) - ((MusECore::AudioTrack*)i->track)->addData(pos, chs, ch, -1, nframes, &audioInBuffers[remch]); - else - if(!u1 && !u2) - ((MusECore::AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &audioInBuffers[remch]); - else - { - if(u1) - ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch, 1, nframes, &audioInBuffers[remch]); - else - ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch, 1, nframes, &audioInBuffers[remch]); + const int h = remch + chs; + for(int j = remch; j < h; ++j) + _iUsedIdx[j] = true; - if(u2) - ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch + 1, 1, nframes, &audioInBuffers[remch + 1]); - else - ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch + 1, 1, nframes, &audioInBuffers[remch + 1]); - } + ((AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]); } - else + } + + ++i; + for(; i != irl->end(); ++i) + { + if(i->track->isMidiTrack()) + continue; + + const int ch = i->channel == -1 ? 0 : i->channel; + const int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; + const int chs = i->channels == -1 ? 0 : i->channels; + + if((unsigned)ch < _synth->inPorts() && (unsigned)(ch + chs) <= _synth->inPorts()) { - if(u1) - ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch, -1, nframes, &audioInBuffers[remch]); + const bool u1 = _iUsedIdx[remch]; + if(chs >= 2) + { + const bool u2 = _iUsedIdx[remch + 1]; + if(u1 && u2) + ((AudioTrack*)i->track)->addData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]); else - ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch, -1, nframes, &audioInBuffers[remch]); + if(!u1 && !u2) + ((AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]); + else + { + if(u1) + ((AudioTrack*)i->track)->addData(pos, 1, ch, 1, nframes, &_audioInBuffers[remch]); + else + ((AudioTrack*)i->track)->copyData(pos, 1, ch, 1, nframes, &_audioInBuffers[remch]); + + if(u2) + ((AudioTrack*)i->track)->addData(pos, 1, ch + 1, 1, nframes, &_audioInBuffers[remch + 1]); + else + ((AudioTrack*)i->track)->copyData(pos, 1, ch + 1, 1, nframes, &_audioInBuffers[remch + 1]); + } + } + else + { + if(u1) + ((AudioTrack*)i->track)->addData(pos, 1, ch, -1, nframes, &_audioInBuffers[remch]); + else + ((AudioTrack*)i->track)->copyData(pos, 1, ch, -1, nframes, &_audioInBuffers[remch]); + } + + const int h = remch + chs; + for(int j = remch; j < h; ++j) + _iUsedIdx[j] = true; } - - int h = remch + chs; - for(int j = remch; j < h; ++j) - synth->iUsedIdx[j] = true; } } - } - - #ifdef DSSI_DEBUG_PROCESS + } + + #ifdef DSSI_DEBUG_PROCESS fprintf(stderr, "DssiSynthIF::getData: Processing automation control values...\n"); #endif - + + int cur_slice = 0; while(sample < nframes) { - unsigned long nsamp = usefixedrate ? fixedsize : nframes - sample; + unsigned long nsamp = nframes - sample; + const unsigned long slice_frame = pos + sample; // - // Process automation control values, while also determining the maximum acceptable - // size of this run. Further processing, from FIFOs for example, can lower the size - // from there, but this section determines where the next highest maximum frame + // Process automation control values, while also determining the maximum acceptable + // size of this run. Further processing, from FIFOs for example, can lower the size + // from there, but this section determines where the next highest maximum frame // absolutely needs to be for smooth playback of the controller value stream... // - if(id() != -1) + if(ports != 0) // Don't bother if not 'running'. { - unsigned long frame = pos + sample; - AutomationType at = AUTO_OFF; - at = synti->automationType(); - bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; - AudioTrack* track = (static_cast<AudioTrack*>(synti)); - int nextFrame; - for(unsigned long k = 0; k < synth->_controlInPorts; ++k) - { - controls[k].val = track->controller()->value(genACnum(id(), k), frame, - no_auto || !controls[k].enCtrl || !controls[k].en2Ctrl, - &nextFrame); -#ifdef DSSI_DEBUG_PROCESS - printf("DssiSynthIF::getData k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, frame, nextFrame, nsamp); -#endif - if(MusEGlobal::audio->isPlaying() && !usefixedrate && nextFrame != -1) + ciCtrlList icl = icl_first; + for(unsigned long k = 0; k < in_ctrls; ++k) + { + CtrlList* cl = (cll && plug_id != -1 && icl != cll->end()) ? icl->second : NULL; + CtrlInterpolate& ci = _controls[k].interp; + // Always refresh the interpolate struct at first, since things may have changed. + // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. + if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && + (slice_frame < (unsigned long)ci.sFrame || (ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame)) ) ) { - // Returned value of nextFrame can be zero meaning caller replaces with some (constant) value. - unsigned long samps = (unsigned long)nextFrame; - if(samps > frame + min_per) + if(cl && plug_id != -1 && (unsigned long)cl->id() == genACnum(plug_id, k)) + { + cl->getInterpolation(slice_frame, no_auto || !_controls[k].enCtrl, &ci); + if(icl != cll->end()) + ++icl; + } + else { - unsigned long diff = samps - frame; - unsigned long mask = min_per-1; // min_per must be power of 2 - samps = diff & ~mask; - if((diff & mask) != 0) + // No matching controller, or end. Just copy the current value into the interpolator. + // Keep the current icl iterator, because since they are sorted by frames, + // if the IDs didn't match it means we can just let k catch up with icl. + ci.sFrame = 0; + ci.eFrame = -1; + ci.sVal = _controls[k].val; + ci.eVal = ci.sVal; + ci.doInterp = false; + ci.eStop = false; + } + } + else + { + if(ci.eStop && ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. + { + // Clear the stop condition and set up the interp struct appropriately as an endless value. + ci.sFrame = 0; //ci->eFrame; + ci.eFrame = -1; + ci.sVal = ci.eVal; + ci.doInterp = false; + ci.eStop = false; + } + if(cl && cll && icl != cll->end()) + ++icl; + } + + if(!usefixedrate && MusEGlobal::audio->isPlaying()) + { + unsigned long samps = nsamp; + if(ci.eFrame != -1) + samps = (unsigned long)ci.eFrame - slice_frame; + + if(!ci.doInterp && samps > min_per) + { + samps &= ~min_per_mask; + if((samps & min_per_mask) != 0) samps += min_per; } else samps = min_per; - + if(samps < nsamp) nsamp = samps; + } - } -#ifdef DSSI_DEBUG - printf("DssiSynthIF::getData sample:%lu nsamp:%lu\n", sample, nsamp); + + if(ci.doInterp && cl) + _controls[k].val = cl->interpolate(MusEGlobal::audio->isPlaying() ? slice_frame : pos, ci); + else + _controls[k].val = ci.sVal; + +#ifdef DSSI_DEBUG_PROCESS + fprintf(stderr, "DssiSynthIF::getData k:%lu sample:%lu frame:%lu ci.eFrame:%d nsamp:%lu \n", k, sample, frame, ci.eFrame, nsamp); #endif + + } } - + +#ifdef DSSI_DEBUG_PROCESS + fprintf(stderr, "DssiSynthIF::getData sample:%lu nsamp:%lu\n", sample, nsamp); +#endif + bool found = false; - unsigned long frame = 0; + unsigned long frame = 0; unsigned long index = 0; - unsigned long evframe; + unsigned long evframe; // Get all control ring buffer items valid for this time period... while(!_controlFifo.isEmpty()) { - ControlEvent v = _controlFifo.peek(); - // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. + ControlEvent v = _controlFifo.peek(); + // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. - evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes; + evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes; - #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::getData found:%d evframe:%lu frame:%lu event frame:%lu idx:%lu val:%f unique:%d\n", + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::getData found:%d evframe:%lu frame:%lu event frame:%lu idx:%lu val:%f unique:%d\n", found, evframe, frame, v.frame, v.idx, v.value, v.unique); #endif // Protection. Observed this condition. Why? Supposed to be linear timestamps. if(found && evframe < frame) { - printf("DssiSynthIF::getData *** Error: evframe:%lu < frame:%lu event: frame:%lu idx:%lu val:%f unique:%d\n", - evframe, frame, v.frame, v.idx, v.value, v.unique); + fprintf(stderr, "DssiSynthIF::getData *** Error: evframe:%lu < frame:%lu event: frame:%lu idx:%lu val:%f unique:%d\n", + evframe, frame, v.frame, v.idx, v.value, v.unique); // No choice but to ignore it. _controlFifo.remove(); // Done with the ring buffer's item. Remove it. continue; - } - - if(evframe >= nframes // Next events are for a later period. - || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) - || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close. - || (usefixedrate && found && v.unique && v.idx == index)) // Special for dssi-vst: Fixed rate and must reply to all. + } + + if(evframe >= nframes // Next events are for a later period. + || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) + || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close. + || (usefixedrate && found && v.unique && v.idx == index)) // Special for dssi-vst: Fixed rate and must reply to all. break; _controlFifo.remove(); // Done with the ring buffer's item. Remove it. - if(v.idx >= synth->_controlInPorts) // Sanity check. + if(v.idx >= in_ctrls) // Sanity check. break; + found = true; frame = evframe; index = v.idx; - // Set the ladspa control port value. - controls[v.idx].val = v.value; - + + if(ports == 0) // Don't bother if not 'running'. + _controls[v.idx].val = v.value; // Might as well at least update these. + else + { + CtrlInterpolate* ci = &_controls[v.idx].interp; + // Tell it to stop the current ramp at this frame, when it does stop, set this value: + ci->eFrame = frame; + ci->eVal = v.value; + ci->eStop = true; + } + // Need to update the automation value, otherwise it overwrites later with the last automation value. - if(id() != -1) - synti->setPluginCtrlVal(genACnum(id(), v.idx), v.value); + if(plug_id != -1) + synti->setPluginCtrlVal(genACnum(plug_id, v.idx), v.value); } - + if(found && !usefixedrate) // If a control FIFO item was found, takes priority over automation controller stream. nsamp = frame - sample; - - if(sample + nsamp >= nframes) // Safety check. - nsamp = nframes - sample; - + + if(sample + nsamp > nframes) // Safety check. + nsamp = nframes - sample; + // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead. // Note this means it is still possible to get stuck in the top loop (at least for a while). - if(nsamp == 0) - continue; - - nevents = 0; - // Process event list events... - for(; start_event != el->end(); ++start_event) + if(nsamp != 0) { - #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::getData eventlist event time:%d pos:%u sample:%lu nsamp:%lu frameOffset:%d\n", start_event->time(), pos, sample, nsamp, frameOffset); - #endif - - if(start_event->time() >= (pos + sample + nsamp + frameOffset)) // frameOffset? Test again... + unsigned long nevents = 0; + if(ports != 0) // Don't bother if not 'running'. { - #ifdef DSSI_DEBUG - fprintf(stderr, " event is for future:%lu, breaking loop now\n", start_event->time() - frameOffset - pos - sample); - #endif - break; - } - - // Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values. - // Same code as in MidiPort::sendEvent() - if(synti->midiPort() != -1) - { - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[synti->midiPort()]; - if(start_event->type() == MusECore::ME_CONTROLLER) - { - int da = start_event->dataA(); - int db = start_event->dataB(); - db = mp->limitValToInstrCtlRange(da, db); - if(!mp->setHwCtrlState(start_event->channel(), da, db)) - continue; - } - else if(start_event->type() == MusECore::ME_PITCHBEND) - { - int da = mp->limitValToInstrCtlRange(MusECore::CTRL_PITCH, start_event->dataA()); - if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_PITCH, da)) - continue; - } - else if(start_event->type() == MusECore::ME_AFTERTOUCH) - { - int da = mp->limitValToInstrCtlRange(MusECore::CTRL_AFTERTOUCH, start_event->dataA()); - if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_AFTERTOUCH, da)) - continue; - } - else if(start_event->type() == MusECore::ME_POLYAFTER) - { - int ctl = (MusECore::CTRL_POLYAFTER & ~0xff) | (start_event->dataA() & 0x7f); - int db = mp->limitValToInstrCtlRange(ctl, start_event->dataB()); - if(!mp->setHwCtrlState(start_event->channel(), ctl , db)) - continue; - } - else if(start_event->type() == MusECore::ME_PROGRAM) + // Process event list events... + for(; start_event != el->end(); ++start_event) { - if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_PROGRAM, start_event->dataA())) - continue; + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::getData eventlist event time:%d pos:%u sample:%lu nsamp:%lu frameOffset:%d\n", start_event->time(), pos, sample, nsamp, frameOffset); + #endif + + if(start_event->time() >= (pos + sample + nsamp + frameOffset)) // frameOffset? Test again... + { + #ifdef DSSI_DEBUG + fprintf(stderr, " event is for future:%lu, breaking loop now\n", start_event->time() - frameOffset - pos - sample); + #endif + break; + } + + // Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values. + // Same code as in MidiPort::sendEvent() + if(synti->midiPort() != -1) + { + MidiPort* mp = &MusEGlobal::midiPorts[synti->midiPort()]; + if(start_event->type() == ME_CONTROLLER) + { + int da = start_event->dataA(); + int db = start_event->dataB(); + db = mp->limitValToInstrCtlRange(da, db); + if(!mp->setHwCtrlState(start_event->channel(), da, db)) + continue; + } + else if(start_event->type() == ME_PITCHBEND) + { + int da = mp->limitValToInstrCtlRange(CTRL_PITCH, start_event->dataA()); + if(!mp->setHwCtrlState(start_event->channel(), CTRL_PITCH, da)) + continue; + } + else if(start_event->type() == ME_AFTERTOUCH) + { + int da = mp->limitValToInstrCtlRange(CTRL_AFTERTOUCH, start_event->dataA()); + if(!mp->setHwCtrlState(start_event->channel(), CTRL_AFTERTOUCH, da)) + continue; + } + else if(start_event->type() == ME_POLYAFTER) + { + int ctl = (CTRL_POLYAFTER & ~0xff) | (start_event->dataA() & 0x7f); + int db = mp->limitValToInstrCtlRange(ctl, start_event->dataB()); + if(!mp->setHwCtrlState(start_event->channel(), ctl , db)) + continue; + } + else if(start_event->type() == ME_PROGRAM) + { + if(!mp->setHwCtrlState(start_event->channel(), CTRL_PROGRAM, start_event->dataA())) + continue; + } + } + + // Returns false if the event was not filled. It was handled, but some other way. + if(processEvent(*start_event, &events[nevents])) + { + // Time-stamp the event. + int ft = start_event->time() - frameOffset - pos - sample; + if(ft < 0) + ft = 0; + + if (ft >= int(nsamp)) + { + fprintf(stderr, "DssiSynthIF::getData: eventlist event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", start_event->time(), pos, frameOffset, ft, sample, nsamp); + ft = nsamp - 1; + } + + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::getData eventlist: ft:%d current nevents:%lu\n", ft, nevents); + #endif + + // "Each event is timestamped relative to the start of the block, (mis)using the ALSA "tick time" field as a frame count. + // The host is responsible for ensuring that events with differing timestamps are already ordered by time." - From dssi.h + events[nevents].time.tick = ft; + + ++nevents; + } } } - - // Returns false if the event was not filled. It was handled, but some other way. - if(processEvent(*start_event, &events[nevents])) + + // Now process putEvent events... + while(!synti->eventFifo.isEmpty()) { - // Time-stamp the event. - int ft = start_event->time() - frameOffset - pos - sample; - if(ft < 0) - ft = 0; + MidiPlayEvent e = synti->eventFifo.peek(); + + #ifdef DSSI_DEBUG + fprintf(stderr, "DssiSynthIF::getData eventFifo event time:%d\n", e.time()); + #endif - if (ft >= int(nsamp)) + if(e.time() >= (pos + sample + nsamp + frameOffset)) + break; + + synti->eventFifo.remove(); // Done with ring buffer's event. Remove it. + if(ports != 0) // Don't bother if not 'running'. { - printf("DssiSynthIF::getData: eventlist event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", start_event->time(), pos, frameOffset, ft, sample, nsamp); - ft = nsamp - 1; + // Returns false if the event was not filled. It was handled, but some other way. + if(processEvent(e, &events[nevents])) + { + // Time-stamp the event. + int ft = e.time() - frameOffset - pos - sample; + if(ft < 0) + ft = 0; + if (ft >= int(nsamp)) + { + fprintf(stderr, "DssiSynthIF::getData: eventFifo event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", e.time(), pos, frameOffset, ft, sample, nsamp); + ft = nsamp - 1; + } + // "Each event is timestamped relative to the start of the block, (mis)using the ALSA "tick time" field as a frame count. + // The host is responsible for ensuring that events with differing timestamps are already ordered by time." - From dssi.h + events[nevents].time.tick = ft; + + ++nevents; + } } - - #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::getData eventlist: ft:%d current nevents:%lu\n", ft, nevents); - #endif - - // "Each event is timestamped relative to the start of the block, (mis)using the ALSA "tick time" field as a frame count. - // The host is responsible for ensuring that events with differing timestamps are already ordered by time." - From dssi.h - events[nevents].time.tick = ft; - - ++nevents; } - } - - // Now process putEvent events... - while(!synti->eventFifo.isEmpty()) - { - MusECore::MidiPlayEvent e = synti->eventFifo.peek(); - - #ifdef DSSI_DEBUG - fprintf(stderr, "DssiSynthIF::getData eventFifo event time:%d\n", e.time()); + + #ifdef DSSI_DEBUG_PROCESS + fprintf(stderr, "DssiSynthIF::getData: Connecting and running. sample:%lu nsamp:%lu nevents:%lu\n", sample, nsamp, nevents); #endif - - if(e.time() >= (pos + sample + nsamp + frameOffset)) - break; - - synti->eventFifo.remove(); // Done with ring buffer's event. Remove it. - // Returns false if the event was not filled. It was handled, but some other way. - if(processEvent(e, &events[nevents])) + + if(ports != 0) // Don't bother if not 'running'. { - // Time-stamp the event. - int ft = e.time() - frameOffset - pos - sample; - if(ft < 0) - ft = 0; - if (ft >= int(nsamp)) + unsigned long k = 0; + // Connect the given buffers directly to the ports, up to a max of synth ports. + for(; k < nop; ++k) + descr->connect_port(_handle, _synth->oIdx[k], buffer[k] + sample); + // Connect the remaining ports to some local buffers (not used yet). + for(; k < _synth->_outports; ++k) + descr->connect_port(_handle, _synth->oIdx[k], _audioOutBuffers[k] + sample); + // Connect all inputs either to some local buffers, or a silence buffer. + for(k = 0; k < _synth->_inports; ++k) + { + if(_iUsedIdx[k]) + { + _iUsedIdx[k] = false; // Reset + descr->connect_port(_handle, _synth->iIdx[k], _audioInBuffers[k] + sample); + } + else + { + descr->connect_port(_handle, _synth->iIdx[k], _audioInSilenceBuf + sample); + } + } + + // Run the synth for a period of time. This processes events and gets/fills our local buffers... + if(_synth->dssi->run_synth) { - printf("DssiSynthIF::getData: eventFifo event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", e.time(), pos, frameOffset, ft, sample, nsamp); - ft = nsamp - 1; + _synth->dssi->run_synth(_handle, nsamp, events, nevents); } - // "Each event is timestamped relative to the start of the block, (mis)using the ALSA "tick time" field as a frame count. - // The host is responsible for ensuring that events with differing timestamps are already ordered by time." - From dssi.h - events[nevents].time.tick = ft; - - ++nevents; - } - } - - #ifdef DSSI_DEBUG_PROCESS - fprintf(stderr, "DssiSynthIF::getData: Connecting and running. sample:%lu nsamp:%lu nevents:%lu\n", sample, nsamp, nevents); - #endif - - k = 0; - // Connect the given buffers directly to the ports, up to a max of synth ports. - for(; k < nop; ++k) - descr->connect_port(handle, synth->oIdx[k], buffer[k] + sample); - // Connect the remaining ports to some local buffers (not used yet). - for(; k < synth->_outports; ++k) - descr->connect_port(handle, synth->oIdx[k], audioOutBuffers[k] + sample); - // Connect all inputs either to some local buffers, or a silence buffer. - for(k = 0; k < synth->_inports; ++k) - { - if(synth->iUsedIdx[k]) - { - synth->iUsedIdx[k] = false; // Reset - descr->connect_port(handle, synth->iIdx[k], audioInBuffers[k] + sample); + else if (_synth->dssi->run_multiple_synths) + { + snd_seq_event_t* ev = events; + _synth->dssi->run_multiple_synths(1, &_handle, nsamp, &ev, &nevents); + } + // TIP: Until we add programs to plugins, uncomment these four checks to load dssi effects as synths, in order to have programs. + //else + //if(synth->dssi->LADSPA_Plugin->run) + //{ + // synth->dssi->LADSPA_Plugin->run(handle, nsamp); + //} } - else - { - descr->connect_port(handle, synth->iIdx[k], audioInSilenceBuf + sample); - } - } - - // Run the synth for a period of time. This processes events and gets/fills our local buffers... - if(synth->dssi->run_synth) - { - synth->dssi->run_synth(handle, nsamp, events, nevents); - } - else if (synth->dssi->run_multiple_synths) - { - snd_seq_event_t* ev = events; - synth->dssi->run_multiple_synths(1, &handle, nsamp, &ev, &nevents); + + sample += nsamp; } - // TIP: Until we add programs to plugins, uncomment these four checks to load dssi effects as synths, in order to have programs. - //else - //if(synth->dssi->LADSPA_Plugin->run) - //{ - // synth->dssi->LADSPA_Plugin->run(handle, nsamp); - //} - - sample += nsamp; + + ++cur_slice; // Slice is done. Moving on to any next slice now... } - + return start_event; } @@ -1797,7 +1909,7 @@ MusECore::iMPEvent DssiSynthIF::getData(MusECore::MidiPort* /*mp*/, MusECore::MP // putEvent //--------------------------------------------------------- -bool DssiSynthIF::putEvent(const MusECore::MidiPlayEvent& ev) +bool DssiSynthIF::putEvent(const MidiPlayEvent& ev) { #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::putEvent midi event time:%d chn:%d a:%d b:%d\n", ev.time(), ev.channel(), ev.dataA(), ev.dataB()); @@ -1830,7 +1942,6 @@ void DssiSynth::incInstances(int val) iIdx.clear(); oIdx.clear(); rpIdx.clear(); - iUsedIdx.clear(); midiCtl2PortMap.clear(); port2MidiCtlMap.clear(); } @@ -1858,10 +1969,10 @@ void DssiSynthIF::guiHeartBeat() _oscif.oscSendProgram(synti->_curProgram, synti->_curBankL); // Update the gui's controls if needed. - unsigned long ports = synth->_controlInPorts; + unsigned long ports = _synth->_controlInPorts; for(unsigned long i = 0; i < ports; ++i) - _oscif.oscSendControl(controls[i].idx, controls[i].val); + _oscif.oscSendControl(_controls[i].idx, _controls[i].val); #endif } @@ -1890,10 +2001,10 @@ int DssiSynthIF::oscUpdate() _oscif.oscSendProgram(synti->_curProgram, synti->_curBankL, true /*force*/); // Send current control values. - unsigned long ports = synth->_controlInPorts; + unsigned long ports = _synth->_controlInPorts; for(unsigned long i = 0; i < ports; ++i) { - _oscif.oscSendControl(controls[i].idx, controls[i].val, true /*force*/); + _oscif.oscSendControl(_controls[i].idx, _controls[i].val, true /*force*/); // Avoid overloading the GUI if there are lots and lots of ports. if((i+1) % 50 == 0) usleep(300000); @@ -1920,7 +2031,7 @@ int DssiSynthIF::oscProgram(unsigned long program, unsigned long bank) if(port != -1) { - MusECore::MidiPlayEvent event(0, port, ch, MusECore::ME_PROGRAM, (bank << 8) + program, 0); + MidiPlayEvent event(0, port, ch, ME_PROGRAM, (bank << 8) + program, 0); #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::oscProgram midi event chn:%d a:%d b:%d\n", event.channel(), event.dataA(), event.dataB()); @@ -1950,14 +2061,14 @@ int DssiSynthIF::oscControl(unsigned long port, float value) printf("DssiSynthIF::oscControl received oscControl port:%lu val:%f\n", port, value); #endif - if(port >= synth->rpIdx.size()) + if(port >= _synth->rpIdx.size()) { - fprintf(stderr, "DssiSynthIF::oscControl: port number:%lu is out of range of index list size:%zd\n", port, synth->rpIdx.size()); + fprintf(stderr, "DssiSynthIF::oscControl: port number:%lu is out of range of index list size:%zd\n", port, _synth->rpIdx.size()); return 0; } // Convert from DSSI port number to control input port index. - unsigned long cport = synth->rpIdx[port]; + unsigned long cport = _synth->rpIdx[port]; if((int)cport == -1) { @@ -1965,16 +2076,28 @@ int DssiSynthIF::oscControl(unsigned long port, float value) return 0; } + // Record automation: + // Take care of this immediately, because we don't want the silly delay associated with + // processing the fifo one-at-a-time in the apply(). + // NOTE: With some vsts we don't receive control events until the user RELEASES a control. + // So the events all arrive at once when the user releases a control. + // That makes this pretty useless... But what the heck... + if(id() != -1) + { + unsigned long pid = genACnum(id(), cport); + synti->recordAutomation(pid, value); + } + // DELETETHIS????: is the below still correct? of so, then keep it of course! // p3.3.39 Set the DSSI control input port's value. // Observations: With a native DSSI synth like LessTrivialSynth, the native GUI's controls do not change the sound at all - // ie. they don't update the DSSI control port values themselves. + // ie. they don't update the DSSI control port values themselves. // Hence in response to the call to this oscControl, sent by the native GUI, it is required to that here. /// controls[cport].val = value; DELETETHIS // DSSI-VST synths however, unlike DSSI synths, DO change their OWN sound in response to their gui controls. - // AND this function is called. - // Despite the descrepency we are STILL required to update the DSSI control port values here - // because dssi-vst is WAITING FOR A RESPONSE. (A CHANGE in the control port value). + // AND this function is called. + // Despite the descrepency we are STILL required to update the DSSI control port values here + // because dssi-vst is WAITING FOR A RESPONSE. (A CHANGE in the control port value). // It will output something like "...4 events expected..." and count that number down as 4 actual control port value CHANGES // are done here in response. Normally it says "...0 events expected..." when MusE is the one doing the DSSI control changes. // @@ -1987,62 +2110,21 @@ int DssiSynthIF::oscControl(unsigned long port, float value) // (Because the server simply ignores the 'expected' messages.) // // Well, at least here are the fifos. Try this ... - // DELETETHIS 20 pretty old as well - /* - OscControlFifo* cfifo = _oscif.oscFifo(cport); - if(cfifo) - { - OscControlValue cv; - //cv.idx = cport; - cv.value = value; - // Time-stamp the event. Looks like no choice but to use the (possibly slow) call to gettimeofday via timestamp(), - // because these are asynchronous events arriving from OSC. timestamp() is more or less an estimate of the - // current frame. (This is exactly how ALSA events are treated when they arrive in our ALSA driver.) p4.0.15 Tim. - cv.frame = MusEGlobal::audio->timestamp(); - if(cfifo->put(cv)) - { - fprintf(stderr, "DssiSynthIF::oscControl: fifo overflow: in control number:%lu\n", cport); - } - } - */ - // p4.0.21 + + // Schedules a timed control change: ControlEvent ce; - ce.unique = synth->_isDssiVst; // Special for messages from vst gui to host - requires processing every message. - ce.fromGui = true; // It came form the plugin's own GUI. + ce.unique = _synth->_isDssiVst; // Special for messages from vst gui to host - requires processing every message. + ce.fromGui = true; // It came from the plugin's own GUI. ce.idx = cport; ce.value = value; - - ce.frame = MusEGlobal::audio->curFrame(); - // don't use timestamp(), because it's circular, which is making it impossible to deal + // Don't use timestamp(), because it's circular, which is making it impossible to deal // with 'modulo' events which slip in 'under the wire' before processing the ring buffers. - - + ce.frame = MusEGlobal::audio->curFrame(); if(_controlFifo.put(ce)) - { fprintf(stderr, "DssiSynthIF::oscControl: fifo overflow: in control number:%lu\n", cport); - } - - // Record automation: - // Take care of this immediately, because we don't want the silly delay associated with - // processing the fifo one-at-a-time in the apply(). - // NOTE: With some vsts we don't receive control events until the user RELEASES a control. - // So the events all arrive at once when the user releases a control. - // That makes this pretty useless... But what the heck... - if(id() != -1) - { - unsigned long pid = genACnum(id(), cport); - AutomationType at = synti->automationType(); + + enableController(cport, false); //TODO maybe re-enable the ctrl soon? - // TODO: Taken from our native gui control handlers. - // This may need modification or may cause problems - - // we don't have the luxury of access to the dssi gui controls ! - if ((at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying())) - enableController(cport, false); //TODO maybe re-enable the ctrl soon? - - synti->recordAutomation(pid, value); - } - return 0; } @@ -2052,8 +2134,8 @@ int DssiSynthIF::oscControl(unsigned long port, float value) int DssiSynthIF::oscMidi(int a, int b, int c) { - if (a == MusECore::ME_NOTEOFF) { - a = MusECore::ME_NOTEON; + if (a == ME_NOTEOFF) { + a = ME_NOTEON; c = 0; } int channel = 0; // TODO: ?? @@ -2061,7 +2143,7 @@ int DssiSynthIF::oscMidi(int a, int b, int c) if(port != -1) { - MusECore::MidiPlayEvent event(0, port, channel, a, b, c); + MidiPlayEvent event(0, port, channel, a, b, c); #ifdef DSSI_DEBUG printf("DssiSynthIF::oscMidi midi event chn:%d a:%d b:%d\n", event.channel(), event.dataA(), event.dataB()); @@ -2104,10 +2186,10 @@ int DssiSynthIF::oscConfigure(const char *key, const char *value) return 0; } - if (!synth->dssi->configure) + if (!_synth->dssi->configure) return 0; - char* message = synth->dssi->configure(handle, key, value); + char* message = _synth->dssi->configure(_handle, key, value); if (message) { printf("MusE: on configure '%s' '%s', plugin '%s' returned error '%s'\n", key, value, synti->name().toLatin1().constData(), message); @@ -2141,11 +2223,11 @@ void DssiSynthIF::queryPrograms() } programs.clear(); - if (!synth->dssi->get_program) + if (!_synth->dssi->get_program) return; for (int i = 0;; ++i) { - const DSSI_Program_Descriptor* pd = synth->dssi->get_program(handle, i); + const DSSI_Program_Descriptor* pd = _synth->dssi->get_program(_handle, i); if (pd == 0) break; DSSI_Program_Descriptor d; @@ -2158,7 +2240,7 @@ void DssiSynthIF::queryPrograms() void DssiSynthIF::doSelectProgram(LADSPA_Handle handle, int bank, int prog) { - const DSSI_Descriptor* dssi = synth->dssi; + const DSSI_Descriptor* dssi = _synth->dssi; dssi->select_program(handle, bank, prog); // Need to update the automation value, otherwise it overwrites later with the last automation value. @@ -2167,10 +2249,10 @@ void DssiSynthIF::doSelectProgram(LADSPA_Handle handle, int bank, int prog) // (This is the only circumstance in which a DSSI plugin is allowed to modify its own input ports.)" From dssi.h if(id() != -1) { - for(unsigned long k = 0; k < synth->_controlInPorts; ++k) + for(unsigned long k = 0; k < _synth->_controlInPorts; ++k) { // We're in the audio thread context: no need to send a message, just modify directly. - synti->setPluginCtrlVal(genACnum(id(), k), controls[k].val); + synti->setPluginCtrlVal(genACnum(id(), k), _controls[k].val); } } } @@ -2224,7 +2306,7 @@ void DssiSynthIF::populatePatchPopup(MusEGui::PopupMenu* menu, int /*ch*/, bool int DssiSynthIF::getControllerInfo(int id, const char** name, int* ctrl, int* min, int* max, int* initval) { - int controlPorts = synth->_controlInPorts; + int controlPorts = _synth->_controlInPorts; if(id == controlPorts || id == controlPorts + 1) { // @@ -2232,22 +2314,22 @@ int DssiSynthIF::getControllerInfo(int id, const char** name, int* ctrl, int* mi // (channel and key pressure) midi messages, so add support for them now (as controllers). // if(id == controlPorts) - *ctrl = MusECore::CTRL_POLYAFTER; + *ctrl = CTRL_POLYAFTER; else if(id == controlPorts + 1) - *ctrl = MusECore::CTRL_AFTERTOUCH; + *ctrl = CTRL_AFTERTOUCH; *min = 0; *max = 127; - *initval = MusECore::CTRL_VAL_UNKNOWN; + *initval = CTRL_VAL_UNKNOWN; *name = midiCtrlName(*ctrl).toLatin1().constData(); return ++id; } else if(id >= controlPorts + 2) return 0; - const DSSI_Descriptor* dssi = synth->dssi; + const DSSI_Descriptor* dssi = _synth->dssi; const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; - unsigned long i = controls[id].idx; + unsigned long i = _controls[id].idx; #ifdef DSSI_DEBUG printf("DssiSynthIF::getControllerInfo control port:%d port idx:%lu name:%s\n", id, i, ld->PortNames[i]); @@ -2255,14 +2337,14 @@ int DssiSynthIF::getControllerInfo(int id, const char** name, int* ctrl, int* mi int ctlnum = DSSI_NONE; if(dssi->get_midi_controller_for_port) - ctlnum = dssi->get_midi_controller_for_port(handle, i); + ctlnum = dssi->get_midi_controller_for_port(_handle, i); // No controller number? Give it one. if(ctlnum == DSSI_NONE) { // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. - ctlnum = MusECore::CTRL_NRPN14_OFFSET + 0x2000 + id; + ctlnum = CTRL_NRPN14_OFFSET + 0x2000 + id; } else { @@ -2292,15 +2374,15 @@ int DssiSynthIF::getControllerInfo(int id, const char** name, int* ctrl, int* mi printf("DssiSynthIF::getControllerInfo is NRPN control\n"); #endif - ctlnum = DSSI_NRPN_NUMBER(c) + MusECore::CTRL_NRPN14_OFFSET; + ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; } } - int def = MusECore::CTRL_VAL_UNKNOWN; + int def = CTRL_VAL_UNKNOWN; if(ladspa2MidiControlValues(ld, i, ctlnum, min, max, &def)) *initval = def; else - *initval = MusECore::CTRL_VAL_UNKNOWN; + *initval = CTRL_VAL_UNKNOWN; #ifdef DSSI_DEBUG printf("DssiSynthIF::getControllerInfo passed ctlnum:%d min:%d max:%d initval:%d\n", ctlnum, *min, *max, *initval); @@ -2313,17 +2395,17 @@ int DssiSynthIF::getControllerInfo(int id, const char** name, int* ctrl, int* mi int DssiSynthIF::channels() const { - return ((int)synth->_outports) > MAX_CHANNELS ? MAX_CHANNELS : ((int)synth->_outports) ; + return ((int)_synth->_outports) > MAX_CHANNELS ? MAX_CHANNELS : ((int)_synth->_outports) ; } int DssiSynthIF::totalOutChannels() const { - return synth->_outports; + return _synth->_outports; } int DssiSynthIF::totalInChannels() const { - return synth->_inports; + return _synth->_inports; } void DssiSynthIF::deactivate3() @@ -2336,42 +2418,28 @@ void DssiSynthIF::deactivate3() // Methods for PluginIBase: //-------------------------------- -//bool DssiSynthIF::on() const { return true; } // Synth is not part of a rack plugin chain. Always on. -//void DssiSynthIF::setOn(bool /*val*/) { } -unsigned long DssiSynthIF::pluginID() { return (synth && synth->dssi) ? synth->dssi->LADSPA_Plugin->UniqueID : 0; } +unsigned long DssiSynthIF::pluginID() { return (_synth && _synth->dssi) ? _synth->dssi->LADSPA_Plugin->UniqueID : 0; } int DssiSynthIF::id() { return MAX_PLUGINS; } // Set for special block reserved for dssi synth. p4.0.20 -QString DssiSynthIF::pluginLabel() const { return (synth && synth->dssi) ? QString(synth->dssi->LADSPA_Plugin->Label) : QString(); } -//QString DssiSynthIF::name() const { return synti->name(); } -QString DssiSynthIF::lib() const { return synth ? synth->completeBaseName() : QString(); } -QString DssiSynthIF::dirPath() const { return synth ? synth->absolutePath() : QString(); } -QString DssiSynthIF::fileName() const { return synth ? synth->fileName() : QString(); } -//QString DssiSynthIF::titlePrefix() const { return QString(); } -//MusECore::AudioTrack* DssiSynthIF::track() { return (MusECore::AudioTrack*)synti; } -void DssiSynthIF::enableController(unsigned long i, bool v) { controls[i].enCtrl = v; } -bool DssiSynthIF::controllerEnabled(unsigned long i) const { return controls[i].enCtrl; } -void DssiSynthIF::enable2Controller(unsigned long i, bool v) { controls[i].en2Ctrl = v; } -bool DssiSynthIF::controllerEnabled2(unsigned long i) const { return controls[i].en2Ctrl; } +QString DssiSynthIF::pluginLabel() const { return (_synth && _synth->dssi) ? QString(_synth->dssi->LADSPA_Plugin->Label) : QString(); } +QString DssiSynthIF::lib() const { return _synth ? _synth->completeBaseName() : QString(); } +QString DssiSynthIF::dirPath() const { return _synth ? _synth->absolutePath() : QString(); } +QString DssiSynthIF::fileName() const { return _synth ? _synth->fileName() : QString(); } +void DssiSynthIF::enableController(unsigned long i, bool v) { _controls[i].enCtrl = v; } +bool DssiSynthIF::controllerEnabled(unsigned long i) const { return _controls[i].enCtrl; } void DssiSynthIF::enableAllControllers(bool v) { - if(!synth) + if(!_synth) return; - for(unsigned long i = 0; i < synth->_controlInPorts; ++i) - controls[i].enCtrl = v; -} -void DssiSynthIF::enable2AllControllers(bool v) -{ - if(!synth) - return; - for(unsigned long i = 0; i < synth->_controlInPorts; ++i) - controls[i].en2Ctrl = v; + for(unsigned long i = 0; i < _synth->_controlInPorts; ++i) + _controls[i].enCtrl = v; } void DssiSynthIF::updateControllers() { } void DssiSynthIF::activate() { - if(synth && synth->dssi && synth->dssi->LADSPA_Plugin && synth->dssi->LADSPA_Plugin->activate) + if(_synth && _synth->dssi && _synth->dssi->LADSPA_Plugin && _synth->dssi->LADSPA_Plugin->activate) //for (int i = 0; i < instances; ++i) // _plugin->activate(handle[i]); - synth->dssi->LADSPA_Plugin->activate(handle); + _synth->dssi->LADSPA_Plugin->activate(_handle); // REMOVE Tim. Or keep? From PluginI::activate(). // if (initControlValues) { @@ -2388,27 +2456,24 @@ void DssiSynthIF::activate() } void DssiSynthIF::deactivate() { - if(!synth || !synth->dssi || !synth->dssi->LADSPA_Plugin ||!synth->dssi->LADSPA_Plugin->deactivate) + if(!_synth || !_synth->dssi || !_synth->dssi->LADSPA_Plugin ||!_synth->dssi->LADSPA_Plugin->deactivate) return; //for (int i = 0; i < instances; ++i) // synth->dssi->LADSPA_Plugin->deactivate(handle[i]); - synth->dssi->LADSPA_Plugin->deactivate(handle); + _synth->dssi->LADSPA_Plugin->deactivate(_handle); } -//void DssiSynthIF::writeConfiguration(int /*level*/, Xml& /*xml*/) { } -//bool DssiSynthIF::readConfiguration(Xml& /*xml*/, bool /*readPreset*/) { return false; } - -unsigned long DssiSynthIF::parameters() const { return synth ? synth->_controlInPorts : 0; } -unsigned long DssiSynthIF::parametersOut() const { return synth ? synth->_controlOutPorts : 0; } +unsigned long DssiSynthIF::parameters() const { return _synth ? _synth->_controlInPorts : 0; } +unsigned long DssiSynthIF::parametersOut() const { return _synth ? _synth->_controlOutPorts : 0; } void DssiSynthIF::setParam(unsigned long i, float val) { setParameter(i, val); } float DssiSynthIF::param(unsigned long i) const { return getParameter(i); } float DssiSynthIF::paramOut(unsigned long i) const { return getParameterOut(i); } -const char* DssiSynthIF::paramName(unsigned long i) { return (synth && synth->dssi) ? synth->dssi->LADSPA_Plugin->PortNames[controls[i].idx] : 0; } -const char* DssiSynthIF::paramOutName(unsigned long i) { return (synth && synth->dssi) ? synth->dssi->LADSPA_Plugin->PortNames[controlsOut[i].idx] : 0; } -LADSPA_PortRangeHint DssiSynthIF::range(unsigned long i) { return synth->dssi->LADSPA_Plugin->PortRangeHints[controls[i].idx]; } -LADSPA_PortRangeHint DssiSynthIF::rangeOut(unsigned long i) { return synth->dssi->LADSPA_Plugin->PortRangeHints[controlsOut[i].idx]; } -CtrlValueType DssiSynthIF::ctrlValueType(unsigned long i) const { return ladspaCtrlValueType(synth->dssi->LADSPA_Plugin, controls[i].idx); } -CtrlList::Mode DssiSynthIF::ctrlMode(unsigned long i) const { return ladspaCtrlMode(synth->dssi->LADSPA_Plugin, controls[i].idx); }; +const char* DssiSynthIF::paramName(unsigned long i) { return (_synth && _synth->dssi) ? _synth->dssi->LADSPA_Plugin->PortNames[_controls[i].idx] : 0; } +const char* DssiSynthIF::paramOutName(unsigned long i) { return (_synth && _synth->dssi) ? _synth->dssi->LADSPA_Plugin->PortNames[_controlsOut[i].idx] : 0; } +LADSPA_PortRangeHint DssiSynthIF::range(unsigned long i) { return _synth->dssi->LADSPA_Plugin->PortRangeHints[_controls[i].idx]; } +LADSPA_PortRangeHint DssiSynthIF::rangeOut(unsigned long i) { return _synth->dssi->LADSPA_Plugin->PortRangeHints[_controlsOut[i].idx]; } +CtrlValueType DssiSynthIF::ctrlValueType(unsigned long i) const { return ladspaCtrlValueType(_synth->dssi->LADSPA_Plugin, _controls[i].idx); } +CtrlList::Mode DssiSynthIF::ctrlMode(unsigned long i) const { return ladspaCtrlMode(_synth->dssi->LADSPA_Plugin, _controls[i].idx); }; } // namespace MusECore diff --git a/muse2/muse/dssihost.h b/muse2/muse/dssihost.h index 27ef05a3..13d604a9 100644 --- a/muse2/muse/dssihost.h +++ b/muse2/muse/dssihost.h @@ -4,7 +4,7 @@ // $Id: dssihost.h,v 1.10.2.7 2009/12/06 10:05:00 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2013 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License @@ -83,10 +83,9 @@ class DssiSynth : public Synth { unsigned long _portCount, _inports, _outports, _controlInPorts, _controlOutPorts; std::vector<unsigned long> iIdx; // Audio input index to port number. std::vector<unsigned long> oIdx; // Audio output index to port number. - std::vector<int> iUsedIdx; // During process, tells whether an audio input port was used by any input routes. std::vector<unsigned long> rpIdx; // Port number to control input index. Item is -1 if it's not a control input. - MusECore::MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to DSSI port numbers. - MusECore::MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps DSSI port numbers to midi controller numbers. + MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to DSSI port numbers. + MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps DSSI port numbers to midi controller numbers. bool _hasGui; bool _inPlaceCapable; // Hack: Special flag required. @@ -116,11 +115,11 @@ class DssiSynth : public Synth { class DssiSynthIF : public SynthIF { - DssiSynth* synth; - LADSPA_Handle handle; + DssiSynth* _synth; + LADSPA_Handle _handle; - Port* controls; - Port* controlsOut; + Port* _controls; + Port* _controlsOut; #ifdef OSC_SUPPORT OscDssiIF _oscif; @@ -129,11 +128,12 @@ class DssiSynthIF : public SynthIF std::vector<DSSI_Program_Descriptor> programs; void queryPrograms(); void doSelectProgram(LADSPA_Handle handle, int bank, int prog); - bool processEvent(const MusECore::MidiPlayEvent&, snd_seq_event_t*); + bool processEvent(const MidiPlayEvent&, snd_seq_event_t*); - float** audioInBuffers; - float** audioOutBuffers; - float* audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence. + float** _audioInBuffers; + float** _audioOutBuffers; + float* _audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence. + std::vector<unsigned long> _iUsedIdx; // During process, tells whether an audio input port was used by any input routes. public: DssiSynthIF(SynthI* s); @@ -145,7 +145,7 @@ class DssiSynthIF : public SynthIF bool init(DssiSynth* s); - virtual DssiSynth* dssiSynth() { return synth; } + virtual DssiSynth* dssiSynth() { return _synth; } virtual SynthI* dssiSynthI() { return synti; } virtual bool initGui(); @@ -156,15 +156,15 @@ class DssiSynthIF : public SynthIF virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool); virtual bool hasNativeGui() const { return !dssi_ui_filename().isEmpty(); } - virtual void getGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; } - virtual void setGeometry(int, int, int, int) {} + virtual void getGeometry(int*x, int*y, int*w, int*h) const; + virtual void setGeometry(int, int, int, int); virtual void getNativeGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; } virtual void setNativeGeometry(int, int, int, int) {} virtual void preProcessAlways(); - virtual MusECore::iMPEvent getData(MusECore::MidiPort*, MusECore::MPEventList*, MusECore::iMPEvent, unsigned pos, int ports, unsigned n, float** buffer); - virtual bool putEvent(const MusECore::MidiPlayEvent& ev); - virtual MusECore::MidiPlayEvent receiveEvent(); + virtual iMPEvent getData(MidiPort*, MPEventList*, iMPEvent, unsigned pos, int ports, unsigned n, float** buffer); + virtual bool putEvent(const MidiPlayEvent& ev); + virtual MidiPlayEvent receiveEvent(); virtual int eventsPending() const { return 0; } virtual int channels() const; @@ -204,10 +204,7 @@ class DssiSynthIF : public SynthIF QString fileName() const; void enableController(unsigned long i, bool v = true); bool controllerEnabled(unsigned long i) const; - void enable2Controller(unsigned long i, bool v = true); - bool controllerEnabled2(unsigned long i) const; void enableAllControllers(bool v = true); - void enable2AllControllers(bool v = true); void updateControllers(); void activate(); void deactivate(); diff --git a/muse2/muse/master/masteredit.cpp b/muse2/muse/master/masteredit.cpp index 90675d25..a134ef14 100644 --- a/muse2/muse/master/masteredit.cpp +++ b/muse2/muse/master/masteredit.cpp @@ -285,13 +285,14 @@ void MasterEdit::readStatus(MusECore::Xml& xml) case MusECore::Xml::TagStart: if (tag == "midieditor") MidiEditor::readStatus(xml); + else if (tag == "xpos") + hscroll->setPos(xml.parseInt()); + else if (tag == "xmag") + hscroll->setMag(xml.parseInt()); else if (tag == "ypos") vscroll->setPos(xml.parseInt()); - else if (tag == "ymag") { - // vscroll->setMag(xml.parseInt()); - int mag = xml.parseInt(); - vscroll->setMag(mag); - } + else if (tag == "ymag") + vscroll->setMag(xml.parseInt()); else xml.unknown("MasterEdit"); break; @@ -324,8 +325,10 @@ void MasterEdit::readStatus(MusECore::Xml& xml) void MasterEdit::writeStatus(int level, MusECore::Xml& xml) const { xml.tag(level++, "master"); - xml.intTag(level, "ypos", vscroll->pos()); + xml.intTag(level, "xmag", hscroll->mag()); + xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "ymag", vscroll->mag()); + xml.intTag(level, "ypos", vscroll->pos()); MidiEditor::writeStatus(level, xml); xml.tag(level, "/master"); } diff --git a/muse2/muse/midi.cpp b/muse2/muse/midi.cpp index 8f5ed22c..524329b1 100644 --- a/muse2/muse/midi.cpp +++ b/muse2/muse/midi.cpp @@ -819,7 +819,51 @@ void Audio::processMidi() while (s->eventsPending()) { MusECore::MidiRecordEvent ev = s->receiveEvent(); - md->recordEvent(ev); + // FIXME: This is for recording the events sent by GUI. + // It never gets a chance to be processed since reading of + // record FIFOs is done only by connected input ROUTES, below. + // To be useful, the synth itself must be allowed to be chosen + // as an input route, which is simple enough, but we currently don't + // list synths as inputs for fear of too many INCOMPATIBLE messages + // from DIFFERING synths. However, we could allow ONLY THIS synth + // to be listed and therefore be automatically connected too, if desired. + //md->recordEvent(ev); + // + // For now, instead of recording, here is the minimum that we must do: + // + // Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values. + // Same code as in MidiPort::sendEvent() + if(md->midiPort() != -1) + { + MidiPort* mp = &MusEGlobal::midiPorts[md->midiPort()]; + if(ev.type() == ME_CONTROLLER) + { + int da = ev.dataA(); + int db = ev.dataB(); + db = mp->limitValToInstrCtlRange(da, db); + mp->setHwCtrlState(ev.channel(), da, db); + } + else if(ev.type() == ME_PITCHBEND) + { + int da = mp->limitValToInstrCtlRange(CTRL_PITCH, ev.dataA()); + mp->setHwCtrlState(ev.channel(), CTRL_PITCH, da); + } + else if(ev.type() == ME_AFTERTOUCH) + { + int da = mp->limitValToInstrCtlRange(CTRL_AFTERTOUCH, ev.dataA()); + mp->setHwCtrlState(ev.channel(), CTRL_AFTERTOUCH, da); + } + else if(ev.type() == ME_POLYAFTER) + { + int ctl = (CTRL_POLYAFTER & ~0xff) | (ev.dataA() & 0x7f); + int db = mp->limitValToInstrCtlRange(ctl, ev.dataB()); + mp->setHwCtrlState(ev.channel(), ctl , db); + } + else if(ev.type() == ME_PROGRAM) + { + mp->setHwCtrlState(ev.channel(), CTRL_PROGRAM, ev.dataA()); + } + } } } @@ -921,8 +965,8 @@ void Audio::processMidi() // Unlike our built-in gui controls, there is not much choice here but to // just do this: if ( (at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) - //if(isPlaying() && (at == AUTO_WRITE || at == AUTO_TOUCH)) DELETETHIS + (at == AUTO_READ && !MusEGlobal::audio->isPlaying()) || + (at == AUTO_TOUCH) ) track->enableController(actrl, false); if(isPlaying()) { diff --git a/muse2/muse/midiedit/dcanvas.cpp b/muse2/muse/midiedit/dcanvas.cpp index ff30eb64..734871ef 100644 --- a/muse2/muse/midiedit/dcanvas.cpp +++ b/muse2/muse/midiedit/dcanvas.cpp @@ -1003,18 +1003,8 @@ void DrumCanvas::keyReleased(int, bool) { // release note: if(_playEvents) - { - // REMOVE Tim. - //int pitch, port, channel; - //if(index2Note(index, &port, &channel, &pitch)) - //{ - // MusECore::MidiPlayEvent e(0, port, channel, 0x90, pitch, 0); - // MusEGlobal::audio->msgPlayMidiEvent(&e); - //} stopPlayEvent(); } - //playedPitch=-1; - } //--------------------------------------------------------- // mapChanged diff --git a/muse2/muse/midiedit/drumedit.cpp b/muse2/muse/midiedit/drumedit.cpp index 3ddf6f7b..e2ba0ef1 100644 --- a/muse2/muse/midiedit/drumedit.cpp +++ b/muse2/muse/midiedit/drumedit.cpp @@ -986,10 +986,10 @@ void DrumEdit::writeStatus(int level, MusECore::Xml& xml) const xml.intTag(level, "midiin", canvas->midiin()); xml.intTag(level, "tool", int(canvas->tool())); xml.intTag(level, "playEvents", _playEvents); - xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "xmag", hscroll->mag()); - xml.intTag(level, "ypos", vscroll->pos()); + xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "ymag", vscroll->mag()); + xml.intTag(level, "ypos", vscroll->pos()); xml.intTag(level, "ignore_hide", _ignore_hide); xml.tag(level, "/drumedit"); } diff --git a/muse2/muse/midiedit/ecanvas.cpp b/muse2/muse/midiedit/ecanvas.cpp index 492727cb..4ae702f1 100644 --- a/muse2/muse/midiedit/ecanvas.cpp +++ b/muse2/muse/midiedit/ecanvas.cpp @@ -527,7 +527,6 @@ void EventCanvas::stopPlayEvent() if(playedPitch == -1 || playedPitchPort == -1 || playedPitchChannel == -1) return; // release note: - //MusECore::MidiPlayEvent ev(0, playedPitchPort, playedPitchChannel, 0x90, playedPitch, 0); // REMOVE Tim. MusECore::MidiPlayEvent ev(0, playedPitchPort, playedPitchChannel, MusECore::ME_NOTEOFF, playedPitch, playedVelocity); MusEGlobal::audio->msgPlayMidiEvent(&ev); playedPitch = playedPitchPort = playedPitchChannel = -1; diff --git a/muse2/muse/midiedit/pianoroll.cpp b/muse2/muse/midiedit/pianoroll.cpp index 66e6c2fb..d0cb3f2a 100644 --- a/muse2/muse/midiedit/pianoroll.cpp +++ b/muse2/muse/midiedit/pianoroll.cpp @@ -1067,10 +1067,10 @@ void PianoRoll::writeStatus(int level, MusECore::Xml& xml) const xml.intTag(level, "midiin", canvas->midiin()); xml.intTag(level, "tool", int(canvas->tool())); xml.intTag(level, "playEvents", _playEvents); - xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "xmag", hscroll->mag()); - xml.intTag(level, "ypos", vscroll->pos()); + xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "ymag", vscroll->mag()); + xml.intTag(level, "ypos", vscroll->pos()); xml.tag(level, "/pianoroll"); } diff --git a/muse2/muse/mixer/astrip.cpp b/muse2/muse/mixer/astrip.cpp index 32504cc8..9381e3ec 100644 --- a/muse2/muse/mixer/astrip.cpp +++ b/muse2/muse/mixer/astrip.cpp @@ -4,7 +4,7 @@ // $Id: astrip.cpp,v 1.23.2.17 2009/11/16 01:55:55 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2013 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -69,6 +69,7 @@ #include "menutitleitem.h" //#include "popupmenu.h" #include "routepopup.h" +#include "ctrl.h" namespace MusEGui { @@ -305,20 +306,21 @@ void AudioStrip::songChanged(MusECore::SongChangedFlags_t val) void AudioStrip::updateVolume() { + if(_volPressed) // Inhibit the controller stream if control is currently pressed. + return; double vol = ((MusECore::AudioTrack*)track)->volume(); - if (vol != volume) - { - //printf("AudioStrip::updateVolume setting slider and label\n"); - - slider->blockSignals(true); - sl->blockSignals(true); - double val = MusECore::fast_log10(vol) * 20.0; - slider->setValue(val); - sl->setValue(val); - sl->blockSignals(false); - slider->blockSignals(false); - volume = vol; - } + if (vol != volume) + { + //printf("AudioStrip::updateVolume setting slider and label\n"); + slider->blockSignals(true); + sl->blockSignals(true); + double val = MusECore::fast_log10(vol) * 20.0; + slider->setValue(val); + sl->setValue(val); + sl->blockSignals(false); + slider->blockSignals(false); + volume = vol; + } } //--------------------------------------------------------- @@ -327,20 +329,21 @@ void AudioStrip::updateVolume() void AudioStrip::updatePan() { + if(_panPressed) // Inhibit the controller stream if control is currently pressed. + return; double v = ((MusECore::AudioTrack*)track)->pan(); - if (v != panVal) - { - //printf("AudioStrip::updatePan setting slider and label\n"); - - pan->blockSignals(true); - panl->blockSignals(true); - pan->setValue(v); - panl->setValue(v); - panl->blockSignals(false); - pan->blockSignals(false); - panVal = v; - } -} + if (v != panVal) + { + //printf("AudioStrip::updatePan setting slider and label\n"); + pan->blockSignals(true); + panl->blockSignals(true); + pan->setValue(v); + panl->setValue(v); + panl->blockSignals(false); + pan->blockSignals(false); + panVal = v; + } +} //--------------------------------------------------------- // offToggled @@ -469,11 +472,9 @@ void AudioStrip::auxLabelChanged(double val, unsigned int idx) void AudioStrip::volumeChanged(double val, int, bool shift_pressed) { - AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if ( (at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) - track->enableVolumeController(false); - + if(track->isMidiTrack()) + return; + MusECore::AudioTrack* t = static_cast<MusECore::AudioTrack*>(track); double vol; if (val <= MusEGlobal::config.minSlider) { vol = 0.0; @@ -482,10 +483,9 @@ void AudioStrip::volumeChanged(double val, int, bool shift_pressed) else vol = pow(10.0, val/20.0); volume = vol; - //MusEGlobal::audio->msgSetVolume((MusECore::AudioTrack*)track, vol); - // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to? - ((MusECore::AudioTrack*)track)->setVolume(vol); - if (!shift_pressed) ((MusECore::AudioTrack*)track)->recordAutomation(MusECore::AC_VOLUME, vol); + if (!shift_pressed) t->recordAutomation(MusECore::AC_VOLUME, vol); // with shift, we get straight lines :) + t->setParam(MusECore::AC_VOLUME, vol); // Schedules a timed control change. + t->enableController(MusECore::AC_VOLUME, false); } //--------------------------------------------------------- @@ -494,10 +494,10 @@ void AudioStrip::volumeChanged(double val, int, bool shift_pressed) void AudioStrip::volumePressed() { - AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE) - track->enableVolumeController(false); - + if(track->isMidiTrack()) + return; + _volPressed = true; + MusECore::AudioTrack* t = static_cast<MusECore::AudioTrack*>(track); double val = slider->value(); double vol; if (val <= MusEGlobal::config.minSlider) { @@ -507,10 +507,9 @@ void AudioStrip::volumePressed() else vol = pow(10.0, val/20.0); volume = vol; - //MusEGlobal::audio->msgSetVolume((MusECore::AudioTrack*)track, volume); - // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to? - ((MusECore::AudioTrack*)track)->setVolume(volume); - ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_VOLUME, volume); + t->startAutoRecord(MusECore::AC_VOLUME, vol); + t->setVolume(vol); + t->enableController(MusECore::AC_VOLUME, false); } //--------------------------------------------------------- @@ -519,11 +518,15 @@ void AudioStrip::volumePressed() void AudioStrip::volumeReleased() { - AutomationType at = track->automationType(); - if (at == AUTO_OFF || at == AUTO_READ || at == AUTO_TOUCH) - track->enableVolumeController(true); - - ((MusECore::AudioTrack*)track)->stopAutoRecord(MusECore::AC_VOLUME, volume); + if(track->isMidiTrack()) + return; + MusECore::AudioTrack* t = static_cast<MusECore::AudioTrack*>(track); + AutomationType at = t->automationType(); + t->stopAutoRecord(MusECore::AC_VOLUME, volume); + if(at == AUTO_OFF || + at == AUTO_TOUCH) + t->enableController(MusECore::AC_VOLUME, true); + _volPressed = false; } //--------------------------------------------------------- @@ -540,11 +543,9 @@ void AudioStrip::volumeRightClicked(const QPoint &p) void AudioStrip::volLabelChanged(double val) { - AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if ( (at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) - track->enableVolumeController(false); - + if(track->isMidiTrack()) + return; + MusECore::AudioTrack* t = static_cast<MusECore::AudioTrack*>(track); double vol; if (val <= MusEGlobal::config.minSlider) { vol = 0.0; @@ -553,11 +554,12 @@ void AudioStrip::volLabelChanged(double val) else vol = pow(10.0, val/20.0); volume = vol; - slider->setValue(val); - //audio->msgSetVolume((MusECore::AudioTrack*)track, vol); - // p4.0.21 audio->msgXXX waits. Do we really need to? - ((MusECore::AudioTrack*)track)->setVolume(vol); - ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_VOLUME, vol); + slider->blockSignals(true); + slider->setValue(val); + slider->blockSignals(false); + t->startAutoRecord(MusECore::AC_VOLUME, vol); + t->setParam(MusECore::AC_VOLUME, vol); // Schedules a timed control change. + t->enableController(MusECore::AC_VOLUME, false); } //--------------------------------------------------------- @@ -566,16 +568,13 @@ void AudioStrip::volLabelChanged(double val) void AudioStrip::panChanged(double val, int, bool shift_pressed) { - AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if ( (at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) - track->enablePanController(false); - - panVal = val; - //MusEGlobal::audio->msgSetPan(((MusECore::AudioTrack*)track), val); - // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to? - ((MusECore::AudioTrack*)track)->setPan(val); - if (!shift_pressed) ((MusECore::AudioTrack*)track)->recordAutomation(MusECore::AC_PAN, val); + if(track->isMidiTrack()) + return; + MusECore::AudioTrack* t = static_cast<MusECore::AudioTrack*>(track); + panVal = val; + if (!shift_pressed) t->recordAutomation(MusECore::AC_PAN, val); // with shift, we get straight lines :) + t->setParam(MusECore::AC_PAN, val); // Schedules a timed control change. + t->enableController(MusECore::AC_PAN, false); } //--------------------------------------------------------- @@ -584,15 +583,14 @@ void AudioStrip::panChanged(double val, int, bool shift_pressed) void AudioStrip::panPressed() { - AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE) - track->enablePanController(false); - - panVal = pan->value(); - //MusEGlobal::audio->msgSetPan(((MusECore::AudioTrack*)track), panVal); - // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to? - ((MusECore::AudioTrack*)track)->setPan(panVal); - ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_PAN, panVal); + if(track->isMidiTrack()) + return; + _panPressed = true; + MusECore::AudioTrack* t = static_cast<MusECore::AudioTrack*>(track); + panVal = pan->value(); + t->startAutoRecord(MusECore::AC_PAN, panVal); + t->setPan(panVal); + t->enableController(MusECore::AC_PAN, false); } //--------------------------------------------------------- @@ -601,10 +599,15 @@ void AudioStrip::panPressed() void AudioStrip::panReleased() { - AutomationType at = track->automationType(); - if (at == AUTO_OFF || at == AUTO_READ || at == AUTO_TOUCH) - track->enablePanController(true); - ((MusECore::AudioTrack*)track)->stopAutoRecord(MusECore::AC_PAN, panVal); + if(track->isMidiTrack()) + return; + MusECore::AudioTrack* t = static_cast<MusECore::AudioTrack*>(track); + AutomationType at = t->automationType(); + t->stopAutoRecord(MusECore::AC_PAN, panVal); + if(at == AUTO_OFF || + at == AUTO_TOUCH) + t->enableController(MusECore::AC_PAN, true); + _panPressed = false; } //--------------------------------------------------------- @@ -621,17 +624,16 @@ void AudioStrip::panRightClicked(const QPoint &p) void AudioStrip::panLabelChanged(double val) { - AutomationType at = ((MusECore::AudioTrack*)track)->automationType(); - if ( (at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) - track->enablePanController(false); - + if(track->isMidiTrack()) + return; + MusECore::AudioTrack* t = static_cast<MusECore::AudioTrack*>(track); panVal = val; + pan->blockSignals(true); pan->setValue(val); - //MusEGlobal::audio->msgSetPan((MusECore::AudioTrack*)track, val); - // p4.0.21 MusEGlobal::audio->msgXXX waits. Do we really need to? - ((MusECore::AudioTrack*)track)->setPan(val); - ((MusECore::AudioTrack*)track)->startAutoRecord(MusECore::AC_PAN, val); + pan->blockSignals(false); + t->startAutoRecord(MusECore::AC_PAN, val); + t->setParam(MusECore::AC_PAN, val); // Schedules a timed control change. + t->enableController(MusECore::AC_PAN, false); } //--------------------------------------------------------- @@ -772,6 +774,8 @@ AudioStrip::AudioStrip(QWidget* parent, MusECore::AudioTrack* at) { volume = -1.0; panVal = 0; + _volPressed = false; + _panPressed = false; record = 0; off = 0; diff --git a/muse2/muse/mixer/astrip.h b/muse2/muse/mixer/astrip.h index 7088301f..3b32ec07 100644 --- a/muse2/muse/mixer/astrip.h +++ b/muse2/muse/mixer/astrip.h @@ -4,7 +4,7 @@ // $Id: astrip.h,v 1.8.2.6 2009/11/14 03:37:48 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2013 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -82,6 +82,9 @@ class AudioStrip : public Strip { double volume; double panVal; + bool _volPressed; + bool _panPressed; + //QToolButton* iR; //QToolButton* oR; diff --git a/muse2/muse/node.cpp b/muse2/muse/node.cpp index 9ecd8a0a..73495d8f 100644 --- a/muse2/muse/node.cpp +++ b/muse2/muse/node.cpp @@ -4,7 +4,7 @@ // $Id: node.cpp,v 1.36.2.25 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2013 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -364,13 +364,414 @@ void Track::setOff(bool val) _off = val; } +//--------------------------------------------------------- +// applyTrackCtrls +// If trackChans is 0, just process controllers only, not audio (do not 'run'). +//--------------------------------------------------------- + +void AudioTrack::processTrackCtrls(unsigned pos, int trackChans, unsigned nframes, float** buffer) +{ + const unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); + const unsigned long min_per = (MusEGlobal::config.minControlProcessPeriod > nframes) ? nframes : MusEGlobal::config.minControlProcessPeriod; + unsigned long sample = 0; + + const AutomationType at = automationType(); + const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; + CtrlListList* cll = controller(); + CtrlList* vol_ctrl = 0; + CtrlList* pan_ctrl = 0; + { + ciCtrlList icl = cll->find(AC_VOLUME); + if(icl == cll->end()) + return; + vol_ctrl = icl->second; + icl = cll->find(AC_PAN); + if(icl == cll->end()) + return; + pan_ctrl = icl->second; + } + + int cur_slice = 0; + while(sample < nframes) + { + unsigned long nsamp = nframes - sample; + const unsigned long slice_frame = pos + sample; + + // Process automation control values, while also determining the maximum acceptable + // size of this run. Further processing, from FIFOs for example, can lower the size + // from there, but this section determines where the next highest maximum frame + // absolutely needs to be for smooth playback of the controller value stream... + // + if(trackChans != 0 && !_prefader) // Don't bother if we don't want to run, or prefader is on. + { + ciCtrlList icl = cll->begin(); + for(unsigned long k = 0; k < _controlPorts; ++k) + { + CtrlList* cl = (icl != cll->end() ? icl->second : NULL); + CtrlInterpolate& ci = _controls[k].interp; + // Always refresh the interpolate struct at first, since things may have changed. + // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. + if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && + (slice_frame < (unsigned long)ci.sFrame || (ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame)) ) ) + { + if(cl && (unsigned long)cl->id() == k) + { + cl->getInterpolation(slice_frame, no_auto || !_controls[k].enCtrl, &ci); + if(icl != cll->end()) + ++icl; + } + else + { + // No matching controller, or end. Just copy the current value into the interpolator. + // Keep the current icl iterator, because since they are sorted by frames, + // if the IDs didn't match it means we can just let k catch up with icl. + ci.sFrame = 0; + ci.eFrame = -1; + ci.sVal = _controls[k].val; + ci.eVal = ci.sVal; + ci.doInterp = false; + ci.eStop = false; + } + } + else + { + if(ci.eStop && ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. + { + // Clear the stop condition and set up the interp struct appropriately as an endless value. + ci.sFrame = 0; //ci->eFrame; + ci.eFrame = -1; + ci.sVal = ci.eVal; + ci.doInterp = false; + ci.eStop = false; + } + if(icl != cll->end()) + ++icl; + } + + if(MusEGlobal::audio->isPlaying()) + { + unsigned long samps = nsamp; + if(ci.eFrame != -1) + samps = (unsigned long)ci.eFrame - slice_frame; + if(samps < nsamp) + nsamp = samps; + } + +#ifdef NODE_DEBUG_PROCESS + fprintf(stderr, "AudioTrack::processTrackCtrls k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, frame, ci.eFrame, nsamp); +#endif + } + } + +#ifdef NODE_DEBUG_PROCESS + fprintf(stderr, "AudioTrack::processTrackCtrls sample:%lu nsamp:%lu\n", sample, nsamp); +#endif + + // + // Process all control ring buffer items valid for this time period... + // + bool found = false; + unsigned long frame = 0; + unsigned long evframe; + while(!_controlFifo.isEmpty()) + { + ControlEvent v = _controlFifo.peek(); + // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. + // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. + evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes; + + // Protection. Observed this condition. Why? Supposed to be linear timestamps. + if(found && evframe < frame) + { + fprintf(stderr, "AudioTrack::processTrackCtrls *** Error: evframe:%lu < frame:%lu idx:%lu val:%f unique:%d\n", + evframe, v.frame, v.idx, v.value, v.unique); + + // No choice but to ignore it. + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + continue; + } + + if(evframe >= nframes // Next events are for a later period. + || (!found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) + || (found && !v.unique && (evframe - sample >= min_per))) // Eat up events within minimum slice - they're too close. + break; + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + + if(v.idx >= _controlPorts) // Sanity check + break; + + found = true; + frame = evframe; + + if(trackChans != 0 && !_prefader) // Only if we want to run, and prefader is off. + { + CtrlInterpolate* ci = &_controls[v.idx].interp; + ci->eFrame = frame; + ci->eVal = v.value; + ci->eStop = true; + } + + // Need to update the automation value, otherwise it overwrites later with the last automation value. + setPluginCtrlVal(v.idx, v.value); + } + + if(found && trackChans != 0 && !_prefader) // If a control FIFO item was found, takes priority over automation controller stream. + nsamp = frame - sample; + + if(sample + nsamp > nframes) // Safety check. + nsamp = nframes - sample; + + // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead. + // Note this means it is still possible to get stuck in the top loop (at least for a while). + if(nsamp != 0) + { + if(trackChans != 0 && !_prefader) + { + const CtrlInterpolate& vol_interp = _controls[AC_VOLUME].interp; + const CtrlInterpolate& pan_interp = _controls[AC_PAN].interp; + unsigned k; + //const float up_fact = 1.002711275; // 3.01.. dB / 256 + //const float down_fact = 0.997296056; + const float up_fact = 1.003471749; // 3.01.. dB / 200 + const float down_fact = 0.996540262; + float *sp1, *sp2, *dp1, *dp2; + float _volume, v, _pan, v1, v2; + + if(trackChans == 1) + { + float* sp = buffer[0] + sample; + float* dp = outBuffers[0] + sample; + sp1 = sp2 = sp; + dp1 = outBuffersExtraMix[0] + sample; + dp2 = outBuffersExtraMix[1] + sample; + k = 0; + if(vol_interp.doInterp && MusEGlobal::audio->isPlaying()) + { + for( ; k < nsamp; ++k) + { + _volume = vol_ctrl->interpolate(slice_frame + k, vol_interp); + v = _volume * _gain; + if(v > _curVolume) + { + if(_curVolume == 0.0) + _curVolume = 0.001; // Kick-start it from zero at -30dB. + _curVolume *= up_fact; + if(_curVolume >= v) + _curVolume = v; + } + else + if(v < _curVolume) + { + _curVolume *= down_fact; + if(_curVolume <= v || _curVolume <= 0.001) // Or if less than -30dB. + _curVolume = v; + } + *dp++ = *sp++ * _curVolume; + } + _controls[AC_VOLUME].val = _volume; // Update the port. + } + else + { + if(vol_interp.doInterp) // And not playing... + _volume = vol_ctrl->interpolate(pos, vol_interp); + else + _volume = vol_interp.sVal; + _controls[AC_VOLUME].val = _volume; // Update the port. + v = _volume * _gain; + if(v > _curVolume) + { + //fprintf(stderr, "A %f %f\n", v, _curVolume); + if(_curVolume == 0.0) + _curVolume = 0.001; // Kick-start it from zero at -30dB. + for( ; k < nsamp; ++k) + { + _curVolume *= up_fact; + if(_curVolume >= v) + { + _curVolume = v; + break; + } + *dp++ = *sp++ * _curVolume; + } + } + else + if(v < _curVolume) + { + //fprintf(stderr, "B %f %f\n", v, _curVolume); + for( ; k < nsamp; ++k) + { + _curVolume *= down_fact; + if(_curVolume <= v || _curVolume <= 0.001) // Or if less than -30dB. + { + _curVolume = v; + break; + } + *dp++ = *sp++ * _curVolume; + } + } + for( ; k < nsamp; ++k) + *dp++ = *sp++ * _curVolume; + } + } + else + if(trackChans >= 2) + { + sp1 = buffer[0] + sample; + sp2 = buffer[1] + sample; + dp1 = outBuffers[0] + sample; + dp2 = outBuffers[1] + sample; + } + + k = 0; + if((vol_interp.doInterp || pan_interp.doInterp) && MusEGlobal::audio->isPlaying()) + { + for( ; k < nsamp; ++k) + { + _volume = vol_ctrl->interpolate(slice_frame + k, vol_interp); + v = _volume * _gain; + _pan = pan_ctrl->interpolate(slice_frame + k, pan_interp); + v1 = v * (1.0 - _pan); + v2 = v * (1.0 + _pan); + if(v1 > _curVol1) + { + //fprintf(stderr, "C %f %f \n", v1, _curVol1); + if(_curVol1 == 0.0) + _curVol1 = 0.001; // Kick-start it from zero at -30dB. + _curVol1 *= up_fact; + if(_curVol1 >= v1) + _curVol1 = v1; + } + else + if(v1 < _curVol1) + { + //fprintf(stderr, "D %f %f \n", v1, _curVol1); + _curVol1 *= down_fact; + if(_curVol1 <= v1 || _curVol1 <= 0.001) // Or if less than -30dB. + _curVol1 = v1; + } + *dp1++ = *sp1++ * _curVol1; + + if(v2 > _curVol2) + { + //fprintf(stderr, "E %f %f \n", v2, _curVol2); + if(_curVol2 == 0.0) + _curVol2 = 0.001; // Kick-start it from zero at -30dB. + _curVol2 *= up_fact; + if(_curVol2 >= v2) + _curVol2 = v2; + } + else + if(v2 < _curVol2) + { + //fprintf(stderr, "F %f %f \n", v2, _curVol2); + _curVol2 *= down_fact; + if(_curVol2 <= v2 || _curVol2 <= 0.001) // Or if less than -30dB. + _curVol2 = v2; + } + *dp2++ = *sp2++ * _curVol2; + } + _controls[AC_VOLUME].val = _volume; // Update the ports. + _controls[AC_PAN].val = _pan; + } + else + { + if(vol_interp.doInterp) // And not playing... + _volume = vol_ctrl->interpolate(pos, vol_interp); + else + _volume = vol_interp.sVal; + if(pan_interp.doInterp) // And not playing... + _pan = pan_ctrl->interpolate(pos, pan_interp); + else + _pan = pan_interp.sVal; + _controls[AC_VOLUME].val = _volume; // Update the ports. + _controls[AC_PAN].val = _pan; + v = _volume * _gain; + v1 = v * (1.0 - _pan); + v2 = v * (1.0 + _pan); + if(v1 > _curVol1) + { + //fprintf(stderr, "C %f %f \n", v1, _curVol1); + if(_curVol1 == 0.0) + _curVol1 = 0.001; // Kick-start it from zero at -30dB. + for( ; k < nsamp; ++k) + { + _curVol1 *= up_fact; + if(_curVol1 >= v1) + { + _curVol1 = v1; + break; + } + *dp1++ = *sp1++ * _curVol1; + } + } + else + if(v1 < _curVol1) + { + //fprintf(stderr, "D %f %f \n", v1, _curVol1); + for( ; k < nsamp; ++k) + { + _curVol1 *= down_fact; + if(_curVol1 <= v1 || _curVol1 <= 0.001) // Or if less than -30dB. + { + _curVol1 = v1; + break; + } + *dp1++ = *sp1++ * _curVol1; + } + } + for( ; k < nsamp; ++k) + *dp1++ = *sp1++ * _curVol1; + + k = 0; + if(v2 > _curVol2) + { + //fprintf(stderr, "E %f %f \n", v2, _curVol2); + if(_curVol2 == 0.0) + _curVol2 = 0.001; // Kick-start it from zero at -30dB. + for( ; k < nsamp; ++k) + { + _curVol2 *= up_fact; + if(_curVol2 >= v2) + { + _curVol2 = v2; + break; + } + *dp2++ = *sp2++ * _curVol2; + } + } + else + if(v2 < _curVol2) + { + //fprintf(stderr, "F %f %f \n", v2, _curVol2); + for( ; k < nsamp; ++k) + { + _curVol2 *= down_fact; + if(_curVol2 <= v2 || _curVol2 <= 0.001) // Or if less than -30dB. + { + _curVol2 = v2; + break; + } + *dp2++ = *sp2++ * _curVol2; + } + } + for( ; k < nsamp; ++k) + *dp2++ = *sp2++ * _curVol2; + } + } + + sample += nsamp; + } + + ++cur_slice; // Slice is done. Moving on to any next slice now... + } +} //--------------------------------------------------------- // copyData //--------------------------------------------------------- // this is also addData(). addData() just calls copyData(..., true); -void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int srcChannels, unsigned nframes, float** dstBuffer, bool add /*=false*/) +void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int srcChannels, unsigned nframes, float** dstBuffer, bool add) { //Changed by T356. 12/12/09. // Overhaul and streamline to eliminate multiple processing during one process loop. @@ -385,11 +786,9 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s if(srcStartChan == -1) srcStartChan = 0; - int trackChans = channels(); + const int trackChans = channels(); int srcChans = (srcChannels == -1) ? trackChans : srcChannels; - int srcTotalOutChans = totalOutChannels(); - if(channels() == 1) - srcTotalOutChans = 1; + const int srcTotalOutChans = (channels() == 1) ? 1 : totalOutChannels(); // Special consideration for metronome: It is not part of the track list, // and it has no in or out routes, yet multiple output tracks may call addData on it! @@ -397,26 +796,14 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s // Not strictly necessary here because only addData is ever called, but just to be consistent... int i; - - float* buffer[srcTotalOutChans]; + + float* buffer[srcTotalOutChans]; float data[nframes * srcTotalOutChans]; - - // precalculate stereo volume - double vol[2]; - double _volume = controller()->value(AC_VOLUME, pos, - !MusEGlobal::automation || automationType() == AUTO_OFF || !_volumeEnCtrl || !_volumeEn2Ctrl); - double _pan = controller()->value(AC_PAN, pos, - !MusEGlobal::automation || automationType() == AUTO_OFF || !_panEnCtrl || !_panEn2Ctrl); - - vol[0] = _volume * (1.0 - _pan) * _gain; - vol[1] = _volume * (1.0 + _pan) * _gain; float meter[trackChans]; // Have we been here already during this process cycle? if(processed()) { - // If there is only one (or no) output routes, it's an error - we've been called more than once per process cycle! - // No, this is no longer an error, it's deliberate. Processing no longer done in 'chains', now done randomly. p4.0.37 #ifdef NODE_DEBUG_PROCESS printf("MusE: AudioTrack::copyData name:%s already processed _haveData:%d\n", name().toLatin1().constData(), _haveData); #endif @@ -424,9 +811,48 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s // Is there already some data gathered from a previous call during this process cycle? if(_haveData) { - // Point the input buffers at our local cached 'pre-volume' buffers. They need processing, so continue on after. - for(i = 0; i < srcTotalOutChans; ++i) - buffer[i] = outBuffers[i]; + if(srcChans == dstChannels) + { + for(int c = 0; c < dstChannels; ++c) + { + float* sp = outBuffers[c + srcStartChan]; + float* dp = dstBuffer[c]; + if (!add) + AL::dsp->cpy(dp, sp, nframes); + else + for(unsigned k = 0; k < nframes; ++k) + *dp++ += *sp++; + } + } + else if(srcChans == 1 && dstChannels == 2) + { + for(int c = 0; c < dstChannels; ++c) + { + float* sp; + if(!_prefader && srcStartChan == 0 && trackChans == 1) + sp = outBuffersExtraMix[c]; // Use the pre-panned mono-to-stereo extra buffers. + else + sp = outBuffers[srcStartChan]; // In all other cases use the main buffers. + float* dp = dstBuffer[c]; + if (!add) + AL::dsp->cpy(dp, sp, nframes); + else + for(unsigned k = 0; k < nframes; ++k) + *dp++ += *sp++; + } + } + else if(srcChans == 2 && dstChannels == 1) + { + float* dp = dstBuffer[0]; + float* sp1 = outBuffers[srcStartChan]; + float* sp2 = outBuffers[srcStartChan + 1]; + if (!add) + for(unsigned k = 0; k < nframes; ++k) + *dp++ = (*sp1++ + *sp2++); + else + for(unsigned k = 0; k < nframes; ++k) + *dp++ += (*sp1++ + *sp2++); + } } else { @@ -446,9 +872,8 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s memset(dstBuffer[i], 0, sizeof(float) * nframes); } } - // else if (add) do nothing. - return; } + return; } else { @@ -463,7 +888,7 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s printf("MusE: AudioTrack::copyData name:%s dstChannels:%d Off, zeroing buffers\n", name().toLatin1().constData(), dstChannels); #endif - if (!add) + if(!add) { // Track is off. Zero the supplied buffers. unsigned int q; @@ -478,9 +903,9 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s memset(dstBuffer[i], 0, sizeof(float) * nframes); } } - // else if (add) do nothing - - _efxPipe->apply(0, nframes, 0); // Just process controls only, not audio (do not 'run'). + + _efxPipe->apply(pos, 0, nframes, 0); // Just process controls only, not audio (do not 'run'). + processTrackCtrls(pos, 0, nframes, 0); for(i = 0; i < trackChans; ++i) _meter[i] = 0.0; @@ -489,14 +914,14 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s } // Point the input buffers at a temporary stack buffer. - for(i = 0; i < srcTotalOutChans; ++i) + for(i = 0; i < srcTotalOutChans; ++i) buffer[i] = data + i * nframes; // getData can use the supplied buffers, or change buffer to point to its own local buffers or Jack buffers etc. // For ex. if this is an audio input, Jack will set the pointers for us in AudioInput::getData! // Don't do any processing at all if off. Whereas, mute needs to be ready for action at all times, // so still call getData before it. Off is NOT meant to be toggled rapidly, but mute is ! - if(!getData(pos, srcTotalOutChans, nframes, buffer) || (!add && isMute() && !_prefader)) + if(!getData(pos, srcTotalOutChans, nframes, buffer)) { #ifdef NODE_DEBUG_PROCESS printf("MusE: AudioTrack::copyData name:%s srcTotalOutChans:%d zeroing buffers\n", name().toLatin1().constData(), srcTotalOutChans); @@ -520,17 +945,75 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s // apply plugin chain //--------------------------------------------------- - _efxPipe->apply(trackChans, nframes, buffer); + // Allow it to process even if muted so that when mute is turned off, left-over buffers (reverb tails etc) can die away. + _efxPipe->apply(pos, trackChans, nframes, buffer); //--------------------------------------------------- + // apply volume, pan + //--------------------------------------------------- + + #ifdef NODE_DEBUG_PROCESS + printf("MusE: AudioTrack::copyData trackChans:%d srcTotalOutChans:%d srcStartChan:%d srcChans:%d dstChannels:%d\n", trackChans, srcTotalOutChans, srcStartChan, srcChans, dstChannels); + #endif + + processTrackCtrls(pos, trackChans, nframes, buffer); + + const int valid_out_bufs = _prefader ? 0 : (trackChans >= 2 ? 2 : trackChans); + + //--------------------------------------------------- + // metering + //--------------------------------------------------- + + for(int c = 0; c < trackChans; ++c) + { + meter[c] = 0.0; + float* sp = (c >= valid_out_bufs) ? buffer[c] : outBuffers[c]; // Optimize: Don't all valid outBuffers just for meters + for(unsigned k = 0; k < nframes; ++k) + { + const double f = fabs(*sp++); // If the track is mono pan has no effect on meters. + if(f > meter[c]) + meter[c] = f; + } + _meter[c] = meter[c]; + if(_meter[c] > _peak[c]) + _peak[c] = _meter[c]; + } + + if(isMute()) + { + if (!add) + { + unsigned int q; + for(i = 0; i < dstChannels; ++i) + { + if(MusEGlobal::config.useDenormalBias) + { + for(q = 0; q < nframes; q++) + dstBuffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(dstBuffer[i], 0, sizeof(float) * nframes); + } + } + return; // We're outta here. + } + + // Copy whole blocks that we can get away with here outside of the track control processing loop. + for(i = valid_out_bufs; i < srcTotalOutChans; ++i) + AL::dsp->cpy(outBuffers[i], buffer[i], nframes); + + // We now have some data! Set to true. + _haveData = true; + + //--------------------------------------------------- // aux sends //--------------------------------------------------- - if(hasAuxSend() && !isMute()) + if(hasAuxSend()) { AuxList* al = MusEGlobal::song->auxs(); unsigned naux = al->size(); - for(unsigned k = 0; k < naux; ++k) + for(unsigned k = 0; k < naux; ++k) { float m = _auxSend[k]; if(m <= 0.0001) // optimize @@ -538,209 +1021,98 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s AudioAux* a = (AudioAux*)((*al)[k]); float** dst = a->sendBuffer(); int auxChannels = a->channels(); - if((srcChans ==1 && auxChannels==1) || srcChans == 2) + if((srcChans ==1 && auxChannels==1) || srcChans == 2) { - for(int ch = 0; ch < srcChans; ++ch) + for(int ch = 0; ch < srcChans; ++ch) { float* db = dst[ch % a->channels()]; // no matter whether there's one or two dst buffers - float* sb = buffer[ch]; - for(unsigned f = 0; f < nframes; ++f) - *db++ += (*sb++ * m * vol[ch]); // add to mix + float* sb = outBuffers[ch]; + for(unsigned f = 0; f < nframes; ++f) + *db++ += (*sb++ * m); // add to mix } } else if(srcChans==1 && auxChannels==2) // copy mono to both channels - { - for(int ch = 0; ch < auxChannels; ++ch) + { + for(int ch = 0; ch < auxChannels; ++ch) { float* db = dst[ch % a->channels()]; - float* sb = buffer[0]; - for(unsigned f = 0; f < nframes; ++f) - *db++ += (*sb++ * m * vol[ch]); // add to mix + float* sb = outBuffers[0]; + for(unsigned f = 0; f < nframes; ++f) + *db++ += (*sb++ * m); // add to mix } } } } //--------------------------------------------------- - // prefader metering + // copy to destination buffers //--------------------------------------------------- - if(_prefader) + // Sanity check. Is source starting channel out of range? Just zero and return. + if(srcStartChan >= srcTotalOutChans) { - for(i = 0; i < trackChans; ++i) + if(!add) { - float* p = buffer[i]; - meter[i] = 0.0; - for(unsigned k = 0; k < nframes; ++k) + unsigned int q; + for(i = 0; i < dstChannels; ++i) { - double f = fabs(*p++); - if(f > meter[i]) - meter[i] = f; - } - _meter[i] = meter[i]; - if(_meter[i] > _peak[i]) - _peak[i] = _meter[i]; - } - } - - if(isMute()) - { - if (!add) - { - unsigned int q; - for(i = 0; i < dstChannels; ++i) + if(MusEGlobal::config.useDenormalBias) { - if(MusEGlobal::config.useDenormalBias) - { - for(q = 0; q < nframes; q++) - dstBuffer[i][q] = MusEGlobal::denormalBias; - } - else - memset(dstBuffer[i], 0, sizeof(float) * nframes); - } + for(q = 0; q < nframes; q++) + dstBuffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(dstBuffer[i], 0, sizeof(float) * nframes); + } } - - if(!_prefader) - for(i = 0; i < trackChans; ++i) // Must process ALL channels, even if unconnected. Only max 2 channels. - _meter[i] = 0.0; - return; } - - // If we're using local cached 'pre-volume' buffers, copy the input buffers (as they are right now: post-effect pre-volume) back to them. - for(i = 0; i < srcTotalOutChans; ++i) - AL::dsp->cpy(outBuffers[i], buffer[i], nframes); - - // We have some data! Set to true. - _haveData = true; - } - - // Sanity check. Is source starting channel out of range? Just zero and return. - if(srcStartChan >= srcTotalOutChans) - { - unsigned int q; - for(i = 0; i < dstChannels; ++i) - { - if(MusEGlobal::config.useDenormalBias) - { - for(q = 0; q < nframes; q++) - dstBuffer[i][q] = MusEGlobal::denormalBias; - } - else - memset(dstBuffer[i], 0, sizeof(float) * nframes); - } - return; - } - // Force a source range to fit actual available total out channels. - if((srcStartChan + srcChans) > srcTotalOutChans) - srcChans = srcTotalOutChans - srcStartChan; - - //--------------------------------------------------- - // apply volume - // postfader metering - //--------------------------------------------------- + // Force a source range to fit actual available total out channels. + if((srcStartChan + srcChans) > srcTotalOutChans) + srcChans = srcTotalOutChans - srcStartChan; - #ifdef NODE_DEBUG_PROCESS - printf("MusE: AudioTrack::copyData trackChans:%d srcTotalOutChans:%d srcStartChan:%d srcChans:%d dstChannels:%d\n", trackChans, srcTotalOutChans, srcStartChan, srcChans, dstChannels); - #endif - - if(!_prefader) - { - for(int c = 0; c < trackChans; ++c) + if(srcChans == dstChannels) { - meter[c] = 0.0; - double v = (trackChans == 1 ? _volume : vol[c]); - float* sp = buffer[c]; - for(unsigned k = 0; k < nframes; ++k) - { - float val = *sp++ * v; // If the track is mono pan has no effect on meters. - double f = fabs(val); - if(f > meter[c]) - meter[c] = f; - } - _meter[c] = meter[c]; - if(_meter[c] > _peak[c]) - _peak[c] = _meter[c]; - } - } - - if(srcChans == dstChannels) - { - for(int c = 0; c < dstChannels; ++c) - { - double v; - if(srcStartChan > 2 || _prefader) // Don't apply pan or volume to extra channels above 2. Or if prefader on. - v = 1.0; - else - if(srcChans >= 2) // If 2 channels apply pan normally. - v = vol[c]; - else - if(trackChans < 2) // If 1 channel and track is 1 channel, don't apply pan. - v = _volume; - else - v = vol[srcStartChan]; // Otherwise 1 channel but track is 2 channels. Apply the channel volume. - - float* sp = buffer[c + srcStartChan]; - float* dp = dstBuffer[c]; - - if (!add) - { - for(unsigned k = 0; k < nframes; ++k) - *dp++ = (*sp++ * v); - } - else // if (add) + for(int c = 0; c < dstChannels; ++c) { + float* sp = outBuffers[c + srcStartChan]; + float* dp = dstBuffer[c]; + if (!add) + AL::dsp->cpy(dp, sp, nframes); + else for(unsigned k = 0; k < nframes; ++k) - *dp++ += (*sp++ * v); + *dp++ += *sp++; } } - } - else if(srcChans == 1 && dstChannels == 2) - { - for(int c = 0; c < dstChannels; ++c) + else if(srcChans == 1 && dstChannels == 2) { - double v; - if(srcStartChan > 2 || _prefader) // Don't apply pan or volume to extra channels above 2. Or if prefader on. - v = 1.0; - else - if(trackChans <= 1) // If track is mono apply pan. - v = vol[c]; - else - v = vol[srcStartChan]; // Otherwise track is stereo, apply the same channel volume to both. - - float* sp = buffer[srcStartChan]; - float* dp = dstBuffer[c]; - - if (!add) - { - for(unsigned k = 0; k < nframes; ++k) - *dp++ = (*sp++ * v); - } - else // if (add) + for(int c = 0; c < dstChannels; ++c) { + float* sp; + if(!_prefader && srcStartChan == 0 && trackChans == 1) + sp = outBuffersExtraMix[c]; // Use the pre-panned mono-to-stereo extra buffers. + else + sp = outBuffers[srcStartChan]; // In all other cases use the main buffers. + float* dp = dstBuffer[c]; + if (!add) + AL::dsp->cpy(dp, sp, nframes); + else for(unsigned k = 0; k < nframes; ++k) - *dp++ += (*sp++ * v); + *dp++ += *sp++; } } - } - else if(srcChans == 2 && dstChannels == 1) - { - double v1 = ((srcStartChan > 2 || _prefader) ? 1.0 : vol[srcStartChan]); // Don't apply pan or volume to extra channels above 2. Or if prefader on. - double v2 = ((srcStartChan > 2 || _prefader) ? 1.0 : vol[srcStartChan + 1]); // - float* dp = dstBuffer[0]; - float* sp1 = buffer[srcStartChan]; - float* sp2 = buffer[srcStartChan + 1]; - - if (!add) + else if(srcChans == 2 && dstChannels == 1) { + float* dp = dstBuffer[0]; + float* sp1 = outBuffers[srcStartChan]; + float* sp2 = outBuffers[srcStartChan + 1]; + if (!add) for(unsigned k = 0; k < nframes; ++k) - *dp++ = (*sp1++ * v1 + *sp2++ * v2); - } - else // if (add) - { + *dp++ = (*sp1++ + *sp2++); + else for(unsigned k = 0; k < nframes; ++k) - *dp++ += (*sp1++ * v1 + *sp2++ * v2); + *dp++ += (*sp1++ + *sp2++); } } } @@ -753,6 +1125,7 @@ void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int sr { copyData(pos,dstChannels,srcStartChan,srcChannels,nframes,dstBuffer, true); } + //--------------------------------------------------------- // readVolume //--------------------------------------------------------- @@ -785,67 +1158,6 @@ void AudioTrack::readVolume(Xml& xml) } } -// DELETETHIS 56 -// Removed by T356 -// "recfile" tag not saved anymore -/* - -THIS CODE IS OBSOLETE! _recFile has been changed from SndFile* to SndFileR. -this code has NOT been adapted! - -//--------------------------------------------------------- -// readRecfile -//--------------------------------------------------------- - -void AudioTrack::readRecfile(Xml& xml) - { - QString path; - int channels = 2; - int format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; - - for (;;) { - Xml::Token token = xml.parse(); - if (token == Xml::Error || token == Xml::End) - break; - const QString& tag = xml.s1(); - switch (token) { - case Xml::TagStart: - if (tag == "path") - path = xml.parse1(); - else if (tag == "channels") - channels = xml.parseInt(); - else if (tag == "format") - format = xml.parseInt(); - else if (tag == "samplebits") - ; - else - xml.unknown("recfile"); - break; - case Xml::TagEnd: - if (tag == "recfile") { - if (QFile::exists(path)) { - setRecFile(getWave(path, true)); - } - else { - setRecFile(new SndFile(path)); - recFile()->setFormat(format, channels, sampleRate); - if (recFile()->openWrite()) { - fprintf(stderr, "create wave file(%s) failed: %s\n", - path.toLatin1().constData(), recFile()->strerror().toLatin1().constData()); - delete _recFile; - _recFile = 0; - } - } - return; - } - default: - break; - } - } - } -*/ - - //--------------------------------------------------------- // setChannels //--------------------------------------------------------- @@ -949,6 +1261,10 @@ bool AudioInput::getData(unsigned, int channels, unsigned nframes, float** buffe for (int ch = 0; ch < channels; ++ch) { void* jackPort = jackPorts[ch]; + + // REMOVE Tim. Just a test. + //if(jackPort) + // MusEGlobal::audioDevice->portLatency(jackPort, true); // Do not get buffers of unconnected client ports. Causes repeating leftover data, can be loud, or DC ! if (jackPort && MusEGlobal::audioDevice->connections(jackPort)) @@ -1183,6 +1499,9 @@ void AudioOutput::processInit(unsigned nframes) if (!MusEGlobal::checkAudioDevice()) return; for (int i = 0; i < channels(); ++i) { if (jackPorts[i]) { + + //MusEGlobal::audioDevice->portLatency(jackPorts[i], true); // REMOVE Tim. Just a test. + buffer[i] = MusEGlobal::audioDevice->getBuffer(jackPorts[i], nframes); if (MusEGlobal::config.useDenormalBias) { for (unsigned int j=0; j < nframes; j++) @@ -1452,6 +1771,23 @@ void Fifo::add() muse_atomic_inc(&count); } +//--------------------------------------------------------- +// setParam +//--------------------------------------------------------- + +void AudioTrack::setParam(unsigned long i, float val) +{ + addScheduledControlEvent(i, val, MusEGlobal::audio->curFrame()); +} + +//--------------------------------------------------------- +// param +//--------------------------------------------------------- + +float AudioTrack::param(unsigned long i) const +{ + return _controls[i].val; +} //--------------------------------------------------------- // setChannels @@ -1473,35 +1809,30 @@ void AudioTrack::setTotalOutChannels(int num) int chans = _totalOutChannels; if(num != chans) { - // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. - if(chans < MAX_CHANNELS) - chans = MAX_CHANNELS; - if(outBuffers) - { - for(int i = 0; i < chans; ++i) - { - if(outBuffers[i]) - free(outBuffers[i]); - } - delete[] outBuffers; - } - _totalOutChannels = num; - chans = num; - // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. + int new_chans = num; + // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. + if(new_chans < MAX_CHANNELS) + new_chans = MAX_CHANNELS; if(chans < MAX_CHANNELS) chans = MAX_CHANNELS; - - outBuffers = new float*[chans]; - for (int i = 0; i < chans; ++i) + if(new_chans != chans) { - int rv = posix_memalign((void**)&outBuffers[i], 16, sizeof(float) * MusEGlobal::segmentSize); - if(rv != 0) + if(outBuffers) { - fprintf(stderr, "ERROR: AudioTrack::setTotalOutChannels: posix_memalign returned error:%d. Aborting!\n", rv); - abort(); + for(int i = 0; i < chans; ++i) + { + if(outBuffers[i]) + { + free(outBuffers[i]); + outBuffers[i] = NULL; + } + } + delete[] outBuffers; + outBuffers = NULL; } } + initBuffers(); } chans = num; // Limit the actual track (meters, copying etc, all 'normal' operation) to two-channel stereo. diff --git a/muse2/muse/plugin.cpp b/muse2/muse/plugin.cpp index 3029e618..f42a62b8 100644 --- a/muse2/muse/plugin.cpp +++ b/muse2/muse/plugin.cpp @@ -4,7 +4,7 @@ // $Id: plugin.cpp,v 1.21.2.23 2009/12/15 22:07:12 spamatica Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2013 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -1165,15 +1165,9 @@ Plugin* PluginList::find(const QString& file, const QString& name) Pipeline::Pipeline() : std::vector<PluginI*>() { - for (int i = 0; i < MAX_CHANNELS; ++i) - { - int rv = posix_memalign((void**)(buffer + i), 16, sizeof(float) * MusEGlobal::segmentSize); - if(rv != 0) - { - fprintf(stderr, "ERROR: Pipeline ctor: posix_memalign returned error:%d. Aborting!\n", rv); - abort(); - } - } + for(int i = 0; i < MAX_CHANNELS; ++i) + buffer[i] = NULL; + initBuffers(); for (int i = 0; i < PipelineDepth; ++i) push_back(0); @@ -1183,22 +1177,38 @@ Pipeline::Pipeline() // Pipeline copy constructor //--------------------------------------------------------- -Pipeline::Pipeline(const Pipeline& /*p*/) +Pipeline::Pipeline(const Pipeline& p, AudioTrack* t) : std::vector<PluginI*>() { - for (int i = 0; i < MAX_CHANNELS; ++i) + for(int i = 0; i < MAX_CHANNELS; ++i) + buffer[i] = NULL; + initBuffers(); + + for(int i = 0; i < PipelineDepth; ++i) { - int rv = posix_memalign((void**)(buffer + i), 16, sizeof(float) * MusEGlobal::segmentSize); - if(rv != 0) + PluginI* pli = p[i]; + if(pli) { - fprintf(stderr, "ERROR: Pipeline copy ctor: posix_memalign returned error:%d. Aborting!\n", rv); - abort(); + Plugin* pl = pli->plugin(); + if(pl) + { + PluginI* new_pl = new PluginI(); + if(new_pl->initPluginInstance(pl, t->channels())) { + fprintf(stderr, "cannot instantiate plugin <%s>\n", + pl->name().toLatin1().constData()); + delete new_pl; + } + else + { + // Assigns valid ID and track to plugin, and creates controllers for plugin. + t->setupPlugin(new_pl, i); + push_back(new_pl); + continue; + } + } } + push_back(NULL); // No plugin. Initialize with NULL. } - - // TODO: Copy plug-ins ! - for (int i = 0; i < PipelineDepth; ++i) - push_back(0); } //--------------------------------------------------------- @@ -1213,6 +1223,33 @@ Pipeline::~Pipeline() ::free(buffer[i]); } +void Pipeline::initBuffers() +{ + for(int i = 0; i < MAX_CHANNELS; ++i) + { + if(!buffer[i]) + { + int rv = posix_memalign((void**)(buffer + i), 16, sizeof(float) * MusEGlobal::segmentSize); + if(rv != 0) + { + fprintf(stderr, "ERROR: Pipeline ctor: posix_memalign returned error:%d. Aborting!\n", rv); + abort(); + } + } + } + + for(int i = 0; i < MAX_CHANNELS; ++i) + { + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + buffer[i][q] = MusEGlobal::denormalBias; + } + else + memset(buffer[i], 0, sizeof(float) * MusEGlobal::segmentSize); + } +} + //--------------------------------------------------------- // addScheduledControlEvent // track_ctrl_id is the fully qualified track audio controller number @@ -1235,29 +1272,24 @@ bool Pipeline::addScheduledControlEvent(int track_ctrl_id, float val, unsigned f } //--------------------------------------------------------- -// controllersEnabled +// controllerEnabled // Returns whether automation control stream is enabled or disabled. // Used during automation recording to inhibit gui controls //--------------------------------------------------------- -void Pipeline::controllersEnabled(int track_ctrl_id, bool* en1, bool* en2) +bool Pipeline::controllerEnabled(int track_ctrl_id) { // If a track controller, or the special dssi synth controller block, just return. if(track_ctrl_id < AC_PLUGIN_CTL_BASE || track_ctrl_id >= (int)genACnum(MAX_PLUGINS, 0)) - return; + return false; int rack_idx = (track_ctrl_id - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW; for (int i = 0; i < PipelineDepth; ++i) { PluginI* p = (*this)[i]; if(p && p->id() == rack_idx) - { - if(en1) - *en1 = p->controllerEnabled(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK); - if(en2) - *en2 = p->controllerEnabled2(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK); - return; - } + return p->controllerEnabled(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK); } + return false; } //--------------------------------------------------------- @@ -1529,7 +1561,7 @@ bool Pipeline::nativeGuiVisible(int idx) // If ports is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- -void Pipeline::apply(unsigned long ports, unsigned long nframes, float** buffer1) +void Pipeline::apply(unsigned pos, unsigned long ports, unsigned long nframes, float** buffer1) { bool swap = false; @@ -1543,22 +1575,22 @@ void Pipeline::apply(unsigned long ports, unsigned long nframes, float** buffer1 if (p->inPlaceCapable()) { if (swap) - p->apply(nframes, ports, buffer, buffer); + p->apply(pos, nframes, ports, buffer, buffer); else - p->apply(nframes, ports, buffer1, buffer1); + p->apply(pos, nframes, ports, buffer1, buffer1); } else { if (swap) - p->apply(nframes, ports, buffer, buffer1); + p->apply(pos, nframes, ports, buffer, buffer1); else - p->apply(nframes, ports, buffer1, buffer); + p->apply(pos, nframes, ports, buffer1, buffer); swap = !swap; } } else { - p->apply(nframes, 0, 0, 0); // Do not process (run) audio, process controllers only. + p->apply(pos, nframes, 0, 0, 0); // Do not process (run) audio, process controllers only. } } } @@ -1937,44 +1969,24 @@ bool PluginI::initPluginInstance(Plugin* plug, int c) { if(pd & LADSPA_PORT_INPUT) { + controls[curPort].idx = k; float val = _plugin->defaultValue(k); controls[curPort].val = val; controls[curPort].tmpVal = val; controls[curPort].enCtrl = true; - controls[curPort].en2Ctrl = true; + for(int i = 0; i < instances; ++i) + _plugin->connectPort(handle[i], k, &controls[curPort].val); ++curPort; } else if(pd & LADSPA_PORT_OUTPUT) { + controlsOut[curOutPort].idx = k; controlsOut[curOutPort].val = 0.0; controlsOut[curOutPort].tmpVal = 0.0; controlsOut[curOutPort].enCtrl = false; - controlsOut[curOutPort].en2Ctrl = false; - ++curOutPort; - } - } - } - curPort = 0; - curOutPort = 0; - for(unsigned long k = 0; k < ports; ++k) - { - LADSPA_PortDescriptor pd = _plugin->portd(k); - if(pd & LADSPA_PORT_CONTROL) - { - if(pd & LADSPA_PORT_INPUT) - { - for(int i = 0; i < instances; ++i) - _plugin->connectPort(handle[i], k, &controls[curPort].val); - controls[curPort].idx = k; - ++curPort; - } - else - if(pd & LADSPA_PORT_OUTPUT) - { for(int i = 0; i < instances; ++i) _plugin->connectPort(handle[i], k, &controlsOut[curOutPort].val); - controlsOut[curOutPort].idx = k; ++curOutPort; } } @@ -2375,16 +2387,6 @@ void PluginI::enableAllControllers(bool v) } //--------------------------------------------------------- -// enable2AllControllers -//--------------------------------------------------------- - -void PluginI::enable2AllControllers(bool v) -{ - for(unsigned long i = 0; i < controlPorts; ++i) - controls[i].en2Ctrl = v; -} - -//--------------------------------------------------------- // titlePrefix //--------------------------------------------------------- @@ -2400,246 +2402,217 @@ QString PluginI::titlePrefix() const // If ports is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- - -void PluginI::apply(unsigned long n, unsigned long ports, float** bufIn, float** bufOut) +void PluginI::apply(unsigned pos, unsigned long n, unsigned long ports, float** bufIn, float** bufOut) { - unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); - unsigned long sample = 0; - - // Must make this detectable for dssi vst effects. - const bool usefixedrate = _plugin->_isDssiVst; // Try this. (was: = true; ) + const unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); + unsigned long sample = 0; + + // Must make this detectable for dssi vst effects. + const bool usefixedrate = _plugin->_isDssiVst; + + // Note for dssi-vst this MUST equal audio period. It doesn't like broken-up runs (it stutters), + // even with fixed sizes. Could be a Wine + Jack thing, wanting a full Jack buffer's length. + // For now, the fixed size is clamped to the audio buffer size. + // TODO: We could later add slower processing over several cycles - + // so that users can select a small audio period but a larger control period. + const unsigned long min_per = (usefixedrate || MusEGlobal::config.minControlProcessPeriod > n) ? n : MusEGlobal::config.minControlProcessPeriod; + const unsigned long min_per_mask = min_per-1; // min_per must be power of 2 + + AutomationType at = AUTO_OFF; + CtrlListList* cll = NULL; + ciCtrlList icl_first; + if(_track) + { + at = _track->automationType(); + cll = _track->controller(); + if(_id != -1 && ports != 0) // Don't bother if not 'running'. + icl_first = cll->lower_bound(genACnum(_id, 0)); + } + const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; + const unsigned long in_ctrls = _plugin->controlInPorts(); - // TODO Make this number a global setting. - // Note for dssi-vst this MUST equal audio period. It doesn't like broken-up runs (it stutters), - // even with fixed sizes. Could be a Wine + Jack thing, wanting a full Jack buffer's length. - unsigned long fixedsize = n; // was: 2048 - - // For now, the fixed size is clamped to the audio buffer size. - // TODO: We could later add slower processing over several cycles - - // so that users can select a small audio period but a larger control period. - if(fixedsize > n) - fixedsize = n; - - unsigned long min_per = MusEGlobal::config.minControlProcessPeriod; - if(min_per > n) - min_per = n; - - // CtrlListList* cll = NULL; // WIP - AutomationType at = AUTO_OFF; - if(_track) - { - at = _track->automationType(); - //cll = _track->controller(); // WIP - } - bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; - - while(sample < n) + // Special for plugins: Deal with tmpVal. TODO: Get rid of tmpVal, maybe by using the FIFO... + for(unsigned long k = 0; k < controlPorts; ++k) + controls[k].val = controls[k].tmpVal; + + int cur_slice = 0; + while(sample < n) + { + unsigned long nsamp = n - sample; + const unsigned long slice_frame = pos + sample; + + // Process automation control values, while also determining the maximum acceptable + // size of this run. Further processing, from FIFOs for example, can lower the size + // from there, but this section determines where the next highest maximum frame + // absolutely needs to be for smooth playback of the controller value stream... + // + if(ports != 0) // Don't bother if not 'running'. + { + ciCtrlList icl = icl_first; + for(unsigned long k = 0; k < controlPorts; ++k) { - // nsamp is the number of samples the plugin->process() call will be supposed to do - unsigned long nsamp = usefixedrate ? fixedsize : n - sample; - - // - // Process automation control values, while also determining the maximum acceptable - // size of this run. Further processing, from FIFOs for example, can lower the size - // from there, but this section determines where the next highest maximum frame - // absolutely needs to be for smooth playback of the controller value stream... - // - if(_track && _id != -1 && ports != 0) // Don't bother if not 'running'. + CtrlList* cl = (cll && _id != -1 && icl != cll->end()) ? icl->second : NULL; + CtrlInterpolate& ci = controls[k].interp; + // Always refresh the interpolate struct at first, since things may have changed. + // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. + if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && + (slice_frame < (unsigned long)ci.sFrame || (ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame)) ) ) { - unsigned long frame = MusEGlobal::audio->pos().frame() + sample; - int nextFrame; - //double val; // WIP - for(unsigned long k = 0; k < controlPorts; ++k) + if(cl && _id != -1 && (unsigned long)cl->id() == genACnum(_id, k)) + { + cl->getInterpolation(slice_frame, no_auto || !controls[k].enCtrl, &ci); + if(icl != cll->end()) + ++icl; + } + else { + // No matching controller, or end. Just copy the current value into the interpolator. + // Keep the current icl iterator, because since they are sorted by frames, + // if the IDs didn't match it means we can just let k catch up with icl. + ci.sFrame = 0; + ci.eFrame = -1; + ci.sVal = controls[k].val; + ci.eVal = ci.sVal; + ci.doInterp = false; + ci.eStop = false; + } + } + else + { + if(ci.eStop && ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. + { + // Clear the stop condition and set up the interp struct appropriately as an endless value. + ci.sFrame = 0; //ci->eFrame; + ci.eFrame = -1; + ci.sVal = ci.eVal; + ci.doInterp = false; + ci.eStop = false; + } + if(cl && cll && icl != cll->end()) + ++icl; + } - -#if 0 // WIP - Work in progress. Tim. + if(!usefixedrate && MusEGlobal::audio->isPlaying()) + { + unsigned long samps = nsamp; + if(ci.eFrame != -1) + samps = (unsigned long)ci.eFrame - slice_frame; - ciCtrlList icl = cll->find(genACnum(_id, k)); - if(icl == cll->end()) - continue; - CtrlList* cl = icl->second; - if(no_auto || !controls[k].enCtrl || !controls[k].en2Ctrl || cl->empty()) - { - nextFrame = -1; - val = cl->curVal(); - } - else - { - ciCtrl i = cl->upper_bound(frame); // get the index after current frame - if (i == cl->end()) { // if we are past all items just return the last value - --i; - nextFrame = -1; - val = i->second.val; - } - else if(cl->mode() == CtrlList::DISCRETE) - { - if(i == cl->begin()) - { - nextFrame = i->second.frame; - val = i->second.val; - } - else - { - nextFrame = i->second.frame; - --i; - val = i->second.val; - } - } - else { // INTERPOLATE - if (i == cl->begin()) { - nextFrame = i->second.frame; - val = i->second.val; - } - else { - int frame2 = i->second.frame; - double val2 = i->second.val; - --i; - int frame1 = i->second.frame; - double val1 = i->second.val; - - - if(val2 != val1) - nextFrame = 0; // Zero signifies the next frame should be determined by caller. - else - nextFrame = frame2; - - if (cl->valueType() == VAL_LOG) { - val1 = 20.0*fast_log10(val1); - if (val1 < MusEGlobal::config.minSlider) - val1=MusEGlobal::config.minSlider; - val2 = 20.0*fast_log10(val2); - if (val2 < MusEGlobal::config.minSlider) - val2=MusEGlobal::config.minSlider; - } + if(!ci.doInterp && samps > min_per) + { + samps &= ~min_per_mask; + if((samps & min_per_mask) != 0) + samps += min_per; + } + else + samps = min_per; - val2 -= val1; - val1 += (double(frame - frame1) * val2)/double(frame2 - frame1); - - if (cl->valueType() == VAL_LOG) { - val1 = exp10(val1/20.0); - } + if(samps < nsamp) + nsamp = samps; + + } + + if(ci.doInterp && cl) + controls[k].val = cl->interpolate(MusEGlobal::audio->isPlaying() ? slice_frame : pos, ci); + else + controls[k].val = ci.sVal; + + controls[k].tmpVal = controls[k].val; // Special for plugins: Deal with tmpVal. - val = val1; - } - } - } - - controls[k].tmpVal = val; - - -#else - controls[k].tmpVal = _track->controller()->value(genACnum(_id, k), frame, - no_auto || !controls[k].enCtrl || !controls[k].en2Ctrl, - &nextFrame); -#endif - - #ifdef PLUGIN_DEBUGIN_PROCESS - printf("PluginI::apply k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, frame, nextFrame, nsamp); + printf("PluginI::apply k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, frame, ci.eFrame, nsamp); #endif - if(MusEGlobal::audio->isPlaying() && !usefixedrate && nextFrame != -1) - { - // Returned value of nextFrame can be zero meaning caller replaces with some (constant) value. - unsigned long samps = (unsigned long)nextFrame; - if(samps > frame + min_per) - { - unsigned long diff = samps - frame; - unsigned long mask = min_per-1; // min_per must be power of 2 - samps = diff & ~mask; - if((diff & mask) != 0) - samps += min_per; - } - else - samps = min_per; - - if(samps < nsamp) - nsamp = samps; - } - } - + } + } + #ifdef PLUGIN_DEBUGIN_PROCESS - printf("PluginI::apply sample:%lu nsamp:%lu\n", sample, nsamp); + printf("PluginI::apply sample:%lu nsamp:%lu\n", sample, nsamp); #endif - } - - // - // Process all control ring buffer items valid for this time period... - // - bool found = false; - unsigned long frame = 0; - unsigned long index = 0; - unsigned long evframe; - while(!_controlFifo.isEmpty()) - { - ControlEvent v = _controlFifo.peek(); - // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. - // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. - evframe = (syncFrame > v.frame + n) ? 0 : v.frame - syncFrame + n; - // Process only items in this time period. Make sure to process all - // subsequent items which have the same frame. - - // Protection. Observed this condition. Why? Supposed to be linear timestamps. - if(found && evframe < frame) - { - printf("PluginI::apply *** Error: evframe:%lu < frame:%lu idx:%lu val:%f unique:%d\n", - evframe, v.frame, v.idx, v.value, v.unique); - // No choice but to ignore it. - _controlFifo.remove(); // Done with the ring buffer's item. Remove it. - continue; - } - - // process control events up to the end of our processing cycle. - // but stop after a control event was found (then process(), - // then loop here again), but ensure that process() must process - // at least min_per frames. - if(evframe >= n // Next events are for a later period. - || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) - || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close. - || (usefixedrate && found && v.unique && v.idx == index)) // Special for dssi-vst: Fixed rate and must reply to all. - break; - _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + // + // Process all control ring buffer items valid for this time period... + // + bool found = false; + unsigned long frame = 0; + unsigned long index = 0; + unsigned long evframe; + // Get all control ring buffer items valid for this time period... + while(!_controlFifo.isEmpty()) + { + ControlEvent v = _controlFifo.peek(); + // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. + // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. + evframe = (syncFrame > v.frame + n) ? 0 : v.frame - syncFrame + n; + + #ifdef PLUGIN_DEBUGIN_PROCESS + fprintf(stderr, "PluginI::apply found:%d evframe:%lu frame:%lu event frame:%lu idx:%lu val:%f unique:%d\n", + found, evframe, frame, v.frame, v.idx, v.value, v.unique); + #endif - if(v.idx >= _plugin->_controlInPorts) // Sanity check - break; + // Protection. Observed this condition. Why? Supposed to be linear timestamps. + if(found && evframe < frame) + { + fprintf(stderr, "PluginI::apply *** Error: evframe:%lu < frame:%lu event: frame:%lu idx:%lu val:%f unique:%d\n", + evframe, frame, v.frame, v.idx, v.value, v.unique); + + // No choice but to ignore it. + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. + continue; + } - found = true; - frame = evframe; - index = v.idx; + if(evframe >= n // Next events are for a later period. + || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) + || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close. + || (usefixedrate && found && v.unique && v.idx == index)) // Special for dssi-vst: Fixed rate and must reply to all. + break; + _controlFifo.remove(); // Done with the ring buffer's item. Remove it. - controls[v.idx].tmpVal = v.value; - - // Need to update the automation value, otherwise it overwrites later with the last automation value. - if(_track && _id != -1) - _track->setPluginCtrlVal(genACnum(_id, v.idx), v.value); - } + if(v.idx >= in_ctrls) // Sanity check + break; - // Now update the actual values from the temporary values... - for(unsigned long k = 0; k < controlPorts; ++k) - controls[k].val = controls[k].tmpVal; - - if(found && !usefixedrate) // If a control FIFO item was found, takes priority over automation controller stream. - nsamp = frame - sample; + found = true; + frame = evframe; + index = v.idx; - if(sample + nsamp >= n) // Safety check. - nsamp = n - sample; - - // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead. - // Note this means it is still possible to get stuck in the top loop (at least for a while). - if(nsamp == 0) - continue; - - if(ports != 0) - { - connect(ports, sample, bufIn, bufOut); - - for(int i = 0; i < instances; ++i) - _plugin->apply(handle[i], nsamp); - } - - sample += nsamp; + if(ports == 0) // Don't bother if not 'running'. + controls[v.idx].val = controls[v.idx].tmpVal = v.value; // Might as well at least update these. + else + { + CtrlInterpolate* ci = &controls[v.idx].interp; + // Tell it to stop the current ramp at this frame, when it does stop, set this value: + ci->eFrame = frame; + ci->eVal = v.value; + ci->eStop = true; } + + // Need to update the automation value, otherwise it overwrites later with the last automation value. + if(_track && _id != -1) + _track->setPluginCtrlVal(genACnum(_id, v.idx), v.value); + } + + if(found && !usefixedrate) // If a control FIFO item was found, takes priority over automation controller stream. + nsamp = frame - sample; + + if(sample + nsamp > n) // Safety check. + nsamp = n - sample; + + // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead. + // Note this means it is still possible to get stuck in the top loop (at least for a while). + if(nsamp != 0) + { + if(ports != 0) // Don't bother if not 'running'. + { + connect(ports, sample, bufIn, bufOut); + + for(int i = 0; i < instances; ++i) + _plugin->apply(handle[i], nsamp); + } + + sample += nsamp; + } + + ++cur_slice; // Slice is done. Moving on to any next slice now... + } } //--------------------------------------------------------- @@ -2783,7 +2756,6 @@ int PluginI::oscControl(unsigned long port, float value) // Convert from DSSI port number to control input port index. unsigned long cport = _plugin->rpIdx[port]; - //unsigned long cport = _plugin->port2InCtrl(port); if((int)cport == -1) { @@ -2791,77 +2763,48 @@ int PluginI::oscControl(unsigned long port, float value) return 0; } + // Record automation: + // Take care of this immediately, because we don't want the silly delay associated with + // processing the fifo one-at-a-time in the apply(). + // NOTE: With some vsts we don't receive control events until the user RELEASES a control. + // So the events all arrive at once when the user releases a control. + // That makes this pretty useless... But what the heck... + if(_track && _id != -1) + { + unsigned long id = genACnum(_id, cport); + _track->recordAutomation(id, value); + } + // (From DSSI module). // p3.3.39 Set the DSSI control input port's value. // Observations: With a native DSSI synth like LessTrivialSynth, the native GUI's controls do not change the sound at all - // ie. they don't update the DSSI control port values themselves. + // ie. they don't update the DSSI control port values themselves. // Hence in response to the call to this oscControl, sent by the native GUI, it is required to that here. /// controls[cport].val = value; // DSSI-VST synths however, unlike DSSI synths, DO change their OWN sound in response to their gui controls. - // AND this function is called ! - // Despite the descrepency we are STILL required to update the DSSI control port values here - // because dssi-vst is WAITING FOR A RESPONSE! (A CHANGE in the control port value). + // AND this function is called ! + // Despite the descrepency we are STILL required to update the DSSI control port values here + // because dssi-vst is WAITING FOR A RESPONSE! (A CHANGE in the control port value). // It will output something like "...4 events expected..." and count that number down as 4 actual control port value CHANGES // are done here in response. Normally it says "...0 events expected..." when MusE is the one doing the DSSI control changes. - // TODO: May need FIFOs on each control(!) so that the control changes get sent one per process cycle! + // TODO: May need FIFOs on each control(!) so that the control changes get sent one per process cycle! // Observed countdown not actually going to zero upon string of changes. // Try this ... - /* DELETETHIS 20 - OscControlFifo* cfifo = _oscif.oscFifo(cport); - if(cfifo) - { - OscControlValue cv; - //cv.idx = cport; - cv.value = value; - // Time-stamp the event. Looks like no choice but to use the (possibly slow) call to gettimeofday via timestamp(), - // because these are asynchronous events arriving from OSC. timestamp() is more or less an estimate of the - // current frame. (This is exactly how ALSA events are treated when they arrive in our ALSA driver.) p4.0.15 Tim. - cv.frame = MusEGlobal::audio->timestamp(); - if(cfifo->put(cv)) - { - fprintf(stderr, "PluginI::oscControl: fifo overflow: in control number:%lu\n", cport); - } - } - */ + + // Schedules a timed control change: ControlEvent ce; ce.unique = _plugin->_isDssiVst; // Special for messages from vst gui to host - requires processing every message. - ce.fromGui = true; // It came form the plugin's own GUI. + ce.fromGui = true; // It came from the plugin's own GUI. ce.idx = cport; ce.value = value; - // Time-stamp the event. This does a possibly slightly slow call to gettimeofday via timestamp(). - // timestamp() is more or less an estimate of the current frame. (This is exactly how ALSA events - // are treated when they arrive in our ALSA driver.) - //ce.frame = MusEGlobal::audio->timestamp(); - // p4.0.23 timestamp() is circular, which is making it impossible to deal with 'modulo' events which - // slip in 'under the wire' before processing the ring buffers. So try this linear timestamp instead: - ce.frame = MusEGlobal::audio->curFrame(); + // Don't use timestamp(), because it's circular, which is making it impossible to deal + // with 'modulo' events which slip in 'under the wire' before processing the ring buffers. + ce.frame = MusEGlobal::audio->curFrame(); if(_controlFifo.put(ce)) - { fprintf(stderr, "PluginI::oscControl: fifo overflow: in control number:%lu\n", cport); - } - - - // Record automation: - // Take care of this immediately, because we don't want the silly delay associated with - // processing the fifo one-at-a-time in the apply(). - // NOTE: With some vsts we don't receive control events until the user RELEASES a control. - // So the events all arrive at once when the user releases a control. - // That makes this pretty useless... But what the heck... - if(_track && _id != -1) - { - unsigned long id = genACnum(_id, cport); - AutomationType at = _track->automationType(); - - // TODO: Taken from our native gui control handlers. - // This may need modification or may cause problems - - // we don't have the luxury of access to the dssi gui controls ! - if ((at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying())) - enableController(cport, false); //TODO maybe re-enable the ctrl soon? - - _track->recordAutomation(id, value); - } - + + enableController(cport, false); //TODO maybe re-enable the ctrl soon? + /* DELETETHIS 12 const DSSI_Descriptor* dssi = synth->dssi; const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; @@ -3478,9 +3421,10 @@ PluginGui::PluginGui(MusECore::PluginIBase* p) mapperReleased->setMapping(obj, nobj); mapperContextMenuReq->setMapping(obj, nobj); - gw[nobj].widget = (QWidget*)obj; - gw[nobj].param = parameter; - gw[nobj].type = -1; + gw[nobj].widget = (QWidget*)obj; + gw[nobj].param = parameter; + gw[nobj].type = -1; + gw[nobj].pressed = false; if (strcmp(obj->metaObject()->className(), "MusEGui::Slider") == 0) { gw[nobj].type = GuiWidgets::SLIDER; @@ -3559,6 +3503,7 @@ PluginGui::PluginGui(MusECore::PluginIBase* p) double dupper = upper; double val = plugin->param(i); double dval = val; + params[i].pressed = false; params[i].hint = range.HintDescriptor; getPluginConvertedValues(range, lower, upper, dlower, dupper, dval); @@ -3636,11 +3581,12 @@ PluginGui::PluginGui(MusECore::PluginIBase* p) QLabel* label = 0; LADSPA_PortRangeHint range = plugin->rangeOut(i); double lower = 0.0; // default values - double upper = 1.0; + double upper = 32768.0; // Many latency outs have no hints so set this arbitrarily high double dlower = lower; double dupper = upper; double val = plugin->paramOut(i); double dval = val; + paramsOut[i].pressed = false; paramsOut[i].hint = range.HintDescriptor; getPluginConvertedValues(range, lower, upper, dlower, dupper, dval); @@ -3733,48 +3679,39 @@ void PluginGui::heartBeat() void PluginGui::ctrlPressed(int param) { - AutomationType at = AUTO_OFF; + params[param].pressed = true; MusECore::AudioTrack* track = plugin->track(); - if(track) - at = track->automationType(); - - if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE) - plugin->enableController(param, false); - int id = plugin->id(); - - if(id == -1) - return; - - id = MusECore::genACnum(id, param); - - if(params[param].type == GuiParam::GUI_SLIDER) + if(id != -1) { - double val = ((Slider*)params[param].actuator)->value(); - if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) - val = pow(10.0, val/20.0); - else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) - val = rint(val); - plugin->setParam(param, val); - ((DoubleLabel*)params[param].label)->setValue(val); - - if(track) + id = MusECore::genACnum(id, param); + if(params[param].type == GuiParam::GUI_SLIDER) { - track->setPluginCtrlVal(id, val); - track->startAutoRecord(id, val); + double val = ((Slider*)params[param].actuator)->value(); + if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) + val = pow(10.0, val/20.0); + else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) + val = rint(val); + params[param].label->blockSignals(true); + params[param].label->setValue(val); + params[param].label->blockSignals(false); + if(track) + { + track->startAutoRecord(id, val); + track->setPluginCtrlVal(id, val); + } } - } - else if(params[param].type == GuiParam::GUI_SWITCH) - { - float val = (float)((CheckBox*)params[param].actuator)->isChecked(); - plugin->setParam(param, val); - - if(track) + else if(params[param].type == GuiParam::GUI_SWITCH) { - track->setPluginCtrlVal(id, val); - track->startAutoRecord(id, val); + float val = (float)((CheckBox*)params[param].actuator)->isChecked(); + if(track) + { + track->startAutoRecord(id, val); + track->setPluginCtrlVal(id, val); + } } } + plugin->enableController(param, false); } //--------------------------------------------------------- @@ -3787,28 +3724,29 @@ void PluginGui::ctrlReleased(int param) MusECore::AudioTrack* track = plugin->track(); if(track) at = track->automationType(); - + + int id = plugin->id(); + if(track && id != -1) + { + id = MusECore::genACnum(id, param); + if(params[param].type == GuiParam::GUI_SLIDER) + { + double val = ((Slider*)params[param].actuator)->value(); + if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) + val = pow(10.0, val/20.0); + else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) + val = rint(val); + track->stopAutoRecord(id, val); + } + } + // Special for switch - don't enable controller until transport stopped. if ((at == AUTO_OFF) || - (at == AUTO_READ) || (at == AUTO_TOUCH && (params[param].type != GuiParam::GUI_SWITCH || !MusEGlobal::audio->isPlaying()) ) ) plugin->enableController(param, true); - int id = plugin->id(); - if(!track || id == -1) - return; - id = MusECore::genACnum(id, param); - - if(params[param].type == GuiParam::GUI_SLIDER) - { - double val = ((Slider*)params[param].actuator)->value(); - if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) - val = pow(10.0, val/20.0); - else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) - val = rint(val); - track->stopAutoRecord(id, val); - } + params[param].pressed = false; } //--------------------------------------------------------- @@ -3828,35 +3766,24 @@ void PluginGui::ctrlRightClicked(const QPoint &p, int param) void PluginGui::sliderChanged(double val, int param, bool shift_pressed) { - AutomationType at = AUTO_OFF; MusECore::AudioTrack* track = plugin->track(); - if(track) - at = track->automationType(); - - if ( (at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) - plugin->enableController(param, false); - + if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) val = pow(10.0, val/20.0); else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) val = rint(val); - - if (plugin->param(param) != val) { - plugin->setParam(param, val); - ((DoubleLabel*)params[param].label)->setValue(val); - } - + + params[param].label->blockSignals(true); + params[param].label->setValue(val); + params[param].label->blockSignals(false); int id = plugin->id(); - if(id == -1) - return; - id = MusECore::genACnum(id, param); - - if(track) + if(track && id != -1) { - track->setPluginCtrlVal(id, val); + id = MusECore::genACnum(id, param); if (!shift_pressed) track->recordAutomation(id, val); //with shift, we get straight lines :) - } + } + plugin->setParam(param, val); // Schedules a timed control change. + plugin->enableController(param, false); } //--------------------------------------------------------- @@ -3865,36 +3792,24 @@ void PluginGui::sliderChanged(double val, int param, bool shift_pressed) void PluginGui::labelChanged(double val, int param) { - AutomationType at = AUTO_OFF; MusECore::AudioTrack* track = plugin->track(); - if(track) - at = track->automationType(); - - if ( (at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) - plugin->enableController(param, false); - + double dval = val; if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) dval = MusECore::fast_log10(val) * 20.0; else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) dval = rint(val); - if (plugin->param(param) != val) { - plugin->setParam(param, val); - ((Slider*)params[param].actuator)->setValue(dval); - } - + params[param].actuator->blockSignals(true); + ((Slider*)params[param].actuator)->setValue(dval); + params[param].actuator->blockSignals(false); int id = plugin->id(); - if(id == -1) - return; - - id = MusECore::genACnum(id, param); - - if(track) + if(track && id != -1) { - track->setPluginCtrlVal(id, val); + id = MusECore::genACnum(id, param); track->startAutoRecord(id, val); - } + } + plugin->setParam(param, val); // Schedules a timed control change. + plugin->enableController(param, false); } //--------------------------------------------------------- @@ -4032,12 +3947,18 @@ void PluginGui::updateValues() { sv = rint(lv); lv = sv; - } + } + gp->label->blockSignals(true); + gp->actuator->blockSignals(true); gp->label->setValue(lv); ((Slider*)(gp->actuator))->setValue(sv); + gp->label->blockSignals(false); + gp->actuator->blockSignals(false); } else if (gp->type == GuiParam::GUI_SWITCH) { + gp->actuator->blockSignals(true); ((CheckBox*)(gp->actuator))->setChecked(int(plugin->param(i))); + gp->actuator->blockSignals(false); } } } @@ -4047,6 +3968,7 @@ void PluginGui::updateValues() int type = gw[i].type; unsigned long param = gw[i].param; float val = plugin->param(param); + widget->blockSignals(true); switch(type) { case GuiWidgets::SLIDER: ((Slider*)widget)->setValue(val); // Note conversion to double @@ -4061,6 +3983,7 @@ void PluginGui::updateValues() ((QComboBox*)widget)->setCurrentIndex(int(val)); break; } + widget->blockSignals(false); } } } @@ -4098,126 +4021,93 @@ void PluginGui::updateControls() if (params) { - for (unsigned long i = 0; i < plugin->parameters(); ++i) { - GuiParam* gp = ¶ms[i]; - if (gp->type == GuiParam::GUI_SLIDER) { - { - double lv = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), i), - MusEGlobal::audio->curFramePos(), - !MusEGlobal::automation || - plugin->track()->automationType() == AUTO_OFF || - !plugin->controllerEnabled(i) || - !plugin->controllerEnabled2(i)); - double sv = lv; - if (LADSPA_IS_HINT_LOGARITHMIC(params[i].hint)) - sv = MusECore::fast_log10(lv) * 20.0; - else - if (LADSPA_IS_HINT_INTEGER(params[i].hint)) - { - sv = rint(lv); - lv = sv; - } - if(((Slider*)(gp->actuator))->value() != sv) + for (unsigned long i = 0; i < plugin->parameters(); ++i) { + GuiParam* gp = ¶ms[i]; + if(gp->pressed) // Inhibit the controller stream if control is currently pressed. + continue; + double v = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), i), + MusEGlobal::audio->curFramePos(), + !MusEGlobal::automation || + plugin->track()->automationType() == AUTO_OFF || + !plugin->controllerEnabled(i)); + if (gp->type == GuiParam::GUI_SLIDER) { { - gp->label->blockSignals(true); - ((Slider*)(gp->actuator))->blockSignals(true); - ((Slider*)(gp->actuator))->setValue(sv); - gp->label->setValue(lv); - ((Slider*)(gp->actuator))->blockSignals(false); - gp->label->blockSignals(false); - } + double sv = v; + if (LADSPA_IS_HINT_LOGARITHMIC(params[i].hint)) + sv = MusECore::fast_log10(v) * 20.0; + else + if (LADSPA_IS_HINT_INTEGER(params[i].hint)) + { + sv = rint(v); + v = sv; + } + if(((Slider*)(gp->actuator))->value() != sv) + { + gp->label->blockSignals(true); + gp->actuator->blockSignals(true); + ((Slider*)(gp->actuator))->setValue(sv); + gp->label->setValue(v); + gp->actuator->blockSignals(false); + gp->label->blockSignals(false); + } + } } - } - else if (gp->type == GuiParam::GUI_SWITCH) { - { - bool v = (int)plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), i), - MusEGlobal::audio->curFramePos(), - !MusEGlobal::automation || - plugin->track()->automationType() == AUTO_OFF || - !plugin->controllerEnabled(i) || - !plugin->controllerEnabled2(i)); - if(((CheckBox*)(gp->actuator))->isChecked() != v) + else if (gp->type == GuiParam::GUI_SWITCH) { { - ((CheckBox*)(gp->actuator))->blockSignals(true); - ((CheckBox*)(gp->actuator))->setChecked(v); - ((CheckBox*)(gp->actuator))->blockSignals(false); - } + bool b = (int)v; + if(((CheckBox*)(gp->actuator))->isChecked() != b) + { + gp->actuator->blockSignals(true); + ((CheckBox*)(gp->actuator))->setChecked(b); + gp->actuator->blockSignals(false); + } + } } - } - } + } } else if (gw) { - for (unsigned long i = 0; i < nobj; ++i) { + for (unsigned long i = 0; i < nobj; ++i) { + if(gw[i].pressed) // Inhibit the controller stream if control is currently pressed. + continue; QWidget* widget = gw[i].widget; int type = gw[i].type; - unsigned long param = gw[i].param; + unsigned long param = gw[i].param; + double v = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), param), + MusEGlobal::audio->curFramePos(), + !MusEGlobal::automation || + plugin->track()->automationType() == AUTO_OFF || + !plugin->controllerEnabled(param)); + widget->blockSignals(true); switch(type) { case GuiWidgets::SLIDER: { - double v = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), param), - MusEGlobal::audio->curFramePos(), - !MusEGlobal::automation || - plugin->track()->automationType() == AUTO_OFF || - !plugin->controllerEnabled(param) || - !plugin->controllerEnabled2(param)); if(((Slider*)widget)->value() != v) - { - ((Slider*)widget)->blockSignals(true); ((Slider*)widget)->setValue(v); - ((Slider*)widget)->blockSignals(false); - } } break; case GuiWidgets::DOUBLE_LABEL: { - double v = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), param), - MusEGlobal::audio->curFramePos(), - !MusEGlobal::automation || - plugin->track()->automationType() == AUTO_OFF || - !plugin->controllerEnabled(param) || - !plugin->controllerEnabled2(param)); if(((DoubleLabel*)widget)->value() != v) - { - ((DoubleLabel*)widget)->blockSignals(true); ((DoubleLabel*)widget)->setValue(v); - ((DoubleLabel*)widget)->blockSignals(false); - } } break; case GuiWidgets::QCHECKBOX: - { - bool b = (bool) plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), param), - MusEGlobal::audio->curFramePos(), - !MusEGlobal::automation || - plugin->track()->automationType() == AUTO_OFF || - !plugin->controllerEnabled(param) || - !plugin->controllerEnabled2(param)); + { + bool b = (bool)v; if(((QCheckBox*)widget)->isChecked() != b) - { - ((QCheckBox*)widget)->blockSignals(true); ((QCheckBox*)widget)->setChecked(b); - ((QCheckBox*)widget)->blockSignals(false); - } } break; case GuiWidgets::QCOMBOBOX: - { - int n = (int) plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), param), - MusEGlobal::audio->curFramePos(), - !MusEGlobal::automation || - plugin->track()->automationType() == AUTO_OFF || - !plugin->controllerEnabled(param) || - !plugin->controllerEnabled2(param)); + { + int n = (int)v; if(((QComboBox*)widget)->currentIndex() != n) - { - ((QComboBox*)widget)->blockSignals(true); ((QComboBox*)widget)->setCurrentIndex(n); - ((QComboBox*)widget)->blockSignals(false); - } } break; } - } + widget->blockSignals(false); + } } } @@ -4228,18 +4118,11 @@ void PluginGui::updateControls() void PluginGui::guiParamChanged(int idx) { QWidget* w = gw[idx].widget; - unsigned long param = gw[idx].param; + unsigned long param = gw[idx].param; int type = gw[idx].type; - AutomationType at = AUTO_OFF; MusECore::AudioTrack* track = plugin->track(); - if(track) - at = track->automationType(); - - if ( (at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying()) ) - plugin->enableController(param, false); - + double val = 0.0; switch(type) { case GuiWidgets::SLIDER: @@ -4256,11 +4139,12 @@ void PluginGui::guiParamChanged(int idx) break; } - for (unsigned long i = 0; i < nobj; ++i) { + for (unsigned long i = 0; i < nobj; ++i) { QWidget* widget = gw[i].widget; if (widget == w || param != gw[i].param) continue; int type = gw[i].type; + widget->blockSignals(true); switch(type) { case GuiWidgets::SLIDER: ((Slider*)widget)->setValue(val); @@ -4275,14 +4159,14 @@ void PluginGui::guiParamChanged(int idx) ((QComboBox*)widget)->setCurrentIndex(int(val)); break; } + widget->blockSignals(false); } - + int id = plugin->id(); if(track && id != -1) { id = MusECore::genACnum(id, param); - track->setPluginCtrlVal(id, val); - switch(type) + switch(type) { case GuiWidgets::DOUBLE_LABEL: case GuiWidgets::QCHECKBOX: @@ -4290,10 +4174,12 @@ void PluginGui::guiParamChanged(int idx) break; default: track->recordAutomation(id, val); - break; - } - } - plugin->setParam(param, val); + break; + } + } + + plugin->setParam(param, val); // Schedules a timed control change. + plugin->enableController(param, false); } //--------------------------------------------------------- @@ -4302,27 +4188,20 @@ void PluginGui::guiParamChanged(int idx) void PluginGui::guiParamPressed(int idx) { - unsigned long param = gw[idx].param; - - AutomationType at = AUTO_OFF; - MusECore::AudioTrack* track = plugin->track(); - if(track) - at = track->automationType(); - - if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE) - plugin->enableController(param, false); - - int id = plugin->id(); - if(!track || id == -1) - return; - - id = MusECore::genACnum(id, param); - + gw[idx].pressed = true; + unsigned long param = gw[idx].param; + plugin->enableController(param, false); + + //MusECore::AudioTrack* track = plugin->track(); + //int id = plugin->id(); + //if(!track || id == -1) + // return; + //id = MusECore::genACnum(id, param); // NOTE: For this to be of any use, the freeverb gui 2142.ui // would have to be used, and changed to use CheckBox and ComboBox // instead of QCheckBox and QComboBox, since both of those would // need customization (Ex. QCheckBox doesn't check on click). RECHECK: Qt4 it does? - /* + /* switch(type) { case GuiWidgets::QCHECKBOX: double val = (double)((CheckBox*)w)->isChecked(); @@ -4333,7 +4212,7 @@ void PluginGui::guiParamPressed(int idx) track->startAutoRecord(id, val); break; } - */ + */ } //--------------------------------------------------------- @@ -4352,18 +4231,14 @@ void PluginGui::guiParamReleased(int idx) // Special for switch - don't enable controller until transport stopped. if ((at == AUTO_OFF) || - (at == AUTO_READ) || (at == AUTO_TOUCH && (type != GuiWidgets::QCHECKBOX || !MusEGlobal::audio->isPlaying()) ) ) plugin->enableController(param, true); - int id = plugin->id(); - - if(!track || id == -1) - return; - - id = MusECore::genACnum(id, param); - + //int id = plugin->id(); + //if(!track || id == -1) + // return; + //id = MusECore::genACnum(id, param); // NOTE: For this to be of any use, the freeverb gui 2142.ui // would have to be used, and changed to use CheckBox and ComboBox // instead of QCheckBox and QComboBox, since both of those would @@ -4380,6 +4255,8 @@ void PluginGui::guiParamReleased(int idx) break; } */ + + gw[idx].pressed = false; } //--------------------------------------------------------- @@ -4387,53 +4264,44 @@ void PluginGui::guiParamReleased(int idx) //--------------------------------------------------------- void PluginGui::guiSliderPressed(int idx) - { - unsigned long param = gw[idx].param; +{ + gw[idx].pressed = true; + unsigned long param = gw[idx].param; QWidget *w = gw[idx].widget; - - AutomationType at = AUTO_OFF; MusECore::AudioTrack* track = plugin->track(); - if(track) - at = track->automationType(); - int id = plugin->id(); - - if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE) - plugin->enableController(param, false); - - if(!track || id == -1) - return; - - id = MusECore::genACnum(id, param); - - double val = ((Slider*)w)->value(); - plugin->setParam(param, val); - - track->setPluginCtrlVal(id, val); - track->startAutoRecord(id, val); - - // Needed so that paging a slider updates a label or other buddy control. - for (unsigned long i = 0; i < nobj; ++i) { - QWidget* widget = gw[i].widget; - if (widget == w || param != gw[i].param) - continue; - int type = gw[i].type; - switch(type) { - case GuiWidgets::SLIDER: - ((Slider*)widget)->setValue(val); - break; - case GuiWidgets::DOUBLE_LABEL: - ((DoubleLabel*)widget)->setValue(val); - break; - case GuiWidgets::QCHECKBOX: - ((QCheckBox*)widget)->setChecked(int(val)); - break; - case GuiWidgets::QCOMBOBOX: - ((QComboBox*)widget)->setCurrentIndex(int(val)); - break; - } - } + if(track && id != -1) + { + id = MusECore::genACnum(id, param); + double val = ((Slider*)w)->value(); + track->startAutoRecord(id, val); + // Needed so that paging a slider updates a label or other buddy control. + for (unsigned long i = 0; i < nobj; ++i) { + QWidget* widget = gw[i].widget; + if (widget == w || param != gw[i].param) + continue; + int type = gw[i].type; + widget->blockSignals(true); + switch(type) { + case GuiWidgets::SLIDER: + ((Slider*)widget)->setValue(val); + break; + case GuiWidgets::DOUBLE_LABEL: + ((DoubleLabel*)widget)->setValue(val); + break; + case GuiWidgets::QCHECKBOX: + ((QCheckBox*)widget)->setChecked(int(val)); + break; + case GuiWidgets::QCOMBOBOX: + ((QComboBox*)widget)->setCurrentIndex(int(val)); + break; + } + widget->blockSignals(false); + } + track->setPluginCtrlVal(id, val); } + plugin->enableController(param, false); +} //--------------------------------------------------------- // guiSliderReleased @@ -4449,23 +4317,21 @@ void PluginGui::guiSliderReleased(int idx) if(track) at = track->automationType(); - /* equivalent to - if ((at == AUTO_OFF) || - (at == AUTO_READ) || - (at == AUTO_TOUCH && (type != GuiWidgets::QCHECKBOX || <--- this type is SLIDER != CHECKBOX -> true - !MusEGlobal::audio->isPlaying()) ) ) <--- above==true -> this doesn't matter */ - if (at == AUTO_OFF || at == AUTO_READ || at == AUTO_TOUCH) - plugin->enableController(param, true); - int id = plugin->id(); - if(!track || id == -1) - return; + if(track && id != -1) + { + id = MusECore::genACnum(id, param); + + double val = ((Slider*)w)->value(); + track->stopAutoRecord(id, val); + } - id = MusECore::genACnum(id, param); + if (at == AUTO_OFF || + at == AUTO_TOUCH) + plugin->enableController(param, true); - double val = ((Slider*)w)->value(); - track->stopAutoRecord(id, val); + gw[idx].pressed = false; } //--------------------------------------------------------- diff --git a/muse2/muse/plugin.h b/muse2/muse/plugin.h index 822bafbd..e1682569 100644 --- a/muse2/muse/plugin.h +++ b/muse2/muse/plugin.h @@ -4,7 +4,7 @@ // $Id: plugin.h,v 1.9.2.13 2009/12/06 01:25:21 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2013 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -233,9 +233,8 @@ struct Port { unsigned long idx; float val; float tmpVal; - bool enCtrl; // Enable controller stream. - bool en2Ctrl; // Second enable controller stream (and'ed with enCtrl). + CtrlInterpolate interp; }; //--------------------------------------------------------- @@ -268,10 +267,7 @@ class PluginIBase virtual void enableController(unsigned long i, bool v = true) = 0; virtual bool controllerEnabled(unsigned long i) const = 0; - virtual void enable2Controller(unsigned long i, bool v = true) = 0; - virtual bool controllerEnabled2(unsigned long i) const = 0; virtual void enableAllControllers(bool v = true) = 0; - virtual void enable2AllControllers(bool v = true) = 0; virtual void updateControllers() = 0; virtual void activate() = 0; @@ -352,14 +348,11 @@ class PluginI : public PluginIBase { bool initPluginInstance(Plugin*, int channels); void setChannels(int); void connect(unsigned long ports, unsigned long offset, float** src, float** dst); - void apply(unsigned long n, unsigned long ports, float** bufIn, float** bufOut); + void apply(unsigned pos, unsigned long n, unsigned long ports, float** bufIn, float** bufOut); void enableController(unsigned long i, bool v = true) { controls[i].enCtrl = v; } bool controllerEnabled(unsigned long i) const { return controls[i].enCtrl; } - void enable2Controller(unsigned long i, bool v = true) { controls[i].en2Ctrl = v; } - bool controllerEnabled2(unsigned long i) const { return controls[i].en2Ctrl; } void enableAllControllers(bool v = true); - void enable2AllControllers(bool v = true); void activate(); void deactivate(); @@ -418,11 +411,12 @@ class PluginI : public PluginIBase { //--------------------------------------------------------- class Pipeline : public std::vector<PluginI*> { + private: float* buffer[MAX_CHANNELS]; - + void initBuffers(); public: Pipeline(); - Pipeline(const Pipeline&); + Pipeline(const Pipeline&, AudioTrack*); ~Pipeline(); void insert(PluginI* p, int index); void remove(int index); @@ -439,14 +433,13 @@ class Pipeline : public std::vector<PluginI*> { void deleteAllGuis(); bool guiVisible(int); bool nativeGuiVisible(int); - void apply(unsigned long ports, unsigned long nframes, float** buffer); + void apply(unsigned pos, unsigned long ports, unsigned long nframes, float** buffer); void move(int idx, bool up); bool empty(int idx) const; void setChannels(int); bool addScheduledControlEvent(int track_ctrl_id, float val, unsigned frame); // returns true if event cannot be delivered void enableController(int track_ctrl_id, bool en); - void enable2Controller(int track_ctrl_id, bool en); - void controllersEnabled(int track_ctrl_id, bool* en1, bool* en2); + bool controllerEnabled(int track_ctrl_id); }; typedef Pipeline::iterator iPluginI; @@ -489,6 +482,7 @@ struct GuiParam { }; int type; int hint; + bool pressed; MusEGui::DoubleLabel* label; QWidget* actuator; // Slider or Toggle Button (SWITCH) @@ -505,6 +499,7 @@ struct GuiWidgets { QWidget* widget; int type; unsigned long param; + bool pressed; }; //--------------------------------------------------------- diff --git a/muse2/muse/song.cpp b/muse2/muse/song.cpp index 412b52f1..d13f8398 100644 --- a/muse2/muse/song.cpp +++ b/muse2/muse/song.cpp @@ -393,10 +393,6 @@ void Song::duplicateTracks() if((*it)->selected()) { Track::TrackType type = (*it)->type(); - // TODO: Handle synths. p4.0.47 - if(type == Track::AUDIO_SOFTSYNTH) - continue; - if(type == Track::DRUM) ++drum_found; else if(type == Track::NEW_DRUM) @@ -507,7 +503,6 @@ void Song::duplicateTracks() { new_track->assign(*track, flags); #else - if(track->type() != Track::AUDIO_SOFTSYNTH) // TODO: Handle synths. p4.0.47 { Track* new_track = track->clone(flags); #endif @@ -519,7 +514,7 @@ void Song::duplicateTracks() addUndo(MusECore::UndoOp(MusECore::UndoOp::AddTrack, idx, new_track)); msgInsertTrack(new_track, idx, false); // No undo. insertTrack3(new_track, idx); - } + } } } --trackno; @@ -2458,14 +2453,10 @@ int Song::execAutomationCtlPopup(AudioTrack* track, const QPoint& menupos, int a { CtrlList *cl = icl->second; canAdd = true; - frame = MusEGlobal::audio->pos().frame(); - - bool en1, en2; - track->controllersEnabled(acid, &en1, &en2); - + bool en = track->controllerEnabled(acid); AutomationType at = track->automationType(); - if(!MusEGlobal::automation || at == AUTO_OFF || !en1 || !en2) + if(!MusEGlobal::automation || at == AUTO_OFF || !en) ctlval = cl->curVal(); else ctlval = cl->value(frame); @@ -2830,6 +2821,25 @@ void Song::updateSoloStates() } //--------------------------------------------------------- +// reenableTouchedControllers +// Enable all track and plugin controllers, and synth controllers if applicable, which are NOT in AUTO_WRITE mode. +//--------------------------------------------------------- + +void Song::reenableTouchedControllers() +{ + for(iTrack it = _tracks.begin(); it != _tracks.end(); ++it) + { + if((*it)->isMidiTrack()) + continue; + AudioTrack* t = static_cast<AudioTrack*>(*it); + AutomationType at = t->automationType(); + if(at == AUTO_WRITE) // Exclude write mode because controls need to remain disabled if pressed before play. + continue; + t->enableAllControllers(); + } +} + +//--------------------------------------------------------- // clearRecAutomation //--------------------------------------------------------- diff --git a/muse2/muse/song.h b/muse2/muse/song.h index b9ab5dd5..6621de35 100644 --- a/muse2/muse/song.h +++ b/muse2/muse/song.h @@ -329,6 +329,8 @@ class Song : public QObject { void readRoute(Xml& xml); void recordEvent(MidiTrack*, Event&); void msgInsertTrack(Track* track, int idx, bool u = true); + // Enable all track and plugin controllers, and synth controllers if applicable, which are NOT in AUTO_WRITE mode. + void reenableTouchedControllers(); void clearRecAutomation(bool clearList); void processAutomationEvents(); void processMasterRec(); diff --git a/muse2/muse/songfile.cpp b/muse2/muse/songfile.cpp index 90805175..b07d1243 100644 --- a/muse2/muse/songfile.cpp +++ b/muse2/muse/songfile.cpp @@ -1462,6 +1462,11 @@ void MusE::read(MusECore::Xml& xml, bool doReadMidiPorts, bool isTemplate) { MusEGlobal::song->read(xml, isTemplate); MusEGlobal::audio->msgUpdateSoloStates(); + // Inform the rest of the app that the song (may) have changed, using these flags. + // After this function is called, the caller can do a general Song::update() MINUS these flags, + // like in MusE::loadProjectFile1() - the only place calling so far, as of this writing. + // Some existing windows need this, like arranger, some don't which are dynamically created after this. + MusEGlobal::song->update(SC_TRACK_INSERTED); } else if (tag == "midiport") readMidiport(xml); diff --git a/muse2/muse/synth.cpp b/muse2/muse/synth.cpp index e04b55c8..d8d09048 100644 --- a/muse2/muse/synth.cpp +++ b/muse2/muse/synth.cpp @@ -92,10 +92,7 @@ QString SynthIF::titlePrefix() const { return QString(); } MusECore::AudioTrack* SynthIF::track() { return static_cast < MusECore::AudioTrack* > (synti); } void SynthIF::enableController(unsigned long, bool) { } bool SynthIF::controllerEnabled(unsigned long) const { return true;} -void SynthIF::enable2Controller(unsigned long, bool) { } -bool SynthIF::controllerEnabled2(unsigned long) const { return true; } void SynthIF::enableAllControllers(bool) { } -void SynthIF::enable2AllControllers(bool) { } void SynthIF::updateControllers() { } void SynthIF::activate() { } void SynthIF::deactivate() { } @@ -310,6 +307,44 @@ SynthI::SynthI() setPan(0.0); } +SynthI::SynthI(const SynthI& si, int flags) + : AudioTrack(si, flags) + { + synthesizer = 0; + _sif = 0; + _rwFlags = 1; + _openFlags = 1; + _readEnable = false; + _writeEnable = false; + + _curBankH = 0; + _curBankL = 0; + _curProgram = 0; + + setVolume(1.0); + setPan(0.0); + + Synth* s = si.synth(); + if (s) { + QString n; + n.setNum(s->instances()); + QString instance_name = s->name() + "-" + n; + if(!initInstance(s, instance_name)) { // false if success + return; + } + } + fprintf(stderr, "SynthI copy ctor: error initializing synth s:%p\n", s); + } + +//--------------------------------------------------------- +// ~SynthI +//--------------------------------------------------------- + +SynthI::~SynthI() + { + deactivate2(); + deactivate3(); + } //--------------------------------------------------------- // height in arranger @@ -582,17 +617,6 @@ void MessSynthIF::deactivate3() } //--------------------------------------------------------- -// ~SynthI -//--------------------------------------------------------- - -SynthI::~SynthI() - { - deactivate2(); - deactivate3(); - } - - -//--------------------------------------------------------- // initMidiSynth // search for software MusEGlobal::synthis and advertise //--------------------------------------------------------- @@ -1068,7 +1092,7 @@ iMPEvent MessSynthIF::getData(MidiPort* mp, MPEventList* el, iMPEvent i, unsigne fprintf(stderr, "should not happen - no _mess\n"); else { - _mess->process(buffer, curPos-pos, frame - curPos); + _mess->process(pos, buffer, curPos-pos, frame - curPos); } } curPos = frame; @@ -1088,7 +1112,7 @@ iMPEvent MessSynthIF::getData(MidiPort* mp, MPEventList* el, iMPEvent i, unsigne fprintf(stderr, "should not happen - no _mess\n"); else { - _mess->process(buffer, curPos - off, endPos - curPos); + _mess->process(pos, buffer, curPos - off, endPos - curPos); } } return i; diff --git a/muse2/muse/synth.h b/muse2/muse/synth.h index 6b42c1f5..273e314c 100644 --- a/muse2/muse/synth.h +++ b/muse2/muse/synth.h @@ -175,13 +175,10 @@ class SynthIF : public PluginIBase { virtual QString dirPath() const; virtual QString fileName() const; virtual QString titlePrefix() const; - virtual MusECore::AudioTrack* track(); + virtual AudioTrack* track(); virtual void enableController(unsigned long i, bool v = true); virtual bool controllerEnabled(unsigned long i) const; - virtual void enable2Controller(unsigned long i, bool v = true); - virtual bool controllerEnabled2(unsigned long i) const; virtual void enableAllControllers(bool v = true); - virtual void enable2AllControllers(bool v = true); virtual void updateControllers(); virtual void activate(); virtual void deactivate(); @@ -252,8 +249,9 @@ class SynthI : public AudioTrack, public MidiDevice, friend class VstNativeSynthIF; SynthI(); + SynthI(const SynthI& si, int flags); virtual ~SynthI(); - SynthI* clone(int /*flags*/) const { return new SynthI(*this); } + SynthI* clone(int flags) const { return new SynthI(*this, flags); } virtual inline int deviceType() const { return SYNTH_MIDI; } diff --git a/muse2/muse/ticksynth.cpp b/muse2/muse/ticksynth.cpp index 60d41806..a523ba86 100644 --- a/muse2/muse/ticksynth.cpp +++ b/muse2/muse/ticksynth.cpp @@ -111,6 +111,12 @@ class MetronomeSynthIF : public SynthIF virtual float getParameter(unsigned long) const { return 0.0; } virtual void setParameter(unsigned long, float) {} virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) { return 0; } + + //------------------------- + // Methods for PluginIBase: + //------------------------- + + virtual bool addScheduledControlEvent(unsigned long /*i*/, float /*val*/, unsigned /*frame*/) { return true; } // returns true if event cannot be delivered }; //--------------------------------------------------------- diff --git a/muse2/muse/track.cpp b/muse2/muse/track.cpp index 65580793..cc1e56ac 100644 --- a/muse2/muse/track.cpp +++ b/muse2/muse/track.cpp @@ -217,12 +217,6 @@ void Track::init() _internalSolo = 0; _off = false; _channels = 0; // 1 - mono, 2 - stereo - - _volumeEnCtrl = true; - _volumeEn2Ctrl = true; - _panEnCtrl = true; - _panEn2Ctrl = true; - _selected = false; _height = MusEGlobal::config.trackHeight; _locked = false; @@ -240,11 +234,28 @@ Track::Track(Track::TrackType t) Track::Track(const Track& t, int flags) { - internal_assign(t, flags | ASSIGN_PROPERTIES); - for (int i = 0; i < MAX_CHANNELS; ++i) { - _meter[i] = 0.0; - _peak[i] = 0.0; - } + _type = t.type(); + _name = t.name() + " #"; + for(int i = 2; true; ++i) + { + QString n; + n.setNum(i); + QString s = _name + n; + Track* track = MusEGlobal::song->findTrack(s); + if(track == 0) + { + // Do not call setName here. Audio Input and Output override it and try to set + // Jack ports, which have not been initialized yet here. Must wait until + // Audio Input and Output copy constructors or assign are called. + _name = s; + break; + } + } + internal_assign(t, flags | ASSIGN_PROPERTIES); + for (int i = 0; i < MAX_CHANNELS; ++i) { + _meter[i] = 0.0; + _peak[i] = 0.0; + } } Track::~Track() @@ -275,35 +286,11 @@ void Track::internal_assign(const Track& t, int flags) _internalSolo = t._internalSolo; _off = t._off; _channels = t._channels; - - _volumeEnCtrl = t._volumeEnCtrl; - _volumeEn2Ctrl = t._volumeEn2Ctrl; - _panEnCtrl = t._panEnCtrl; - _panEn2Ctrl = t._panEn2Ctrl; - _selected = t.selected(); _y = t._y; _height = t._height; _comment = t.comment(); - _type = t.type(); _locked = t.locked(); - - _name = t.name() + " #"; - for(int i = 2; true; ++i) - { - QString n; - n.setNum(i); - QString s = _name + n; - Track* track = MusEGlobal::song->findTrack(s); - if(track == 0) - { - // Do not call setName here. Audio Input and Output override it and try to set - // Jack ports, which have not been initialized yet here. Must wait until - // Audio Input and Output copy constructors or assign are called. - _name = s; - break; - } - } } } @@ -366,33 +353,11 @@ void Track::setDefaultName(QString base) void Track::clearRecAutomation(bool clearList) { - _volumeEnCtrl = true; - _volumeEn2Ctrl = true; - _panEnCtrl = true; - _panEn2Ctrl = true; - if(isMidiTrack()) return; - - AudioTrack *t = (AudioTrack*)this; - Pipeline *pl = t->efxPipe(); - PluginI *p; - for(iPluginI i = pl->begin(); i != pl->end(); ++i) - { - p = *i; - if(!p) - continue; - p->enableAllControllers(true); - } - - if(type() == AUDIO_SOFTSYNTH) - { - const SynthI* synth = static_cast<const SynthI*>(this); - SynthIF* sif = synth->sif(); - if(sif) - sif->enableAllControllers(true); - } - + AudioTrack *t = static_cast<AudioTrack*>(this); + // Re-enable all track and plugin controllers, and synth controllers if applicable. + t->enableAllControllers(); if(clearList) t->recEvents()->clear(); } diff --git a/muse2/muse/track.h b/muse2/muse/track.h index 1c1b28f6..664364ba 100644 --- a/muse2/muse/track.h +++ b/muse2/muse/track.h @@ -4,7 +4,7 @@ // $Id: track.h,v 1.39.2.17 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) -// (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) +// (C) Copyright 2011-2013 Tim E. Real (terminator356 on sourceforge) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -38,6 +38,7 @@ #include "ctrl.h" #include "globaldefs.h" #include "cleftypes.h" +#include "controlfifo.h" namespace MusECore { class MPEventList; @@ -47,6 +48,7 @@ class SynthI; class Xml; class DrumMap; class ControlEvent; +struct Port; //--------------------------------------------------------- // Track @@ -88,11 +90,6 @@ class Track { bool _off; int _channels; // 1 - mono, 2 - stereo - bool _volumeEnCtrl; - bool _volumeEn2Ctrl; - bool _panEnCtrl; - bool _panEn2Ctrl; - int _activity; int _lastActivity; double _meter[MAX_CHANNELS]; @@ -127,14 +124,6 @@ class Track { bool locked() const { return _locked; } void setLocked(bool b) { _locked = b; } - bool volumeControllerEnabled() const { return _volumeEnCtrl; } - bool volumeControllerEnabled2() const { return _volumeEn2Ctrl; } - bool panControllerEnabled() const { return _panEnCtrl; } - bool panControllerEnabled2() const { return _panEn2Ctrl; } - void enableVolumeController(bool b) { _volumeEnCtrl = b; } - void enable2VolumeController(bool b) { _volumeEn2Ctrl = b; } - void enablePanController(bool b) { _panEnCtrl = b; } - void enable2PanController(bool b) { _panEn2Ctrl = b; } void clearRecAutomation(bool clearList); const QString& name() const { return _name; } @@ -340,9 +329,17 @@ class MidiTrack : public Track { class AudioTrack : public Track { bool _haveData; // Whether we have data from a previous process call during current cycle. - CtrlListList _controller; + CtrlListList _controller; // Holds all controllers including internal, plugin and synth. + ControlFifo _controlFifo; // For internal controllers like volume and pan. Plugins/synths have their own. CtrlRecList _recEvents; // recorded automation events + unsigned long _controlPorts; + Port* _controls; // For internal controllers like volume and pan. Plugins/synths have their own. + + float _curVolume; + float _curVol1; + float _curVol2; + bool _prefader; // prefader metering std::vector<double> _auxSend; void readAuxSend(Xml& xml); @@ -352,14 +349,29 @@ class AudioTrack : public Track { AutomationType _automationType; Pipeline* _efxPipe; double _gain; + + void initBuffers(); void internal_assign(const Track&, int flags); + void processTrackCtrls(unsigned pos, int trackChans, unsigned nframes, float** buffer); protected: + // Cached audio data for all channels. If prefader is not on, the first two channels + // have volume and pan applied if track is stereo, or the first channel has just + // volume applied if track is mono. float** outBuffers; + // Extra cached audio data. + float** outBuffersExtraMix; + // Just all zeros all the time, so we don't have to clear for silence. + float* audioInSilenceBuf; + // Just a place to connect all unused audio outputs. + float* audioOutDummyBuf; + + // These two are not the same as the number of track channels which is always either 1 (mono) or 2 (stereo): + // Total number of output channels. int _totalOutChannels; + // Total number of input channels. int _totalInChannels; - unsigned bufferPos; virtual bool getData(unsigned, int, unsigned, float**); SndFileR _recFile; Fifo fifo; // fifo -> _recFile @@ -396,6 +408,10 @@ class AudioTrack : public Track { void setRecFile(SndFileR sf) { _recFile = sf; } CtrlListList* controller() { return &_controller; } + // For setting/getting the _controls 'port' values. + unsigned long parameters() const { return _controlPorts; } + void setParam(unsigned long i, float val); + float param(unsigned long i) const; virtual void setChannels(int n); virtual void setTotalOutChannels(int num); @@ -434,7 +450,10 @@ class AudioTrack : public Track { Pipeline* efxPipe() { return _efxPipe; } void deleteAllEfxGuis(); void clearEfxList(); + // Removes any existing plugin and inserts plugin into effects rack, and calls setupPlugin. void addPlugin(PluginI* plugin, int idx); + // Assigns valid ID and track to plugin, and creates controllers for plugin. + void setupPlugin(PluginI* plugin, int idx); double pluginCtrlVal(int ctlID) const; void setPluginCtrlVal(int param, double val); @@ -452,8 +471,10 @@ class AudioTrack : public Track { void processAutomationEvents(); CtrlRecList* recEvents() { return &_recEvents; } bool addScheduledControlEvent(int track_ctrl_id, float val, unsigned frame); // return true if event cannot be delivered - void enableController(int track_ctrl_id, bool en); - void controllersEnabled(int track_ctrl_id, bool* en1, bool* en2) const; + void enableController(int track_ctrl_id, bool en); + bool controllerEnabled(int track_ctrl_id) const; + // Enable all track and plugin controllers, and synth controllers if applicable. + void enableAllControllers(); void recordAutomation(int n, double v); void startAutoRecord(int, double); void stopAutoRecord(int, double); @@ -507,7 +528,6 @@ class AudioOutput : public AudioTrack { float* buffer1[MAX_CHANNELS]; unsigned long _nframes; static bool _isVisible; - float* _monitorBuffer[MAX_CHANNELS]; void internal_assign(const Track& t, int flags); public: @@ -530,7 +550,6 @@ class AudioOutput : public AudioTrack { void silence(unsigned); virtual bool canRecord() const { return true; } - float** monitorBuffer() { return _monitorBuffer; } static void setVisible(bool t) { _isVisible = t; } static bool visible() { return _isVisible; } virtual int height() const; diff --git a/muse2/muse/vst.h b/muse2/muse/vst.h index 1143d56e..5d759af7 100644 --- a/muse2/muse/vst.h +++ b/muse2/muse/vst.h @@ -99,6 +99,12 @@ class VstSynthIF : public SynthIF virtual float getParameter(unsigned long idx) const; virtual void setParameter(unsigned long idx, float value); virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) { return 0; } + + //------------------------- + // Methods for PluginIBase: + //------------------------- + + virtual bool addScheduledControlEvent(unsigned long /*i*/, float /*val*/, unsigned /*frame*/) { return true; } // returns true if event cannot be delivered }; } // namespace MusECore diff --git a/muse2/muse/vst_native.cpp b/muse2/muse/vst_native.cpp index de206313..063d0895 100644 --- a/muse2/muse/vst_native.cpp +++ b/muse2/muse/vst_native.cpp @@ -3,7 +3,7 @@ // Linux Music Editor // // vst_native.cpp -// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2012-2013 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 @@ -598,7 +598,7 @@ void VstNativeSynth::incInstances(int val) // instantiate //--------------------------------------------------------- -AEffect* VstNativeSynth::instantiate() +AEffect* VstNativeSynth::instantiate(VstNativeSynthIF* sif) { int inst_num = _instances; inst_num++; @@ -680,6 +680,7 @@ AEffect* VstNativeSynth::instantiate() else if(MusEGlobal::debugMsg) fprintf(stderr, "Plugin supports processReplacing\n"); + plugin->user = sif; plugin->dispatcher(plugin, effOpen, 0, 0, NULL, 0); // "2 = VST2.x, older versions return 0". Observed 2400 on all the ones tested so far. @@ -693,8 +694,7 @@ AEffect* VstNativeSynth::instantiate() ++_instances; _handle = hnd; - - plugin->dispatcher(plugin, effOpen, 0, 0, NULL, 0); + //plugin->dispatcher(plugin, effSetProgram, 0, 0, NULL, 0.0f); // REMOVE Tim. Or keep? return plugin; @@ -723,8 +723,10 @@ SynthIF* VstNativeSynth::createSIF(SynthI* s) VstNativeSynthIF::VstNativeSynthIF(SynthI* s) : SynthIF(s) { _guiVisible = false; + _gw = NULL; _synth = NULL; _plugin = NULL; + _active = false; _editor = NULL; _inProcess = false; _controls = NULL; @@ -767,6 +769,9 @@ VstNativeSynthIF::~VstNativeSynthIF() if(_controls) delete[] _controls; + + if(_gw) + delete[] _gw; } //--------------------------------------------------------- @@ -776,10 +781,9 @@ VstNativeSynthIF::~VstNativeSynthIF() bool VstNativeSynthIF::init(Synth* s) { _synth = (VstNativeSynth*)s; - _plugin = _synth->instantiate(); + _plugin = _synth->instantiate(this); if(!_plugin) return false; - _plugin->user = this; queryPrograms(); @@ -795,7 +799,13 @@ bool VstNativeSynthIF::init(Synth* s) fprintf(stderr, "ERROR: VstNativeSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } - memset(_audioOutBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + _audioOutBuffers[k][q] = MusEGlobal::denormalBias; + } + else + memset(_audioOutBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); } } @@ -811,7 +821,13 @@ bool VstNativeSynthIF::init(Synth* s) fprintf(stderr, "ERROR: VstNativeSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } - memset(_audioInBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + _audioInBuffers[k][q] = MusEGlobal::denormalBias; + } + else + memset(_audioInBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); _iUsedIdx.push_back(false); // Start out with all false. } @@ -821,20 +837,28 @@ bool VstNativeSynthIF::init(Synth* s) fprintf(stderr, "ERROR: VstNativeSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } - memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); + if(MusEGlobal::config.useDenormalBias) + { + for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) + _audioInSilenceBuf[q] = MusEGlobal::denormalBias; + } + else + memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); } + _controls = NULL; + _gw = NULL; unsigned long controlPorts = _synth->inControls(); if(controlPorts != 0) + { _controls = new Port[controlPorts]; - else - _controls = NULL; - - //_synth->midiCtl2PortMap.clear(); - //_synth->port2MidiCtlMap.clear(); + _gw = new VstNativeGuiWidgets[controlPorts]; + } for(unsigned long i = 0; i < controlPorts; ++i) { + _gw[i].pressed = false; + _controls[i].idx = i; //float val; // TODO //ladspaDefaultValue(ld, k, &val); // FIXME TODO @@ -842,7 +866,6 @@ bool VstNativeSynthIF::init(Synth* s) _controls[i].val = val; _controls[i].tmpVal = val; _controls[i].enCtrl = true; - _controls[i].en2Ctrl = true; // Support a special block for synth ladspa controllers. // Put the ID at a special block after plugins (far after). @@ -855,7 +878,7 @@ bool VstNativeSynthIF::init(Synth* s) float min = 0.0, max = 1.0; CtrlList* cl; - CtrlListList* cll = ((MusECore::AudioTrack*)synti)->controller(); + CtrlListList* cll = track()->controller(); iCtrlList icl = cll->find(id); if (icl == cll->end()) { @@ -941,14 +964,15 @@ VstIntPtr VstNativeSynthIF::hostCallback(VstInt32 opcode, VstInt32 index, VstInt case audioMasterCurrentId: // returns the unique id of a plug that's currently // loading - return 0; + ///return 0; + return _plugin->uniqueID; case audioMasterIdle: // call application idle routine (this will // call effEditIdle for all open editors too) //_plugin->updateParamValues(false); //_plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f); - idleEditor(); + ///idleEditor(); // REMOVE Tim. Or keep. return 0; case audioMasterGetTime: @@ -958,8 +982,12 @@ VstIntPtr VstNativeSynthIF::hostCallback(VstInt32 opcode, VstInt32 index, VstInt // (see valid masks above), as some items may require extensive // conversions + // FIXME TODO: Optimizations: This may be called many times in one process call + // due to our multi-run slices. Some of the (costly) info will be redundant. + // So try to add some flag to try to only call some or all of this once per cycle. + #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::hostCallback master time: valid: nanos:%d ppqpos:%d tempo:%d bars:%d cyclepos:%d sig:%d smpte:%d clock:%d\n", // REMOVE Tim. + fprintf(stderr, "VstNativeSynthIF::hostCallback master time: valid: nanos:%d ppqpos:%d tempo:%d bars:%d cyclepos:%d sig:%d smpte:%d clock:%d\n", (bool)(value & kVstNanosValid), (bool)(value & kVstPpqPosValid), (bool)(value & kVstTempoValid), @@ -1249,9 +1277,35 @@ void VstNativeSynthIF::idleEditor() fprintf(stderr, "VstNativeSynthIF::idleEditor %p\n", this); #endif - _plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f); - if(_editor) - _editor->update(); + // REMOVE Tim. Or keep. + //_plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f); + //if(_editor) + // _editor->update(); +} + +//--------------------------------------------------------- +// guiHeartBeat +//--------------------------------------------------------- + +void VstNativeSynthIF::guiHeartBeat() +{ +#ifdef VST_NATIVE_DEBUG + fprintf(stderr, "VstNativeSynthIF::guiHeartBeat %p\n", this); +#endif + + // REMOVE Tim. Or keep. + if(_plugin && _active) + { +//#ifdef VST_FORCE_DEPRECATED // REMOVE Tim. Or keep + //_plugin->dispatcher(_plugin, effIdle, 0, 0, NULL, 0.0f); +//#endif + if(_guiVisible) + { + _plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f); + if(_editor) + _editor->update(); + } + } } //--------------------------------------------------------- @@ -1330,6 +1384,66 @@ void VstNativeSynthIF::showNativeGui(bool v) } //--------------------------------------------------------- +// getGeometry +//--------------------------------------------------------- + +void VstNativeSynthIF::getGeometry(int*x, int*y, int*w, int*h) const +{ + if(!_gui) + { + *x=0;*y=0;*w=0;*h=0; + return; + } + + *x = _gui->x(); + *y = _gui->y(); + *w = _gui->width(); + *h = _gui->height(); +} + +//--------------------------------------------------------- +// setGeometry +//--------------------------------------------------------- + +void VstNativeSynthIF::setGeometry(int x, int y, int w, int h) +{ + if(!_gui) + return; + + _gui->setGeometry(x, y, w, h); +} + +//--------------------------------------------------------- +// editorOpened +//--------------------------------------------------------- + +void VstNativeSynthIF::getNativeGeometry(int*x, int*y, int*w, int*h) const +{ + if(!_editor) + { + *x=0;*y=0;*w=0;*h=0; + return; + } + + *x = _editor->x(); + *y = _editor->y(); + *w = _editor->width(); + *h = _editor->height(); +} + +//--------------------------------------------------------- +// editorOpened +//--------------------------------------------------------- + +void VstNativeSynthIF::setNativeGeometry(int x, int y, int w, int h) +{ + if(!_editor) + return; + + _editor->setGeometry(x, y, w, h); +} + +//--------------------------------------------------------- // editorOpened //--------------------------------------------------------- @@ -1380,7 +1494,6 @@ bool VstNativeSynthIF::hasNativeGui() const int VstNativeSynthIF::channels() const { - //return _plugin->numOutputs; return _plugin->numOutputs > MAX_CHANNELS ? MAX_CHANNELS : _plugin->numOutputs ; } @@ -1614,7 +1727,6 @@ float VstNativeSynthIF::getParameter(unsigned long idx) const void VstNativeSynthIF::setParameter(unsigned long idx, float value) { - //_plugin->setParameter(_plugin, idx, value); addScheduledControlEvent(idx, value, MusEGlobal::audio->curFrame()); } @@ -1624,53 +1736,44 @@ void VstNativeSynthIF::setParameter(unsigned long idx, float value) void VstNativeSynthIF::guiAutomationBegin(unsigned long param_idx) { - AutomationType at = AUTO_OFF; - MusECore::AudioTrack* t = track(); - if(t) - at = t->automationType(); - - // FIXME TODO: This stuff should probably be sent as control FIFO events. - // And probably not safe - accessing from gui and audio threads... - - if (at == AUTO_READ || at == AUTO_TOUCH || at == AUTO_WRITE) - enableController(param_idx, false); - + _gw[param_idx].pressed = true; + AudioTrack* t = track(); int plug_id = id(); + if(t && plug_id != -1) + { + plug_id = genACnum(plug_id, param_idx); - if(plug_id == -1) - return; - - plug_id = MusECore::genACnum(plug_id, param_idx); - - //if(params[param].type == GuiParam::GUI_SLIDER) - //{ - //double val = ((Slider*)params[param].actuator)->value(); - float val = param(param_idx); - // FIXME TODO: - //if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) - // val = pow(10.0, val/20.0); - //else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) - // val = rint(val); - //plugin->setParam(param, val); - //((DoubleLabel*)params[param].label)->setValue(val); - - if(t) - { - t->setPluginCtrlVal(plug_id, val); - t->startAutoRecord(plug_id, val); - } - //} -// else if(params[param].type == GuiParam::GUI_SWITCH) -// { -// float val = (float)((CheckBox*)params[param].actuator)->isChecked(); -// plugin->setParam(param, val); -// -// if(t) -// { -// t->setPluginCtrlVal(plug_id, val); -// t->startAutoRecord(plug_id, val); -// } -// } + //if(params[param].type == GuiParam::GUI_SLIDER) + //{ + //double val = ((Slider*)params[param].actuator)->value(); + float val = param(param_idx); + // FIXME TODO: + //if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) + // val = pow(10.0, val/20.0); + //else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) + // val = rint(val); + //plugin->setParam(param, val); + //((DoubleLabel*)params[param].label)->setValue(val); + + //if(t) + //{ + t->startAutoRecord(plug_id, val); + t->setPluginCtrlVal(plug_id, val); + //} + //} + // else if(params[param].type == GuiParam::GUI_SWITCH) + // { + // float val = (float)((CheckBox*)params[param].actuator)->isChecked(); + // plugin->setParam(param, val); + // + // //if(t) + // //{ + // t->startAutoRecord(plug_id, val); + // t->setPluginCtrlVal(plug_id, val); + // //} + // } + } + enableController(param_idx, false); } //--------------------------------------------------------- @@ -1680,40 +1783,39 @@ void VstNativeSynthIF::guiAutomationBegin(unsigned long param_idx) void VstNativeSynthIF::guiAutomationEnd(unsigned long param_idx) { AutomationType at = AUTO_OFF; - MusECore::AudioTrack* t = track(); + AudioTrack* t = track(); if(t) at = t->automationType(); - // FIXME TODO: This stuff should probably be sent as control FIFO events. - // And probably not safe - accessing from gui and audio threads... - + int plug_id = id(); + if(t && plug_id != -1) + { + plug_id = genACnum(plug_id, param_idx); + + //if(params[param].type == GuiParam::GUI_SLIDER) + //{ + //double val = ((Slider*)params[param].actuator)->value(); + float val = param(param_idx); + // FIXME TODO: + //if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) + // val = pow(10.0, val/20.0); + //else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) + // val = rint(val); + t->stopAutoRecord(plug_id, val); + //} + } + // Special for switch - don't enable controller until transport stopped. if ((at == AUTO_OFF) || - (at == AUTO_READ) || (at == AUTO_TOUCH)) // && (params[param].type != GuiParam::GUI_SWITCH || // FIXME TODO // !MusEGlobal::audio->isPlaying()) ) ) enableController(param_idx, true); - - int plug_id = id(); - if(!t || plug_id == -1) - return; - plug_id = MusECore::genACnum(plug_id, param_idx); - - //if(params[param].type == GuiParam::GUI_SLIDER) - //{ - //double val = ((Slider*)params[param].actuator)->value(); - float val = param(param_idx); - // FIXME TODO: - //if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) - // val = pow(10.0, val/20.0); - //else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) - // val = rint(val); - t->stopAutoRecord(plug_id, val); - //} + + _gw[param_idx].pressed = false; } - + //--------------------------------------------------------- -// guiControlEventHandler +// guiControlChanged //--------------------------------------------------------- int VstNativeSynthIF::guiControlChanged(unsigned long param_idx, float value) @@ -1728,37 +1830,29 @@ int VstNativeSynthIF::guiControlChanged(unsigned long param_idx, float value) return 0; } + // Record automation: + // Take care of this immediately rather than in the fifo processing. + if(id() != -1) + { + unsigned long pid = genACnum(id(), param_idx); + synti->recordAutomation(pid, value); + } + + // Schedules a timed control change: ControlEvent ce; - ce.unique = false; // Not used for native vst. - ce.fromGui = true; // It came form the plugin's own GUI. + ce.unique = false; // Not used for native vst. + ce.fromGui = true; // It came from the plugin's own GUI. ce.idx = param_idx; ce.value = value; - // don't use timestamp(), because it's circular, which is making it impossible to deal + // Don't use timestamp(), because it's circular, which is making it impossible to deal // with 'modulo' events which slip in 'under the wire' before processing the ring buffers. ce.frame = MusEGlobal::audio->curFrame(); if(_controlFifo.put(ce)) - { fprintf(stderr, "VstNativeSynthIF::guiControlChanged: fifo overflow: in control number:%lu\n", param_idx); - } - // FIXME TODO: This stuff should probably be sent as control FIFO events. - // And probably not safe - accessing from gui and audio threads... + enableController(param_idx, false); - // Record automation: - // Take care of this immediately rather than in the fifo processing. - if(id() != -1) - { - unsigned long pid = genACnum(id(), param_idx); - AutomationType at = synti->automationType(); - - if ((at == AUTO_WRITE) || - (at == AUTO_TOUCH && MusEGlobal::audio->isPlaying())) - enableController(param_idx, false); - - synti->recordAutomation(pid, value); - } - return 0; } @@ -1844,7 +1938,7 @@ void VstNativeSynthIF::setVstEvent(VstMidiEvent* event, int a, int b, int c, int // getData //--------------------------------------------------------- -bool VstNativeSynthIF::processEvent(const MusECore::MidiPlayEvent& e, VstMidiEvent* event) +bool VstNativeSynthIF::processEvent(const MidiPlayEvent& e, VstMidiEvent* event) { int type = e.type(); int chn = e.channel(); @@ -1857,22 +1951,22 @@ bool VstNativeSynthIF::processEvent(const MusECore::MidiPlayEvent& e, VstMidiEve switch(type) { - case MusECore::ME_NOTEON: + case ME_NOTEON: #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_NOTEON\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_NOTEON\n"); #endif setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f); break; - case MusECore::ME_NOTEOFF: + case ME_NOTEOFF: #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_NOTEOFF\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_NOTEOFF\n"); #endif setVstEvent(event, (type | chn) & 0xff, a & 0x7f, 0); break; - case MusECore::ME_PROGRAM: + case ME_PROGRAM: { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_PROGRAM\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_PROGRAM\n"); #endif int bankH = (a >> 16) & 0xff; @@ -1885,19 +1979,19 @@ bool VstNativeSynthIF::processEvent(const MusECore::MidiPlayEvent& e, VstMidiEve return false; // Event pointer not filled. Return false. } break; - case MusECore::ME_CONTROLLER: + case ME_CONTROLLER: { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER\n"); #endif if((a == 0) || (a == 32)) return false; - if(a == MusECore::CTRL_PROGRAM) + if(a == CTRL_PROGRAM) { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_PROGRAM\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PROGRAM\n"); #endif int bankH = (b >> 16) & 0xff; @@ -1911,36 +2005,36 @@ bool VstNativeSynthIF::processEvent(const MusECore::MidiPlayEvent& e, VstMidiEve return false; // Event pointer not filled. Return false. } - if(a == MusECore::CTRL_PITCH) + if(a == CTRL_PITCH) { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_PITCH\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PITCH\n"); #endif int v = b + 8192; setVstEvent(event, (type | chn) & 0xff, v & 0x7f, (v >> 7) & 0x7f); return true; } - if(a == MusECore::CTRL_AFTERTOUCH) + if(a == CTRL_AFTERTOUCH) { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_AFTERTOUCH\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_AFTERTOUCH\n"); #endif setVstEvent(event, (type | chn) & 0xff, b & 0x7f); return true; } - if((a | 0xff) == MusECore::CTRL_POLYAFTER) + if((a | 0xff) == CTRL_POLYAFTER) { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is MusECore::CTRL_POLYAFTER\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_POLYAFTER\n"); #endif setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f); return true; } #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_CONTROLLER, dataA is:%d\n", a); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER, dataA is:%d\n", a); #endif // Regular controller. Pass it on. @@ -1952,14 +2046,14 @@ bool VstNativeSynthIF::processEvent(const MusECore::MidiPlayEvent& e, VstMidiEve // // const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; // -// MusECore::ciMidiCtl2LadspaPort ip = synth->midiCtl2PortMap.find(a); +// ciMidiCtl2LadspaPort ip = synth->midiCtl2PortMap.find(a); // // Is it just a regular midi controller, not mapped to a LADSPA port (either by the plugin or by us)? // // NOTE: There's no way to tell which of these controllers is supported by the plugin. // // For example sustain footpedal or pitch bend may be supported, but not mapped to any LADSPA port. // if(ip == synth->midiCtl2PortMap.end()) // { // int ctlnum = a; -// if(MusECore::midiControllerType(a) != MusECore::MidiController::Controller7) +// if(midiControllerType(a) != MidiController::Controller7) // return false; // Event pointer not filled. Return false. // else // { @@ -1994,7 +2088,7 @@ bool VstNativeSynthIF::processEvent(const MusECore::MidiPlayEvent& e, VstMidiEve // return false; // // // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. -// ctlnum = k + (MusECore::CTRL_NRPN14_OFFSET + 0x2000); +// ctlnum = k + (CTRL_NRPN14_OFFSET + 0x2000); // } // else // { @@ -2020,7 +2114,7 @@ bool VstNativeSynthIF::processEvent(const MusECore::MidiPlayEvent& e, VstMidiEve // else // if(DSSI_IS_NRPN(ctlnum)) // { -// ctlnum = DSSI_NRPN_NUMBER(c) + MusECore::CTRL_NRPN14_OFFSET; +// ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; // // #ifdef VST_NATIVE_DEBUG // fprintf(stderr, "VstNativeSynthIF::processEvent is NRPN ctlnum:%x(h) %d(d)\n", ctlnum, ctlnum); @@ -2047,22 +2141,22 @@ bool VstNativeSynthIF::processEvent(const MusECore::MidiPlayEvent& e, VstMidiEve // return false; } break; - case MusECore::ME_PITCHBEND: + case ME_PITCHBEND: { int v = a + 8192; setVstEvent(event, (type | chn) & 0xff, v & 0x7f, (v >> 7) & 0x7f); } break; - case MusECore::ME_AFTERTOUCH: + case ME_AFTERTOUCH: setVstEvent(event, (type | chn) & 0xff, a & 0x7f); break; - case MusECore::ME_POLYAFTER: + case ME_POLYAFTER: setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f); break; - case MusECore::ME_SYSEX: + case ME_SYSEX: { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_SYSEX\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_SYSEX\n"); #endif const unsigned char* data = e.data(); @@ -2109,7 +2203,7 @@ bool VstNativeSynthIF::processEvent(const MusECore::MidiPlayEvent& e, VstMidiEve if (QString((const char*)e.data()).startsWith("PARAMSAVE")) { #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::processEvent midi event is MusECore::ME_SYSEX PARAMSAVE\n"); + fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_SYSEX PARAMSAVE\n"); #endif unsigned long dlen = e.len() - 9; // Minus "PARAMSAVE" @@ -2170,7 +2264,7 @@ bool VstNativeSynthIF::processEvent(const MusECore::MidiPlayEvent& e, VstMidiEve //len += 2; // NOTE: There is a limit on the size of a sysex. Got this: - // "VstNativeSynthIF::processEvent midi event is MusECore::ME_SYSEX" + // "VstNativeSynthIF::processEvent midi event is ME_SYSEX" // "WARNING: MIDI event of type ? decoded to 367 bytes, discarding" // That might be ALSA doing that. // snd_seq_ev_clear(event); @@ -2193,43 +2287,48 @@ bool VstNativeSynthIF::processEvent(const MusECore::MidiPlayEvent& e, VstMidiEve //--------------------------------------------------------- // getData +// If ports is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- iMPEvent VstNativeSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent start_event, unsigned pos, int ports, unsigned nframes, float** buffer) { - // We may not be using nevents all at once - this will be just the maximum. - unsigned long nevents = el->size() + synti->eventFifo.getSize(); - VstMidiEvent events[nevents]; - char evbuf[sizeof(VstMidiEvent*) * nevents + sizeof(VstEvents)]; + // We may not be using ev_buf_sz all at once - this will be just the maximum. + const unsigned long ev_buf_sz = el->size() + synti->eventFifo.getSize(); + VstMidiEvent events[ev_buf_sz]; + char evbuf[sizeof(VstMidiEvent*) * ev_buf_sz + sizeof(VstEvents)]; VstEvents *vst_events = (VstEvents*)evbuf; vst_events->numEvents = 0; vst_events->reserved = 0; - int frameOffset = MusEGlobal::audio->getFrameOffset(); - unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); + const int frameOffset = MusEGlobal::audio->getFrameOffset(); + const unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); #ifdef VST_NATIVE_DEBUG_PROCESS - fprintf(stderr, "VstNativeSynthIF::getData: pos:%u ports:%d nframes:%u syncFrame:%lu nevents:%lu\n", pos, ports, nframes, syncFrame, nevents); + fprintf(stderr, "VstNativeSynthIF::getData: pos:%u ports:%d nframes:%u syncFrame:%lu ev_buf_sz:%lu\n", pos, ports, nframes, syncFrame, ev_buf_sz); #endif - unsigned long nop, k; - nop = ((unsigned long) ports) > _synth->outPorts() ? _synth->outPorts() : ((unsigned long) ports); + const unsigned long nop = ((unsigned long) ports) > _synth->outPorts() ? _synth->outPorts() : ((unsigned long) ports); unsigned long sample = 0; // I read that some plugins do not like changing sample run length (compressors etc). Some are OK with it. // TODO: In order to support this effectively, must be user selectable, per-plugin. ENABLED for now. - const bool usefixedrate = false; - unsigned long fixedsize = nframes; + const bool usefixedrate = false; // For now, the fixed size is clamped to the audio buffer size. // TODO: We could later add slower processing over several cycles - // so that users can select a small audio period but a larger control period. - if(fixedsize > nframes) - fixedsize = nframes; - - unsigned long min_per = MusEGlobal::config.minControlProcessPeriod; // Must be power of 2 ! - if(min_per > nframes) - min_per = nframes; + const unsigned long min_per = (usefixedrate || MusEGlobal::config.minControlProcessPeriod > nframes) ? nframes : MusEGlobal::config.minControlProcessPeriod; + const unsigned long min_per_mask = min_per-1; // min_per must be power of 2 + + AudioTrack* atrack = track(); + const AutomationType at = atrack->automationType(); + const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; + const unsigned long in_ctrls = _synth->inControls(); + CtrlListList* cll = atrack->controller(); + ciCtrlList icl_first; + const int plug_id = id(); + if(plug_id != -1 && ports != 0) // Don't bother if not 'running'. + icl_first = cll->lower_bound(genACnum(plug_id, 0)); // Inform the host callback we are in the audio thread. _inProcess = true; @@ -2239,71 +2338,74 @@ iMPEvent VstNativeSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent s #endif // Handle inputs... - if(!((MusECore::AudioTrack*)synti)->noInRoute()) + if(ports != 0) // Don't bother if not 'running'. { - RouteList* irl = ((MusECore::AudioTrack*)synti)->inRoutes(); - iRoute i = irl->begin(); - if(!i->track->isMidiTrack()) + if(!atrack->noInRoute()) { - int ch = i->channel == -1 ? 0 : i->channel; - int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; - int chs = i->channels == -1 ? 0 : i->channels; - - if((unsigned)ch < _synth->inPorts() && (unsigned)(ch + chs) <= _synth->inPorts()) + RouteList* irl = atrack->inRoutes(); + iRoute i = irl->begin(); + if(!i->track->isMidiTrack()) { - int h = remch + chs; - for(int j = remch; j < h; ++j) - _iUsedIdx[j] = true; + const int ch = i->channel == -1 ? 0 : i->channel; + const int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; + const int chs = i->channels == -1 ? 0 : i->channels; + + if((unsigned)ch < _synth->inPorts() && (unsigned)(ch + chs) <= _synth->inPorts()) + { + const int h = remch + chs; + for(int j = remch; j < h; ++j) + _iUsedIdx[j] = true; - ((MusECore::AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]); + ((AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]); + } } - } - ++i; - for(; i != irl->end(); ++i) - { - if(i->track->isMidiTrack()) - continue; + ++i; + for(; i != irl->end(); ++i) + { + if(i->track->isMidiTrack()) + continue; - int ch = i->channel == -1 ? 0 : i->channel; - int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; - int chs = i->channels == -1 ? 0 : i->channels; + const int ch = i->channel == -1 ? 0 : i->channel; + const int remch = i->remoteChannel == -1 ? 0 : i->remoteChannel; + const int chs = i->channels == -1 ? 0 : i->channels; - if((unsigned)ch < _synth->inPorts() && (unsigned)(ch + chs) <= _synth->inPorts()) - { - bool u1 = _iUsedIdx[remch]; - if(chs >= 2) + if((unsigned)ch < _synth->inPorts() && (unsigned)(ch + chs) <= _synth->inPorts()) { - bool u2 = _iUsedIdx[remch + 1]; - if(u1 && u2) - ((MusECore::AudioTrack*)i->track)->addData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]); - else - if(!u1 && !u2) - ((MusECore::AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]); - else + const bool u1 = _iUsedIdx[remch]; + if(chs >= 2) { - if(u1) - ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch, 1, nframes, &_audioInBuffers[remch]); + const bool u2 = _iUsedIdx[remch + 1]; + if(u1 && u2) + ((AudioTrack*)i->track)->addData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]); else - ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch, 1, nframes, &_audioInBuffers[remch]); - - if(u2) - ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch + 1, 1, nframes, &_audioInBuffers[remch + 1]); + if(!u1 && !u2) + ((AudioTrack*)i->track)->copyData(pos, chs, ch, -1, nframes, &_audioInBuffers[remch]); else - ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch + 1, 1, nframes, &_audioInBuffers[remch + 1]); + { + if(u1) + ((AudioTrack*)i->track)->addData(pos, 1, ch, 1, nframes, &_audioInBuffers[remch]); + else + ((AudioTrack*)i->track)->copyData(pos, 1, ch, 1, nframes, &_audioInBuffers[remch]); + + if(u2) + ((AudioTrack*)i->track)->addData(pos, 1, ch + 1, 1, nframes, &_audioInBuffers[remch + 1]); + else + ((AudioTrack*)i->track)->copyData(pos, 1, ch + 1, 1, nframes, &_audioInBuffers[remch + 1]); + } + } + else + { + if(u1) + ((AudioTrack*)i->track)->addData(pos, 1, ch, -1, nframes, &_audioInBuffers[remch]); + else + ((AudioTrack*)i->track)->copyData(pos, 1, ch, -1, nframes, &_audioInBuffers[remch]); } - } - else - { - if(u1) - ((MusECore::AudioTrack*)i->track)->addData(pos, 1, ch, -1, nframes, &_audioInBuffers[remch]); - else - ((MusECore::AudioTrack*)i->track)->copyData(pos, 1, ch, -1, nframes, &_audioInBuffers[remch]); - } - int h = remch + chs; - for(int j = remch; j < h; ++j) - _iUsedIdx[j] = true; + const int h = remch + chs; + for(int j = remch; j < h; ++j) + _iUsedIdx[j] = true; + } } } } @@ -2312,9 +2414,11 @@ iMPEvent VstNativeSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent s fprintf(stderr, "VstNativeSynthIF::getData: Processing automation control values...\n"); #endif + int cur_slice = 0; while(sample < nframes) { - unsigned long nsamp = usefixedrate ? fixedsize : nframes - sample; + unsigned long nsamp = nframes - sample; + const unsigned long slice_frame = pos + sample; // // Process automation control values, while also determining the maximum acceptable @@ -2322,51 +2426,62 @@ iMPEvent VstNativeSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent s // from there, but this section determines where the next highest maximum frame // absolutely needs to be for smooth playback of the controller value stream... // - if(id() != -1) + if(ports != 0) // Don't bother if not 'running'. { - unsigned long frame = pos + sample; - AutomationType at = AUTO_OFF; - at = synti->automationType(); - bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; - AudioTrack* track = (static_cast<AudioTrack*>(synti)); - int nextFrame; - const unsigned long in_ctrls = _synth->inControls(); + ciCtrlList icl = icl_first; for(unsigned long k = 0; k < in_ctrls; ++k) { - const float v = track->controller()->value(genACnum(id(), k), frame, - no_auto || !_controls[k].enCtrl || !_controls[k].en2Ctrl, - &nextFrame); - if(_controls[k].val != v) + CtrlList* cl = (cll && plug_id != -1 && icl != cll->end()) ? icl->second : NULL; + CtrlInterpolate& ci = _controls[k].interp; + // Always refresh the interpolate struct at first, since things may have changed. + // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. + if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && + (slice_frame < (unsigned long)ci.sFrame || (ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame)) ) ) { - _controls[k].val = v; -#ifndef VST_VESTIGE_SUPPORT - if(dispatch(effCanBeAutomated, k, 0, NULL, 0.0f) == 1) + if(cl && plug_id != -1 && (unsigned long)cl->id() == genACnum(plug_id, k)) { -#endif - if(_plugin->getParameter(_plugin, k) != v) - _plugin->setParameter(_plugin, k, v); -#ifndef VST_VESTIGE_SUPPORT + cl->getInterpolation(slice_frame, no_auto || !_controls[k].enCtrl, &ci); + if(icl != cll->end()) + ++icl; } - #ifdef VST_NATIVE_DEBUG else - fprintf(stderr, "VstNativeSynthIF::getData %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), k); - #endif -#endif + { + // No matching controller, or end. Just copy the current value into the interpolator. + // Keep the current icl iterator, because since they are sorted by frames, + // if the IDs didn't match it means we can just let k catch up with icl. + ci.sFrame = 0; + ci.eFrame = -1; + ci.sVal = _controls[k].val; + ci.eVal = ci.sVal; + ci.doInterp = false; + ci.eStop = false; + } } - -#ifdef VST_NATIVE_DEBUG_PROCESS - fprintf(stderr, "VstNativeSynthIF::getData k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, frame, nextFrame, nsamp); -#endif - if(MusEGlobal::audio->isPlaying() && !usefixedrate && nextFrame != -1) + else { - // Returned value of nextFrame can be zero meaning caller replaces with some (constant) value. - unsigned long samps = (unsigned long)nextFrame; - if(samps > frame + min_per) + if(ci.eStop && ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. { - unsigned long diff = samps - frame; - unsigned long mask = min_per-1; // min_per must be power of 2 - samps = diff & ~mask; - if((diff & mask) != 0) + // Clear the stop condition and set up the interp struct appropriately as an endless value. + ci.sFrame = 0; //ci->eFrame; + ci.eFrame = -1; + ci.sVal = ci.eVal; + ci.doInterp = false; + ci.eStop = false; + } + if(cl && cll && icl != cll->end()) + ++icl; + } + + if(!usefixedrate && MusEGlobal::audio->isPlaying()) + { + unsigned long samps = nsamp; + if(ci.eFrame != -1) + samps = (unsigned long)ci.eFrame - slice_frame; + + if(!ci.doInterp && samps > min_per) + { + samps &= ~min_per_mask; + if((samps & min_per_mask) != 0) samps += min_per; } else @@ -2374,12 +2489,42 @@ iMPEvent VstNativeSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent s if(samps < nsamp) nsamp = samps; + + } + + float new_val; + if(ci.doInterp && cl) + new_val = cl->interpolate(MusEGlobal::audio->isPlaying() ? slice_frame : pos, ci); + else + new_val = ci.sVal; + if(_controls[k].val != new_val) + { + _controls[k].val = new_val; +#ifndef VST_VESTIGE_SUPPORT + if(dispatch(effCanBeAutomated, k, 0, NULL, 0.0f) == 1) + { +#endif + if(_plugin->getParameter(_plugin, k) != new_val) + _plugin->setParameter(_plugin, k, new_val); +#ifndef VST_VESTIGE_SUPPORT + } + #ifdef VST_NATIVE_DEBUG + else + fprintf(stderr, "VstNativeSynthIF::getData %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), k); + #endif +#endif } + +#ifdef VST_NATIVE_DEBUG_PROCESS + fprintf(stderr, "VstNativeSynthIF::getData k:%lu sample:%lu frame:%lu ci.eFrame:%d nsamp:%lu \n", k, sample, frame, ci.eFrame, nsamp); +#endif + } + } + #ifdef VST_NATIVE_DEBUG_PROCESS fprintf(stderr, "VstNativeSynthIF::getData sample:%lu nsamp:%lu\n", sample, nsamp); #endif - } bool found = false; unsigned long frame = 0; @@ -2409,218 +2554,231 @@ iMPEvent VstNativeSynthIF::getData(MidiPort* /*mp*/, MPEventList* el, iMPEvent s continue; } - if(evframe >= nframes // Next events are for a later period. - || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) - || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close. - || (usefixedrate && found && v.unique && v.idx == index)) // Special for dssi-vst: Fixed rate and must reply to all. + if(evframe >= nframes // Next events are for a later period. + || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) + || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close. + || (usefixedrate && found && v.unique && v.idx == index)) // Special for dssi-vst: Fixed rate and must reply to all. break; _controlFifo.remove(); // Done with the ring buffer's item. Remove it. - if(v.idx >= _synth->inControls()) // Sanity check. + if(v.idx >= in_ctrls) // Sanity check. break; + found = true; frame = evframe; index = v.idx; - //if(_controls[v.idx].val != v.value) // Mm nah, force it always. REMOVE Tim. - //{ - _controls[v.idx].val = v.value; -#ifndef VST_VESTIGE_SUPPORT - if(dispatch(effCanBeAutomated, v.idx, 0, NULL, 0.0f) == 1) - { -#endif - if(v.value != _plugin->getParameter(_plugin, v.idx)) - _plugin->setParameter(_plugin, v.idx, v.value); -#ifndef VST_VESTIGE_SUPPORT - } - #ifdef VST_NATIVE_DEBUG - else - fprintf(stderr, "VstNativeSynthIF::getData %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), v.idx); - #endif -#endif - // Need to update the automation value, otherwise it overwrites later with the last automation value. - if(id() != -1) - synti->setPluginCtrlVal(genACnum(id(), v.idx), v.value); - //} + if(ports == 0) // Don't bother if not 'running'. + { + _controls[v.idx].val = v.value; // Might as well at least update these. +// #ifndef VST_VESTIGE_SUPPORT +// if(dispatch(effCanBeAutomated, v.idx, 0, NULL, 0.0f) == 1) +// { +// #endif +// if(v.value != _plugin->getParameter(_plugin, v.idx)) +// _plugin->setParameter(_plugin, v.idx, v.value); +// #ifndef VST_VESTIGE_SUPPORT +// } +// #ifdef VST_NATIVE_DEBUG +// else +// fprintf(stderr, "VstNativeSynthIF::getData %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), v.idx); +// #endif +// #endif + } + else + { + CtrlInterpolate* ci = &_controls[v.idx].interp; + // Tell it to stop the current ramp at this frame, when it does stop, set this value: + ci->eFrame = frame; + ci->eVal = v.value; + ci->eStop = true; + } + + // Need to update the automation value, otherwise it overwrites later with the last automation value. + if(plug_id != -1) + synti->setPluginCtrlVal(genACnum(plug_id, v.idx), v.value); } if(found && !usefixedrate) // If a control FIFO item was found, takes priority over automation controller stream. nsamp = frame - sample; - if(sample + nsamp >= nframes) // Safety check. + if(sample + nsamp > nframes) // Safety check. nsamp = nframes - sample; // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead. // Note this means it is still possible to get stuck in the top loop (at least for a while). - if(nsamp == 0) - continue; - - nevents = 0; - // Process event list events... - for(; start_event != el->end(); ++start_event) + if(nsamp != 0) { - #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::getData eventlist event time:%d pos:%u sample:%lu nsamp:%lu frameOffset:%d\n", start_event->time(), pos, sample, nsamp, frameOffset); - #endif - - if(start_event->time() >= (pos + sample + nsamp + frameOffset)) // frameOffset? Test again... - { - #ifdef VST_NATIVE_DEBUG - fprintf(stderr, " event is for future:%lu, breaking loop now\n", start_event->time() - frameOffset - pos - sample); - #endif - break; - } - - // Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values. - // Same code as in MidiPort::sendEvent() - if(synti->midiPort() != -1) + unsigned long nevents = 0; + if(ports != 0) // Don't bother if not 'running'. { - MusECore::MidiPort* mp = &MusEGlobal::midiPorts[synti->midiPort()]; - if(start_event->type() == MusECore::ME_CONTROLLER) + // Process event list events... + for(; start_event != el->end(); ++start_event) { - int da = start_event->dataA(); - int db = start_event->dataB(); - db = mp->limitValToInstrCtlRange(da, db); - if(!mp->setHwCtrlState(start_event->channel(), da, db)) - continue; - } - else if(start_event->type() == MusECore::ME_PITCHBEND) - { - int da = mp->limitValToInstrCtlRange(MusECore::CTRL_PITCH, start_event->dataA()); - if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_PITCH, da)) - continue; - } - else if(start_event->type() == MusECore::ME_AFTERTOUCH) - { - int da = mp->limitValToInstrCtlRange(MusECore::CTRL_AFTERTOUCH, start_event->dataA()); - if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_AFTERTOUCH, da)) - continue; - } - else if(start_event->type() == MusECore::ME_POLYAFTER) - { - int ctl = (MusECore::CTRL_POLYAFTER & ~0xff) | (start_event->dataA() & 0x7f); - int db = mp->limitValToInstrCtlRange(ctl, start_event->dataB()); - if(!mp->setHwCtrlState(start_event->channel(), ctl , db)) - continue; - } - else if(start_event->type() == MusECore::ME_PROGRAM) - { - if(!mp->setHwCtrlState(start_event->channel(), MusECore::CTRL_PROGRAM, start_event->dataA())) - continue; - } - } + #ifdef VST_NATIVE_DEBUG + fprintf(stderr, "VstNativeSynthIF::getData eventlist event time:%d pos:%u sample:%lu nsamp:%lu frameOffset:%d\n", start_event->time(), pos, sample, nsamp, frameOffset); + #endif - // Returns false if the event was not filled. It was handled, but some other way. - if(processEvent(*start_event, &events[nevents])) - { - // Time-stamp the event. - int ft = start_event->time() - frameOffset - pos - sample; - if(ft < 0) - ft = 0; + if(start_event->time() >= (pos + sample + nsamp + frameOffset)) // frameOffset? Test again... + { + #ifdef VST_NATIVE_DEBUG + fprintf(stderr, " event is for future:%lu, breaking loop now\n", start_event->time() - frameOffset - pos - sample); + #endif + break; + } - if (ft >= int(nsamp)) - { - fprintf(stderr, "VstNativeSynthIF::getData: eventlist event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", start_event->time(), pos, frameOffset, ft, sample, nsamp); - ft = nsamp - 1; + // Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values. + // Same code as in MidiPort::sendEvent() + if(synti->midiPort() != -1) + { + MidiPort* mp = &MusEGlobal::midiPorts[synti->midiPort()]; + if(start_event->type() == ME_CONTROLLER) + { + int da = start_event->dataA(); + int db = start_event->dataB(); + db = mp->limitValToInstrCtlRange(da, db); + if(!mp->setHwCtrlState(start_event->channel(), da, db)) + continue; + } + else if(start_event->type() == ME_PITCHBEND) + { + int da = mp->limitValToInstrCtlRange(CTRL_PITCH, start_event->dataA()); + if(!mp->setHwCtrlState(start_event->channel(), CTRL_PITCH, da)) + continue; + } + else if(start_event->type() == ME_AFTERTOUCH) + { + int da = mp->limitValToInstrCtlRange(CTRL_AFTERTOUCH, start_event->dataA()); + if(!mp->setHwCtrlState(start_event->channel(), CTRL_AFTERTOUCH, da)) + continue; + } + else if(start_event->type() == ME_POLYAFTER) + { + int ctl = (CTRL_POLYAFTER & ~0xff) | (start_event->dataA() & 0x7f); + int db = mp->limitValToInstrCtlRange(ctl, start_event->dataB()); + if(!mp->setHwCtrlState(start_event->channel(), ctl , db)) + continue; + } + else if(start_event->type() == ME_PROGRAM) + { + if(!mp->setHwCtrlState(start_event->channel(), CTRL_PROGRAM, start_event->dataA())) + continue; + } + } + + // Returns false if the event was not filled. It was handled, but some other way. + if(processEvent(*start_event, &events[nevents])) + { + // Time-stamp the event. + int ft = start_event->time() - frameOffset - pos - sample; + if(ft < 0) + ft = 0; + + if (ft >= int(nsamp)) + { + fprintf(stderr, "VstNativeSynthIF::getData: eventlist event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", start_event->time(), pos, frameOffset, ft, sample, nsamp); + ft = nsamp - 1; + } + + #ifdef VST_NATIVE_DEBUG + fprintf(stderr, "VstNativeSynthIF::getData eventlist: ft:%d current nevents:%lu\n", ft, nevents); + #endif + + vst_events->events[nevents] = (VstEvent*)&events[nevents]; + events[nevents].deltaFrames = ft; + ++nevents; + } } + } + + // Now process putEvent events... + while(!synti->eventFifo.isEmpty()) + { + MidiPlayEvent e = synti->eventFifo.peek(); #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::getData eventlist: ft:%d current nevents:%lu\n", ft, nevents); + fprintf(stderr, "VstNativeSynthIF::getData eventFifo event time:%d\n", e.time()); #endif - vst_events->events[nevents] = (VstEvent*)&events[nevents]; - events[nevents].deltaFrames = ft; - ++nevents; - } - } + if(e.time() >= (pos + sample + nsamp + frameOffset)) + break; - // Now process putEvent events... - while(!synti->eventFifo.isEmpty()) - { - MusECore::MidiPlayEvent e = synti->eventFifo.peek(); + synti->eventFifo.remove(); // Done with ring buffer's event. Remove it. + if(ports != 0) // Don't bother if not 'running'. + { + // Returns false if the event was not filled. It was handled, but some other way. + if(processEvent(e, &events[nevents])) + { + // Time-stamp the event. + int ft = e.time() - frameOffset - pos - sample; + if(ft < 0) + ft = 0; + if (ft >= int(nsamp)) + { + fprintf(stderr, "VstNativeSynthIF::getData: eventFifo event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", e.time(), pos, frameOffset, ft, sample, nsamp); + ft = nsamp - 1; + } + vst_events->events[nevents] = (VstEvent*)&events[nevents]; + events[nevents].deltaFrames = ft; - #ifdef VST_NATIVE_DEBUG - fprintf(stderr, "VstNativeSynthIF::getData eventFifo event time:%d\n", e.time()); - #endif + ++nevents; + } + } + } - if(e.time() >= (pos + sample + nsamp + frameOffset)) - break; + #ifdef VST_NATIVE_DEBUG_PROCESS + fprintf(stderr, "VstNativeSynthIF::getData: Connecting and running. sample:%lu nsamp:%lu nevents:%lu\n", sample, nsamp, nevents); + #endif - synti->eventFifo.remove(); // Done with ring buffer's event. Remove it. - // Returns false if the event was not filled. It was handled, but some other way. - if(processEvent(e, &events[nevents])) + if(ports != 0) // Don't bother if not 'running'. { - // Time-stamp the event. - int ft = e.time() - frameOffset - pos - sample; - if(ft < 0) - ft = 0; - if (ft >= int(nsamp)) + // Set the events pointer. + if(nevents > 0) { - fprintf(stderr, "VstNativeSynthIF::getData: eventFifo event time:%d out of range. pos:%d offset:%d ft:%d sample:%lu nsamp:%lu\n", e.time(), pos, frameOffset, ft, sample, nsamp); - ft = nsamp - 1; + vst_events->numEvents = nevents; + dispatch(effProcessEvents, 0, 0, vst_events, 0.0f); } - vst_events->events[nevents] = (VstEvent*)&events[nevents]; - events[nevents].deltaFrames = ft; - - ++nevents; - } - } - - #ifdef VST_NATIVE_DEBUG_PROCESS - fprintf(stderr, "VstNativeSynthIF::getData: Connecting and running. sample:%lu nsamp:%lu nevents:%lu\n", sample, nsamp, nevents); - #endif - // Set the events pointer. - if(nevents > 0) - { - vst_events->numEvents = nevents; - dispatch(effProcessEvents, 0, 0, vst_events, 0.0f); - } + float* in_bufs[_synth->inPorts()]; + float* out_bufs[_synth->outPorts()]; + + unsigned long k = 0; + // Connect the given buffers directly to the ports, up to a max of synth ports. + for(; k < nop; ++k) + out_bufs[k] = buffer[k] + sample; + // Connect the remaining ports to some local buffers (not used yet). + const unsigned long op =_synth->outPorts(); + for(; k < op; ++k) + out_bufs[k] = _audioOutBuffers[k] + sample; + // Connect all inputs either to some local buffers, or a silence buffer. + const unsigned long ip =_synth->inPorts(); + for(k = 0; k < ip; ++k) + { + if(_iUsedIdx[k]) + { + _iUsedIdx[k] = false; // Reset + in_bufs[k] = _audioInBuffers[k] + sample; + } + else + in_bufs[k] = _audioInSilenceBuf + sample; + } - float* in_bufs[_synth->inPorts()]; - float* out_bufs[_synth->outPorts()]; - - k = 0; - // Connect the given buffers directly to the ports, up to a max of synth ports. - for(; k < nop; ++k) - out_bufs[k] = buffer[k] + sample; - // Connect the remaining ports to some local buffers (not used yet). - const unsigned long op =_synth->outPorts(); - for(; k < op; ++k) - out_bufs[k] = _audioOutBuffers[k] + sample; - // Connect all inputs either to some local buffers, or a silence buffer. - const unsigned long ip =_synth->inPorts(); - for(k = 0; k < ip; ++k) - { - if(_iUsedIdx[k]) - { - _iUsedIdx[k] = false; // Reset - in_bufs[k] = _audioInBuffers[k] + sample; + // Run the synth for a period of time. This processes events and gets/fills our local buffers... + if((_plugin->flags & effFlagsCanReplacing) && _plugin->processReplacing) + { + _plugin->processReplacing(_plugin, in_bufs, out_bufs, nsamp); + } } - else - in_bufs[k] = _audioInSilenceBuf + sample; + + sample += nsamp; } - // Run the synth for a period of time. This processes events and gets/fills our local buffers... - if((_plugin->flags & effFlagsCanReplacing) && _plugin->processReplacing) - { - _plugin->processReplacing(_plugin, in_bufs, out_bufs, nsamp); - } -#if 0 //ifdef VST_FORCE_DEPRECATED - else - { - // TODO: This is not right, we don't accumulate data here. Depricated now, and frowned upon anyway... - if(_plugin->process) - _plugin->process(_plugin, in_bufs, out_bufs, nsamp); - } -#endif - - sample += nsamp; + ++cur_slice; // Slice is done. Moving on to any next slice now... } - - // Inform the host callback we will be no longer in the audio thread. - _inProcess = true; + // Inform the host callback we will be no longer in the audio thread. + _inProcess = false; + return start_event; } @@ -2652,8 +2810,6 @@ QString VstNativeSynthIF::dirPath() const { return _synt QString VstNativeSynthIF::fileName() const { return _synth ? _synth->fileName() : QString(); } void VstNativeSynthIF::enableController(unsigned long i, bool v) { _controls[i].enCtrl = v; } bool VstNativeSynthIF::controllerEnabled(unsigned long i) const { return _controls[i].enCtrl;} -void VstNativeSynthIF::enable2Controller(unsigned long i, bool v) { _controls[i].en2Ctrl = v; } -bool VstNativeSynthIF::controllerEnabled2(unsigned long i) const { return _controls[i].en2Ctrl; } void VstNativeSynthIF::enableAllControllers(bool v) { if(!_synth) @@ -2662,14 +2818,6 @@ void VstNativeSynthIF::enableAllControllers(bool v) for(unsigned long i = 0; i < sic; ++i) _controls[i].enCtrl = v; } -void VstNativeSynthIF::enable2AllControllers(bool v) -{ - if(!_synth) - return; - const unsigned long sic = _synth->inControls(); - for(unsigned long i = 0; i < sic; ++i) - _controls[i].en2Ctrl = v; -} void VstNativeSynthIF::updateControllers() { } void VstNativeSynthIF::activate() { @@ -2694,9 +2842,11 @@ void VstNativeSynthIF::activate() // controls[i].tmpVal = controls[i].val; // } // } + _active = true; } void VstNativeSynthIF::deactivate() { + _active = false; //for (unsigned short i = 0; i < instances(); ++i) { #ifndef VST_VESTIGE_SUPPORT //dispatch(i, effStopProcess, 0, 0, NULL, 0.0f); diff --git a/muse2/muse/vst_native.h b/muse2/muse/vst_native.h index 4bbe1411..0d7f5441 100644 --- a/muse2/muse/vst_native.h +++ b/muse2/muse/vst_native.h @@ -3,7 +3,7 @@ // Linux Music Editor // // vst_native.h -// (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) +// (C) Copyright 2012-2013 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 @@ -118,8 +118,8 @@ class VstNativeSynth : public Synth { std::vector<unsigned long> iIdx; // Audio input index to port number. std::vector<unsigned long> oIdx; // Audio output index to port number. std::vector<unsigned long> rpIdx; // Port number to control input index. Item is -1 if it's not a control input. - MusECore::MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to vst port numbers. - MusECore::MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps vst port numbers to midi controller numbers. + MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to vst port numbers. + MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps vst port numbers to midi controller numbers. bool _hasGui; bool _inPlaceCapable; bool _hasChunks; @@ -130,7 +130,7 @@ class VstNativeSynth : public Synth { virtual ~VstNativeSynth() {} virtual Type synthType() const { return VST_NATIVE_SYNTH; } virtual void incInstances(int val); - virtual AEffect* instantiate(); + virtual AEffect* instantiate(VstNativeSynthIF*); virtual SynthIF* createSIF(SynthI*); unsigned long inPorts() const { return _inports; } unsigned long outPorts() const { return _outports; } @@ -143,6 +143,21 @@ class VstNativeSynth : public Synth { }; //--------------------------------------------------------- +// VstNativeGuiWidgets +//--------------------------------------------------------- + +struct VstNativeGuiWidgets { + // TODO: Remove Tim. Or keep. + //enum { + // SLIDER, DOUBLE_LABEL, QCHECKBOX, QCOMBOBOX + // }; + //QWidget* widget; + //int type; + //unsigned long param; + bool pressed; + }; + +//--------------------------------------------------------- // VstNativeSynthIF // VSTi synthesizer instance //--------------------------------------------------------- @@ -154,10 +169,13 @@ class VstNativeSynthIF : public SynthIF VstNativeSynth* _synth; AEffect* _plugin; + bool _active; // Whether it's safe to call effIdle or effEditIdle. MusEGui::VstNativeEditor* _editor; bool _guiVisible; bool _inProcess; // To inform the callback of the 'process level' - are we in the audio thread? + // Struct array to keep track of pressed flags and so on. // TODO: Not used yet. REMOVE Tim. Or keep. + VstNativeGuiWidgets* _gw; Port* _controls; float** _audioOutBuffers; float** _audioInBuffers; @@ -167,7 +185,7 @@ class VstNativeSynthIF : public SynthIF std::vector<VST_Program> programs; void queryPrograms(); void doSelectProgram(int bankH, int bankL, int prog); - bool processEvent(const MusECore::MidiPlayEvent&, VstMidiEvent*); + bool processEvent(const MidiPlayEvent&, VstMidiEvent*); void setVstEvent(VstMidiEvent* event, int a = 0, int b = 0, int c = 0, int d = 0); void editorDeleted(); @@ -188,17 +206,17 @@ class VstNativeSynthIF : public SynthIF bool resizeEditor(int w, int h); virtual bool initGui() { return true; }; - virtual void guiHeartBeat() { } + virtual void guiHeartBeat(); virtual bool guiVisible() const; virtual void showGui(bool); virtual bool hasGui() const { return true; } virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool v); virtual bool hasNativeGui() const; - virtual void getGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; } - virtual void setGeometry(int, int, int, int) {} - virtual void getNativeGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; } - virtual void setNativeGeometry(int, int, int, int) {} + virtual void getGeometry(int*x, int*y, int*w, int*h) const; + virtual void setGeometry(int, int, int, int); + virtual void getNativeGeometry(int*x, int*y, int*w, int*h) const ; + virtual void setNativeGeometry(int, int, int, int); virtual void preProcessAlways() { }; virtual iMPEvent getData(MidiPort*, MPEventList*, iMPEvent, unsigned pos, int ports, unsigned nframes, float** buffer) ; virtual bool putEvent(const MidiPlayEvent& ev); @@ -230,10 +248,7 @@ class VstNativeSynthIF : public SynthIF QString fileName() const; void enableController(unsigned long i, bool v = true); bool controllerEnabled(unsigned long i) const; - void enable2Controller(unsigned long i, bool v = true); - bool controllerEnabled2(unsigned long i) const; void enableAllControllers(bool v = true); - void enable2AllControllers(bool v = true); void updateControllers(); void activate(); void deactivate(); diff --git a/muse2/muse/waveedit/waveedit.cpp b/muse2/muse/waveedit/waveedit.cpp index a8477bbc..7294e7c3 100644 --- a/muse2/muse/waveedit/waveedit.cpp +++ b/muse2/muse/waveedit/waveedit.cpp @@ -529,8 +529,8 @@ void WaveEdit::writeStatus(int level, MusECore::Xml& xml) const xml.tag(level++, "waveedit"); MidiEditor::writeStatus(level, xml); xml.intTag(level, "tool", int(canvas->tool())); - xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "xmag", hscroll->mag()); + xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "ymag", ymag->value()); xml.tag(level, "/waveedit"); } diff --git a/muse2/muse/wavetrack.cpp b/muse2/muse/wavetrack.cpp index c9997eae..363f8966 100644 --- a/muse2/muse/wavetrack.cpp +++ b/muse2/muse/wavetrack.cpp @@ -252,31 +252,25 @@ Part* WaveTrack::newPart(Part*p, bool clone) bool WaveTrack::getData(unsigned framePos, int channels, unsigned nframe, float** bp) { + bool have_data = false; if ((MusEGlobal::song->bounceTrack != this) && !noInRoute()) { RouteList* irl = inRoutes(); - ciRoute i = irl->begin(); - if(i->track->isMidiTrack()) - return false; - - ((AudioTrack*)i->track)->copyData(framePos, channels, - i->channel, - i->channels, - nframe, bp); - - ++i; - for (; i != irl->end(); ++i) + for(ciRoute i = irl->begin(); i != irl->end(); ++i) { if(i->track->isMidiTrack()) continue; - ((AudioTrack*)i->track)->addData(framePos, channels, - i->channel, + ((AudioTrack*)i->track)->copyData(framePos, channels, + i->channel, i->channels, - nframe, bp); - + nframe, bp, have_data); + have_data = true; } + if (recordFlag()) { - if (MusEGlobal::audio->isRecording() && recFile()) { + //if (have_data && recordFlag()) { + //if (MusEGlobal::audio->isRecording() && recFile()) { + if (have_data && MusEGlobal::audio->isRecording() && recFile()) { if (MusEGlobal::audio->freewheel()) { } else { @@ -295,39 +289,42 @@ bool WaveTrack::getData(unsigned framePos, int channels, unsigned nframe, float* framePos, channels, nframe); } } - return true; + //return true; // REMOVE Tim. Can't hear existing parts while recording, even while simply armed. + // FIXME TODO Remove this. But first code below needs to become ADDITIVE/REPLACING (don't just take over buffers). + return have_data; } } if (!MusEGlobal::audio->isPlaying()) return false; + //return have_data; - if (MusEGlobal::audio->freewheel()) { - - // when freewheeling, read data direct from file: - // Indicate do not seek file before each read. - fetchData(framePos, nframe, bp, false); - + if (MusEGlobal::audio->freewheel()) { + + // when freewheeling, read data direct from file: + // Indicate do not seek file before each read. + fetchData(framePos, nframe, bp, false); + + } + else { + unsigned pos; + if (_prefetchFifo.get(channels, nframe, bp, &pos)) { + printf("WaveTrack::getData(%s) fifo underrun\n", + name().toLatin1().constData()); + return false; } - else { - unsigned pos; - if (_prefetchFifo.get(channels, nframe, bp, &pos)) { - printf("WaveTrack::getData(%s) fifo underrun\n", - name().toLatin1().constData()); - return false; - } - if (pos != framePos) { - if (MusEGlobal::debugMsg) - printf("fifo get error expected %d, got %d\n", - framePos, pos); - while (pos < framePos) { - if (_prefetchFifo.get(channels, nframe, bp, &pos)) { - printf("WaveTrack::getData(%s) fifo underrun\n", - name().toLatin1().constData()); - return false; - } + if (pos != framePos) { + if (MusEGlobal::debugMsg) + printf("fifo get error expected %d, got %d\n", + framePos, pos); + while (pos < framePos) { + if (_prefetchFifo.get(channels, nframe, bp, &pos)) { + printf("WaveTrack::getData(%s) fifo underrun\n", + name().toLatin1().constData()); + return false; } } } + } return true; } diff --git a/muse2/muse/widgets/scrollscale.cpp b/muse2/muse/widgets/scrollscale.cpp index fcf36e1b..5664bfc2 100644 --- a/muse2/muse/widgets/scrollscale.cpp +++ b/muse2/muse/widgets/scrollscale.cpp @@ -198,7 +198,6 @@ void ScrollScale::setRange ( int min, int max ) void ScrollScale::setPos ( unsigned pos ) { - scroll->setValue ( pos ); } diff --git a/muse2/muse/widgets/sliderbase.cpp b/muse2/muse/widgets/sliderbase.cpp index 5909c64d..3dfbfa40 100644 --- a/muse2/muse/widgets/sliderbase.cpp +++ b/muse2/muse/widgets/sliderbase.cpp @@ -386,17 +386,19 @@ void SliderBase::mouseMoveEvent(QMouseEvent *e) double ms = 0.0; if (d_scrollMode == ScrMouse ) { - setPosition(e->pos()); - if (d_mass > 0.0) - { - ms = double(d_time.elapsed()); - if (ms < 1.0) ms = 1.0; - d_speed = (exactValue() - exactPrevValue()) / ms; - d_time.start(); - } - if (value() != prevValue()) - emit sliderMoved(value(), _id); - emit sliderMoved(value(), _id, (bool)(e->modifiers() & Qt::ShiftModifier)); + setPosition(e->pos()); + if (d_mass > 0.0) + { + ms = double(d_time.elapsed()); + if (ms < 1.0) ms = 1.0; + d_speed = (exactValue() - exactPrevValue()) / ms; + d_time.start(); + } + if (value() != prevValue()) + { + emit sliderMoved(value(), _id); + emit sliderMoved(value(), _id, (bool)(e->modifiers() & Qt::ShiftModifier)); + } } } diff --git a/muse2/muse/widgets/tracks_duplicate_base.ui b/muse2/muse/widgets/tracks_duplicate_base.ui index 601c9f25..8c5b6a71 100644 --- a/muse2/muse/widgets/tracks_duplicate_base.ui +++ b/muse2/muse/widgets/tracks_duplicate_base.ui @@ -110,7 +110,7 @@ <item> <widget class="QCheckBox" name="standardCtrlsCheckBox"> <property name="text"> - <string>Copy standard controllers (vol, pan)</string> + <string>Copy standard (vol, pan) and synth controllers</string> </property> <property name="checked"> <bool>true</bool> @@ -119,9 +119,6 @@ </item> <item> <widget class="QCheckBox" name="pluginsCheckBox"> - <property name="enabled"> - <bool>false</bool> - </property> <property name="text"> <string>Copy effects rack plugins</string> </property> diff --git a/muse2/muse/widgets/vst_native_editor.cpp b/muse2/muse/widgets/vst_native_editor.cpp index b194529d..651d817f 100644 --- a/muse2/muse/widgets/vst_native_editor.cpp +++ b/muse2/muse/widgets/vst_native_editor.cpp @@ -157,7 +157,7 @@ void VstNativeEditor::open(MusECore::VstNativeSynthIF* sif) show(); raise(); activateWindow(); - _sif->idleEditor(); + ///_sif->idleEditor(); // REMOVE Tim. Or keep. } #if defined(Q_WS_X11) |