From 1777776822468fc45e112144776c1e4527a29be3 Mon Sep 17 00:00:00 2001 From: "Tim E. Real" Date: Fri, 7 May 2010 02:53:42 +0000 Subject: See ChangeLog --- muse/ChangeLog | 15 ++++ muse/README.effects-rack | 75 ++++++++++++++++ muse/muse/audioprefetch.cpp | 4 + muse/muse/driver/audiodev.h | 1 + muse/muse/driver/dummyaudio.cpp | 1 + muse/muse/driver/jackaudio.h | 1 + muse/muse/node.cpp | 80 +++++++++++++++-- muse/muse/plugin.cpp | 194 ++++++++++++++++++++++++++++++---------- muse/muse/waveevent.cpp | 4 + muse/muse/wavetrack.cpp | 18 ++++ 10 files changed, 337 insertions(+), 56 deletions(-) create mode 100644 muse/README.effects-rack diff --git a/muse/ChangeLog b/muse/ChangeLog index 5a0c5b0c..4cec930b 100644 --- a/muse/ChangeLog +++ b/muse/ChangeLog @@ -1,3 +1,18 @@ +06.10.2010 + * Fixed: Audio Input tracks: Loud noises or DC output with unconnected input routes. (T356) + - AudioInput::getData(): Do not get buffers of unconnected client ports. Causes repeating leftover data. + * Fixed: Audio Input tracks: Stereo processing not correct if both input routes connected to same port. (T356) + - AudioInput::getData(): Just copy the buffers always, don't set buffer pointer directly. + * Fixed: Effect rack plugins: Not saving correct number of plugin channels. Upon reload, channels are wrong. (T356) + - PluginI::writeConfiguration() and PluginI::readConfiguration(): Write and read the channels, not instances. + - Optimize TODO: Is xml plugin 'channel' really necessary? Well not now, but later if we optimize the effect rack + by not creating redundant plugin instances if the next plugin in the rack doesn't need them. + What I have changed is always make the number of plugin channels equal to the number of track channels. + PluginI::setChannels() and PluginI::initPluginInstance() then sort out the details of how many instances to create. + * Fixed: Effect rack plugins: Some plugins crash MusE, for example plugins with no audio outputs. (T356) + - PluginI::setChannels() and PluginI::initPluginInstance(): Be more aware of different audio ins and outs. + * Added: First draft help file README.effects-rack titled "Understanding the Effects Rack". (T356) + - Details how MusE uses plugins, especially now with these changes. 27.04.2010 * Changed: Building: Separate --enable-dssi --enable-osc configure options. (T356) --enable-dssi (Enable dssi synths AND special handling of dssi-vst ladspa effect plugins. No guis if by itself.) and diff --git a/muse/README.effects-rack b/muse/README.effects-rack new file mode 100644 index 00000000..085dab33 --- /dev/null +++ b/muse/README.effects-rack @@ -0,0 +1,75 @@ +May 6, 2010 v0.0.1 by Tim. + +Understanding the Effects Rack +------------------------------ + +All audio track types (Input, Output, Group, Wave, Synth, and Aux) have + an effects rack into which audio plugins can be inserted in a chain. +Currently each rack can accomodate up to four plugins. + +MusE currently supports LADSPA plugins and DSSI effects plugins. + +One must carefully consider how many audio inputs and outputs a plugin has, + and how may channels the particular audio track has (1 mono or 2 stereo), + and how MusE uses the plugins in the rack. + +MusE will try to internally create as many independent copies (instances) of + a plugin as necessary, to satisfy the number of channels in the audio track. +Basically it divides the number of track channels by the number of + plugin audio inputs or outputs to determine how many copies to make. +First it examines the number of plugin audio outputs, and if there are none, + it will examine the number of audio inputs, and if there are none, + it will simply use just one plugin copy. + +For mono tracks with plugins having more than one audio input or output, + MusE uses the first input or output and ignores the rest. + +For stereo tracks: + + Audio Audio Independent Real number Real number + inputs: outputs: plugin of track of track + copies: input route output route + channels used: channels used: + ------------------------------------------------------------------- + 0 0 1 0 0 + 0 1 2 0 2 + 0 2 1 0 2 + 1 0 2 2 0 + 1 1 2 2 2 + 1 2 1 1 L only! 2 + 2 0 1 2 0 + 2 1 2 2 2 + 2 2 1 2 2 + +Notice that on a stereo track with a plugin having one audio input and two + audio outputs, only the first track input route channel is used (left only). + +These same rules apply to inter-plugin audio when more than one plugin + is in the rack chain. Extra audio outputs of one plugin may be ignored + by the next plugin if not used. + + +Example: Consider a stereo Audio Input track with these effect rack + LADSPA plugins: + + comb_splitter Comb Splitter by Steve Harris, and + tap_stereo_echo Tap Stereo Echo by Tom Szilagyi + +The Comb Splitter has one audio input and two audio outputs. +The Stereo Echo has two audio inputs and two audio outputs. + +The stereo Audio Input track will therefore ignore its second + input route connection. It will process the left input only, + separating it into stereo with the Comb Splitter, passing the + split stereo signal into the Stereo Echo, finally producing + stereo output available at the Audio Input track's output routes. + + +One improvement would be not creating unused redundant plugin copies + between plugins in stereo tracks. +For example, for a plugin having one audio input and one audio output, + feeding a plugin having one audio input and two audio outputs, + the extra copy of the first plugin is redundant and not required, + but currently it is created anyway. + + \ No newline at end of file diff --git a/muse/muse/audioprefetch.cpp b/muse/muse/audioprefetch.cpp index 26ae4237..e31dcc93 100644 --- a/muse/muse/audioprefetch.cpp +++ b/muse/muse/audioprefetch.cpp @@ -194,6 +194,10 @@ void AudioPrefetch::prefetch(bool doSeek) } //track->fetchData(writePos, segmentSize, bp); track->fetchData(writePos, segmentSize, bp, doSeek); + + // p3.3.41 + //fprintf(stderr, "AudioPrefetch::prefetch data: segmentSize:%ld %e %e %e %e\n", segmentSize, bp[0][0], bp[0][1], bp[0][2], bp[0][3]); + } writePos += segmentSize; } diff --git a/muse/muse/driver/audiodev.h b/muse/muse/driver/audiodev.h index b888f6eb..b28e381c 100644 --- a/muse/muse/driver/audiodev.h +++ b/muse/muse/driver/audiodev.h @@ -48,6 +48,7 @@ class AudioDevice { virtual void unregisterPort(void*) = 0; virtual void connect(void*, void*) = 0; virtual void disconnect(void*, void*) = 0; + virtual int connections(void* /*clientPort*/) = 0; virtual void setPortName(void* p, const char* n) = 0; virtual void* findPort(const char* name) = 0; virtual QString portName(void* port) = 0; diff --git a/muse/muse/driver/dummyaudio.cpp b/muse/muse/driver/dummyaudio.cpp index 0bca9890..54b1609f 100644 --- a/muse/muse/driver/dummyaudio.cpp +++ b/muse/muse/driver/dummyaudio.cpp @@ -106,6 +106,7 @@ class DummyAudioDevice : public AudioDevice { virtual void unregisterPort(void*) {} virtual void connect(void*, void*) {} virtual void disconnect(void*, void*) {} + virtual int connections(void* /*clientPort*/) { return 0; } virtual void setPortName(void*, const char*) {} virtual void* findPort(const char*) { return 0;} virtual QString portName(void*) { diff --git a/muse/muse/driver/jackaudio.h b/muse/muse/driver/jackaudio.h index 242e762d..2d30fe74 100644 --- a/muse/muse/driver/jackaudio.h +++ b/muse/muse/driver/jackaudio.h @@ -68,6 +68,7 @@ class JackAudioDevice : public AudioDevice { virtual void unregisterPort(void*); virtual void connect(void*, void*); virtual void disconnect(void*, void*); + virtual int connections(void* clientPort) { return jack_port_connected((jack_port_t*)clientPort); } 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); diff --git a/muse/muse/node.cpp b/muse/muse/node.cpp index 178c87cc..ab075b01 100644 --- a/muse/muse/node.cpp +++ b/muse/muse/node.cpp @@ -429,6 +429,8 @@ void AudioTrack::copyData(unsigned pos, int dstChannels, int srcStartChan, int s // apply plugin chain //--------------------------------------------------- + // p3.3.41 + //fprintf(stderr, "AudioTrack::copyData %s efx apply srcChans:%d\n", name().latin1(), srcChans); _efxPipe->apply(srcChans, nframes, buffer); //--------------------------------------------------- @@ -816,11 +818,35 @@ void AudioTrack::addData(unsigned pos, int dstChannels, int srcStartChan, int sr return; } + /* + // p3.3.41 Added. + unsigned int q; + for(i = 0; i < srcChans; ++i) + { + if(config.useDenormalBias) + { + for(q = 0; q < nframes; ++q) + { + if(q & 1) + buffer[i][q] -= denormalBias; + else + buffer[i][q] += denormalBias; + } + } + } + */ + //--------------------------------------------------- // apply plugin chain //--------------------------------------------------- + // p3.3.41 + //fprintf(stderr, "AudioTrack::addData %s efx apply srcChans:%d nframes:%ld %e %e %e %e\n", + // name().latin1(), srcChans, nframes, buffer[0][0], buffer[0][1], buffer[0][2], buffer[0][3]); _efxPipe->apply(srcChans, nframes, buffer); + // p3.3.41 + //fprintf(stderr, "AudioTrack::addData after efx: %e %e %e %e\n", + // buffer[0][0], buffer[0][1], buffer[0][2], buffer[0][3]); //--------------------------------------------------- // aux sends @@ -1233,6 +1259,9 @@ bool AudioTrack::getData(unsigned pos, int channels, unsigned nframes, float** b ir->channels, nframes, buffer); + // p3.3.41 + //fprintf(stderr, "AudioTrack::getData %s data: nframes:%ld %e %e %e %e\n", name().latin1(), nframes, buffer[0][0], buffer[0][1], buffer[0][2], buffer[0][3]); + ++ir; for (; ir != rl->end(); ++ir) { #ifdef NODE_DEBUG @@ -1261,25 +1290,58 @@ bool AudioTrack::getData(unsigned pos, int channels, unsigned nframes, float** b bool AudioInput::getData(unsigned, int channels, unsigned nframes, float** buffer) { if (!checkAudioDevice()) return false; - for (int ch = 0; ch < channels; ++ch) { + for (int ch = 0; ch < channels; ++ch) + { void* jackPort = jackPorts[ch]; - if (jackPort) { - buffer[ch] = audioDevice->getBuffer(jackPort, nframes); - if (config.useDenormalBias) { + //float* jackbuf = 0; + + //if (jackPort) { + // p3.3.41 Do not get buffers of unconnected client ports. Causes repeating leftover data, can be loud, or DC ! + if (jackPort && audioDevice->connections(jackPort)) + { + //buffer[ch] = audioDevice->getBuffer(jackPort, nframes); + // p3.3.41 If the client port buffer is also used by another channel (connected to the same jack port), + // don't directly set pointer, copy the data instead. + // Otherwise the next channel will interfere - it will overwrite the buffer ! + // Verified symptoms: Can't use a splitter. Mono noise source on a stereo track sounds in mono. Etc... + // TODO: Problem: What if other Audio Input tracks share the same jack ports as this Audio Input track? + // Users will expect that Audio Inputs just work even if the input routes originate from the same jack port. + // Solution: Rather than having to iterate all other channels, and all other Audio Input tracks and check + // their channel port buffers (if that's even possible) in order to determine if the buffer is shared, + // let's just copy always, for now shall we ? + float* jackbuf = audioDevice->getBuffer(jackPort, nframes); + //memcpy(buffer[ch], jackbuf, nframes* sizeof(float)); + AL::dsp->cpy(buffer[ch], jackbuf, nframes); + + if (config.useDenormalBias) + { for (unsigned int i=0; i < nframes; i++) buffer[ch][i] += denormalBias; + + // p3.3.41 + //fprintf(stderr, "AudioInput::getData %s Jack port %p efx apply channels:%d nframes:%ld %e %e %e %e\n", + // name().latin1(), jackPort, channels, nframes, buffer[0][0], buffer[0][1], buffer[0][2], buffer[0][3]); } - } else { - if (config.useDenormalBias) { + } + else + { + if (config.useDenormalBias) + { for (unsigned int i=0; i < nframes; i++) buffer[ch][i] = denormalBias; - } else { + } + else + { memset(buffer[ch], 0, nframes * sizeof(float)); - } } + + // p3.3.41 + //fprintf(stderr, "AudioInput::getData %s No Jack port efx apply channels:%d nframes:%ld %e %e %e %e\n", + // name().latin1(), channels, nframes, buffer[0][0], buffer[0][1], buffer[0][2], buffer[0][3]); } - return true; } + return true; +} //--------------------------------------------------------- // setName diff --git a/muse/muse/plugin.cpp b/muse/muse/plugin.cpp index 4d6113ff..75edeef4 100644 --- a/muse/muse/plugin.cpp +++ b/muse/muse/plugin.cpp @@ -667,6 +667,10 @@ Plugin::~Plugin() int Plugin::incReferences(int val) { + #ifdef PLUGIN_DEBUGIN + fprintf(stderr, "Plugin::incReferences _references:%d val:%d\n", _references, val); + #endif + int newref = _references + val; if(newref == 0) @@ -1068,6 +1072,10 @@ void initPlugins() if (ladspaPath == 0) ladspaPath = "/usr/local/lib64/ladspa:/usr/lib64/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa"; p = ladspaPath; + + if(debugMsg) + fprintf(stderr, "loadPluginLib: ladspa path:%s\n", ladspaPath); + while (*p != '\0') { const char* pe = p; while (*pe != ':' && *pe != '\0') @@ -1078,6 +1086,9 @@ void initPlugins() char* buffer = new char[n + 1]; strncpy(buffer, p, n); buffer[n] = '\0'; + if(debugMsg) + fprintf(stderr, "loadPluginLib: loading ladspa dir:%s\n", buffer); + loadPluginDir(QString(buffer)); delete[] buffer; } @@ -1371,6 +1382,9 @@ void Pipeline::apply(int ports, unsigned long nframes, float** buffer1) //for (int i = 0; i < ports; ++i) // buffer2[i] = data + i * nframes; + // p3.3.41 + //fprintf(stderr, "Pipeline::apply data: nframes:%ld %e %e %e %e\n", nframes, buffer1[0][0], buffer1[0][1], buffer1[0][2], buffer1[0][3]); + bool swap = false; for (iPluginI ip = begin(); ip != end(); ++ip) { @@ -1404,6 +1418,10 @@ void Pipeline::apply(int ports, unsigned long nframes, float** buffer1) //memcpy(buffer1[i], buffer[i], sizeof(float) * nframes); AL::dsp->cpy(buffer1[i], buffer[i], nframes); } + + // p3.3.41 + //fprintf(stderr, "Pipeline::apply after data: nframes:%ld %e %e %e %e\n", nframes, buffer1[0][0], buffer1[0][1], buffer1[0][2], buffer1[0][3]); + } //--------------------------------------------------------- @@ -1487,14 +1505,45 @@ CtrlValueType PluginI::valueType() const void PluginI::setChannels(int c) { - if (channel == c) - return; - int ni = c / _plugin->outports(); - if (ni == 0) - ni = 1; + // p3.3.41 Removed + //if (channel == c) + // return; + + // p3.3.41 + channel = c; + + //int ni = c / _plugin->outports(); + //if (ni == 0) + // ni = 1; + // p3.3.41 Some plugins have zero out ports, causing exception with the above line. + // Also, pick the least number of ins or outs, and base the number of instances on that. + unsigned long ins = _plugin->inports(); + unsigned long outs = _plugin->outports(); + /* + unsigned long minports = ~0ul; + if(outs && outs < minports) + minports = outs; + if(ins && ins < minports) + minports = ins; + if(minports == ~0ul) + minports = 1; + int ni = c / minports; + */ + int ni = 1; + if(outs) + ni = c / outs; + else + if(ins) + ni = c / ins; + + if(ni < 1) + ni = 1; + if (ni == instances) return; - channel = c; + + // p3.3.41 Moved above. + //channel = c; // remove old instances: deactivate(); @@ -1592,12 +1641,46 @@ bool PluginI::initPluginInstance(Plugin* plug, int c) _name = _plugin->name() + inst; _label = _plugin->label() + inst; - instances = channel/plug->outports(); + //instances = channel/plug->outports(); + // p3.3.41 Some plugins have zero out ports, causing exception with the above line. + // Also, pick the least number of ins or outs, and base the number of instances on that. + unsigned long ins = plug->inports(); + unsigned long outs = plug->outports(); + /* + unsigned long minports = ~0ul; + if(outs && outs < minports) + minports = outs; + if(ins && ins < minports) + minports = ins; + if(minports == ~0ul) + minports = 1; + instances = channel / minports; if(instances < 1) instances = 1; + */ + if(outs) + { + instances = channel / outs; + if(instances < 1) + instances = 1; + } + else + if(ins) + { + instances = channel / ins; + if(instances < 1) + instances = 1; + } + else + instances = 1; + handle = new LADSPA_Handle[instances]; for(int i = 0; i < instances; ++i) { + #ifdef PLUGIN_DEBUGIN + fprintf(stderr, "PluginI::initPluginInstance instance:%d\n", i); + #endif + handle[i] = _plugin->instantiate(); //if (handle[i] == 0) if(handle[i] == NULL) @@ -1711,6 +1794,41 @@ void PluginI::connect(int ports, float** src, float** dst) } } +//--------------------------------------------------------- +// deactivate +//--------------------------------------------------------- + +void PluginI::deactivate() + { + for (int i = 0; i < instances; ++i) { + _plugin->deactivate(handle[i]); + _plugin->cleanup(handle[i]); + } + } + +//--------------------------------------------------------- +// activate +//--------------------------------------------------------- + +void PluginI::activate() + { + for (int i = 0; i < instances; ++i) + _plugin->activate(handle[i]); + if (initControlValues) { + for (int i = 0; i < controlPorts; ++i) { + controls[i].val = controls[i].tmpVal; + } + } + else { + // + // get initial control values from plugin + // + for (int i = 0; i < controlPorts; ++i) { + controls[i].tmpVal = controls[i].val; + } + } + } + //--------------------------------------------------------- // setControl // set plugin instance controller value by name @@ -1736,7 +1854,10 @@ bool PluginI::setControl(const QString& s, double val) void PluginI::writeConfiguration(int level, Xml& xml) { xml.tag(level++, "plugin file=\"%s\" label=\"%s\" channel=\"%d\"", - _plugin->lib().latin1(), _plugin->label().latin1(), instances * _plugin->inports()); + //_plugin->lib().latin1(), _plugin->label().latin1(), instances * _plugin->inports()); + // p3.3.41 + _plugin->lib().latin1(), _plugin->label().latin1(), channel); + for (int i = 0; i < controlPorts; ++i) { int idx = controls[i].idx; QString s("control name=\"%1\" val=\"%2\" /"); @@ -1809,7 +1930,9 @@ bool PluginI::readConfiguration(Xml& xml, bool readPreset) QString file; QString label; if (!readPreset) - instances = 1; + //instances = 1; + // p3.3.41 + channel = 1; for (;;) { Xml::Token token(xml.parse()); @@ -1821,7 +1944,10 @@ bool PluginI::readConfiguration(Xml& xml, bool readPreset) case Xml::TagStart: if (!readPreset && _plugin == 0) { _plugin = plugins.find(file, label); - if (_plugin && initPluginInstance(_plugin, instances)) { + + //if (_plugin && initPluginInstance(_plugin, instances)) { + // p3.3.41 + if (_plugin && initPluginInstance(_plugin, channel)) { _plugin = 0; xml.parse1(); break; @@ -1875,7 +2001,9 @@ bool PluginI::readConfiguration(Xml& xml, bool readPreset) } else if (tag == "channel") { if (!readPreset) - instances = xml.s2().toInt(); + //instances = xml.s2().toInt(); + // p3.3.41 + channel = xml.s2().toInt(); } break; case Xml::TagEnd: @@ -1884,7 +2012,10 @@ bool PluginI::readConfiguration(Xml& xml, bool readPreset) _plugin = plugins.find(file, label); if (_plugin == 0) return true; - if (initPluginInstance(_plugin, instances)) + + //if (initPluginInstance(_plugin, instances)) + // p3.3.41 + if (initPluginInstance(_plugin, channel)) return true; } if (_gui) @@ -2105,7 +2236,11 @@ void PluginI::apply(int n) // controls[i].val = controls[i].tmpVal; for (int i = 0; i < instances; ++i) + { + // p3.3.41 + //fprintf(stderr, "PluginI::apply handle %d\n", i); _plugin->apply(handle[i], n); + } } //--------------------------------------------------------- @@ -2655,41 +2790,6 @@ Plugin* PluginDialog::getPlugin(QWidget* parent) return 0; } -//--------------------------------------------------------- -// deactivate -//--------------------------------------------------------- - -void PluginI::deactivate() - { - for (int i = 0; i < instances; ++i) { - _plugin->deactivate(handle[i]); - _plugin->cleanup(handle[i]); - } - } - -//--------------------------------------------------------- -// activate -//--------------------------------------------------------- - -void PluginI::activate() - { - for (int i = 0; i < instances; ++i) - _plugin->activate(handle[i]); - if (initControlValues) { - for (int i = 0; i < controlPorts; ++i) { - controls[i].val = controls[i].tmpVal; - } - } - else { - // - // get initial control values from plugin - // - for (int i = 0; i < controlPorts; ++i) { - controls[i].tmpVal = controls[i].val; - } - } - } - const char* presetOpenText = " " "Click this button to load a saved preset."; const char* presetSaveText = "Click this button to save curent parameter " diff --git a/muse/muse/waveevent.cpp b/muse/muse/waveevent.cpp index 411f2c44..98bfe8cc 100644 --- a/muse/muse/waveevent.cpp +++ b/muse/muse/waveevent.cpp @@ -440,6 +440,10 @@ void WaveEventBase::readAudio(WavePart* part, unsigned offset, float** buffer, i //sfCurFrame += f.read(channel, buffer, n, overwrite); f.seek(offset + _spos, 0); f.read(channel, buffer, n, overwrite); + + // p3.3.41 + //fprintf(stderr, "WaveEventBase::readAudio data: n:%ld %e %e %e %e\n", n, buffer[0][0], buffer[0][1], buffer[0][2], buffer[0][3]); + //return sfCurFrame; return; diff --git a/muse/muse/wavetrack.cpp b/muse/muse/wavetrack.cpp index 30945e9c..0c2298a8 100644 --- a/muse/muse/wavetrack.cpp +++ b/muse/muse/wavetrack.cpp @@ -105,8 +105,22 @@ void WaveTrack::fetchData(unsigned pos, unsigned samples, float** bp, bool doSee // add denormal bias to outdata for (int i = 0; i < channels(); ++i) for (int j = 0; j < samples; ++j) + { + bp[i][j] +=denormalBias; + + /* + // p3.3.41 + if(j & 1) + bp[i][j] -=denormalBias; + else bp[i][j] +=denormalBias; + */ + } } + + // p3.3.41 + //fprintf(stderr, "WaveTrack::fetchData data: samples:%ld %e %e %e %e\n", samples, bp[0][0], bp[0][1], bp[0][2], bp[0][3]); + _prefetchFifo.add(); } @@ -317,6 +331,10 @@ bool WaveTrack::getData(unsigned framePos, int channels, unsigned nframe, float* } } } + + // p3.3.41 + //fprintf(stderr, "WaveTrack::getData %s data: nframe:%ld %e %e %e %e\n", name().latin1(), nframe, bp[0][0], bp[0][1], bp[0][2], bp[0][3]); + } //} return true; -- cgit v1.2.3