diff options
author | IOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at> | 2021-01-20 09:10:34 +0100 |
---|---|---|
committer | IOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at> | 2021-01-20 09:10:34 +0100 |
commit | 6acadc7e322d4be0e696efd0ad7719be9f66abe9 (patch) | |
tree | 157923cb8752b4de6f8f46c1462bda3ffa2d9be8 /src | |
parent | 9ddd3730560abd20b2eed088a0b8a2fc53b31816 (diff) |
New upstream version 1.3.0+ds0
Diffstat (limited to 'src')
48 files changed, 16920 insertions, 970 deletions
diff --git a/src/AudioInterface.cpp b/src/AudioInterface.cpp index 06fe59e..8caf348 100644 --- a/src/AudioInterface.cpp +++ b/src/AudioInterface.cpp @@ -39,6 +39,7 @@ #include "JackTrip.h" #include <iostream> #include <cmath> +#include <assert.h> using std::cout; using std::endl; @@ -57,7 +58,7 @@ AudioInterface::AudioInterface(JackTrip* jacktrip, mAudioBitResolution(AudioBitResolution*8), mBitResolutionMode(AudioBitResolution), mSampleRate(gDefaultSampleRate), mBufferSizeInSamples(gDefaultBufferSizeInSamples), - mInputPacket(NULL), mOutputPacket(NULL) + mInputPacket(NULL), mOutputPacket(NULL), mLoopBack(false), mProcessingAudio(false) { #ifndef WAIR //cc @@ -85,6 +86,11 @@ AudioInterface::AudioInterface(JackTrip* jacktrip, mAPInBuffer[i] = NULL; } #endif // endwhere + + mInBufCopy.resize(mNumInChans); + for (int i=0; i<mNumInChans; i++) { + mInBufCopy[i] = new sample_t[MAX_AUDIO_BUFFER_SIZE]; // required for processing audio input + } } @@ -93,7 +99,7 @@ AudioInterface::~AudioInterface() { delete[] mInputPacket; delete[] mOutputPacket; -#ifndef WAIR // WAIR +#ifndef WAIR // NOT WAIR: for (int i = 0; i < mNumInChans; i++) { delete[] mInProcessBuffer[i]; } @@ -115,6 +121,16 @@ AudioInterface::~AudioInterface() delete[] mAPInBuffer[i]; } #endif // endwhere + + for (int i = 0; i < mProcessPluginsFromNetwork.size(); i++) { + delete mProcessPluginsFromNetwork[i]; + } + for (int i = 0; i < mProcessPluginsToNetwork.size(); i++) { + delete mProcessPluginsToNetwork[i]; + } + for (int i=0; i<mNumInChans; i++) { + delete mInBufCopy[i]; + } } @@ -153,7 +169,7 @@ void AudioInterface::setup() int nframes = getBufferSizeInSamples(); -#ifndef WAIR // WAIR +#ifndef WAIR // NOT WAIR: for (int i = 0; i < mNumInChans; i++) { mInProcessBuffer[i] = new sample_t[nframes]; // set memory to 0 @@ -208,8 +224,8 @@ void AudioInterface::callback(QVarLengthArray<sample_t*>& in_buffer, // ---------------------------------- #ifdef WAIR // WAIR - // qDebug() << "--" << mProcessPlugins.size(); - bool client = (mProcessPlugins.size() == 2); + // qDebug() << "--" << mProcessPluginsFromNetwork.size(); + bool client = (mProcessPluginsFromNetwork.size() == 2); #define COMBDSP 1 // client #define APDSP 0 // client #define DCBDSP 0 // server @@ -218,7 +234,18 @@ void AudioInterface::callback(QVarLengthArray<sample_t*>& in_buffer, } #endif // endwhere + // ==== RECEIVE AUDIO CHANNELS FROM NETWORK ==== computeProcessFromNetwork(out_buffer, n_frames); + // ============================================= + + // out_buffer is from the network and goes "out" to local audio + // hardware via JACK: + + // mAudioTesterP will be nullptr for hub server's JackTripWorker instances + if (mAudioTesterP && mAudioTesterP->getEnabled()) { + mAudioTesterP->lookForReturnPulse(out_buffer, n_frames); + } + #ifdef WAIR // WAIR // nib16 result now in mNetInBuffer #endif // endwhere @@ -229,19 +256,14 @@ void AudioInterface::callback(QVarLengthArray<sample_t*>& in_buffer, /// \todo Implement for more than one process plugin, now it just works propertely with one. /// do it chaining outputs to inputs in the buffers. May need a tempo buffer -#ifndef WAIR // WAIR - for (int i = 0; i < mNumInChans; i++) { - std::memset(mInProcessBuffer[i], 0, sizeof(sample_t) * n_frames); - std::memcpy(mInProcessBuffer[i], out_buffer[i], sizeof(sample_t) * n_frames); +#ifndef WAIR // NOT WAIR: + for (int i = 0; i < mProcessPluginsFromNetwork.size(); i++) { + ProcessPlugin* p = mProcessPluginsFromNetwork[i]; + if (p->getInited()) { + p->compute(n_frames, out_buffer.data(), out_buffer.data()); + } } - for (int i = 0; i < mNumOutChans; i++) { - std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * n_frames); - } - - for (int i = 0; i < mProcessPlugins.size(); i++) { - mProcessPlugins[i]->compute(n_frames, mInProcessBuffer.data(), mOutProcessBuffer.data()); - } -#else // WAIR +#else // WAIR: for (int i = 0; i < ((mNumNetRevChans)?mNumNetRevChans:mNumOutChans); i++) { std::memset(mOutProcessBuffer[i], 0, sizeof(sample_t) * n_frames); } @@ -257,14 +279,44 @@ void AudioInterface::callback(QVarLengthArray<sample_t*>& in_buffer, } // nib16 to cib16 - if (mNumNetRevChans && client) mProcessPlugins[COMBDSP]->compute(n_frames, - mInProcessBuffer.data(), mOutProcessBuffer.data()); + if (mNumNetRevChans && client) { + mProcessPluginsFromNetwork[COMBDSP]->compute(n_frames, mInProcessBuffer.data(), mOutProcessBuffer.data()); + } // compute cob16 #endif // endwhere - // 3) Finally, send packets to peer - // -------------------------------- - computeProcessToNetwork(in_buffer, n_frames); + // 3) Send packets to network: + // mAudioTesterP will be nullptr for hub server's JackTripWorker instances: + bool audioTesting = (mAudioTesterP && mAudioTesterP->getEnabled()); + int nop = mProcessPluginsToNetwork.size(); // number of OUTGOING processing modules + if (nop>0 || audioTesting) { // cannot modify in_buffer, so make a copy + // in_buffer is "in" from local audio hardware via JACK + if (mInBufCopy.size() < mNumInChans) { // created in constructor above + std::cerr << "*** AudioInterface.cpp: Number of Input Channels changed - insufficient room reserved\n"; + exit(1); + } + if (MAX_AUDIO_BUFFER_SIZE < n_frames) { // allocated in constructor above + std::cerr << "*** AudioInterface.cpp: n_frames = " << n_frames + << " larger than expected max = " << MAX_AUDIO_BUFFER_SIZE << "\n"; + exit(1); + } + for (int i=0; i<mNumInChans; i++) { + std::memcpy(mInBufCopy[i], in_buffer[i], sizeof(sample_t) * n_frames); + } + for (int i = 0; i < nop; i++) { + // process all outgoing channels with ProcessPlugins: + ProcessPlugin* p = mProcessPluginsToNetwork[i]; + if (p->getInited()) { + p->compute(n_frames, mInBufCopy.data(), mInBufCopy.data()); + } + } + if (audioTesting) { + mAudioTesterP->writeImpulse(mInBufCopy, n_frames); // writes last channel of mInBufCopy with test impulse + } + computeProcessToNetwork(mInBufCopy, n_frames); + } else { // copy saved if no plugins and no audio testing in progress: + computeProcessToNetwork(in_buffer, n_frames); // send processed input audio to network - OUTGOING + } #ifdef WAIR // WAIR // aib2 + cob16 to nob16 @@ -302,7 +354,7 @@ void AudioInterface::callback(QVarLengthArray<sample_t*>& in_buffer, for (int i = 0; i < mNumOutChans; i++) { std::memset(out_buffer[i], 0, sizeof(sample_t) * n_frames); } - mProcessPlugins[APDSP]->compute(n_frames, mAPInBuffer.data(), out_buffer.data()); + mProcessPluginsFromNetwork[APDSP]->compute(n_frames, mAPInBuffer.data(), out_buffer.data()); // compute ap2 into aob2 //#define ADD_DIRECT @@ -319,8 +371,7 @@ void AudioInterface::callback(QVarLengthArray<sample_t*>& in_buffer, } #endif // endwhere - - ///************PROTORYPE FOR CELT************************** + ///************PROTOTYPE FOR CELT************************** ///******************************************************** /* CELTMode* mode; @@ -337,6 +388,28 @@ void AudioInterface::callback(QVarLengthArray<sample_t*>& in_buffer, } +//******************************************************************************* +void AudioInterface::broadcastCallback(QVarLengthArray<sample_t*>& mon_buffer, + unsigned int n_frames) +{ + /// \todo cast *mInBuffer[i] to the bit resolution + // Output Process (from NETWORK to JACK) + // ---------------------------------------------------------------- + // Read Audio buffer from RingBuffer (read from incoming packets) + mJackTrip->receiveBroadcastPacket(mOutputPacket); + // Extract separate channels to send to Jack + for (int i = 0; i < mNumOutChans; i++) { + sample_t* tmp_sample = mon_buffer[i]; //sample buffer for channel i + for (unsigned int j = 0; j < n_frames; j++) { + // Change the bit resolution on each sample + fromBitToSampleConversion( + // use interleaved channel layout + //&mOutputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], + &mOutputPacket[(j*mBitResolutionMode*mNumOutChans) + (i*mBitResolutionMode)], + &tmp_sample[j], mBitResolutionMode ); + } + } +} //******************************************************************************* // Before sending and reading to Jack, we have to round to the sample resolution @@ -359,7 +432,9 @@ void AudioInterface::computeProcessFromNetwork(QVarLengthArray<sample_t*>& out_b for (unsigned int j = 0; j < n_frames; j++) { // Change the bit resolution on each sample fromBitToSampleConversion( - &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], + // use interleaved channel layout + //&mOutputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], + &mOutputPacket[(j*mBitResolutionMode*mNumOutChans) + (i*mBitResolutionMode)], &tmp_sample[j], mBitResolutionMode ); } } @@ -371,13 +446,15 @@ void AudioInterface::computeProcessFromNetwork(QVarLengthArray<sample_t*>& out_b //-------- // This should be faster for 32 bits //std::memcpy(mOutBuffer[i], &mOutputPacket[i*mSizeInBytesPerChannel], - // mSizeInBytesPerChannel); + // mSizeInBytesPerChannel); //-------- sample_t* tmp_sample = out_buffer[i]; //sample buffer for channel i for (unsigned int j = 0; j < n_frames; j++) { // Change the bit resolution on each sample fromBitToSampleConversion( - &mOutputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], + // use interleaved channel layout + //&mOutputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], + &mOutputPacket[(j*mBitResolutionMode*mNumOutChans) + (i*mBitResolutionMode)], &tmp_sample[j], mBitResolutionMode ); } } @@ -406,7 +483,9 @@ void AudioInterface::computeProcessToNetwork(QVarLengthArray<sample_t*>& in_buff tmp_result = INGAIN*tmp_sample[j] + COMBGAIN*tmp_process_sample[j]; fromSampleToBitConversion( &tmp_result, - &mInputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], + // use interleaved channel layout + //&mInputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], + &mInputPacket[(j*mBitResolutionMode*mNumOutChans) + (i*mBitResolutionMode)], mBitResolutionMode ); } } @@ -417,7 +496,7 @@ void AudioInterface::computeProcessToNetwork(QVarLengthArray<sample_t*>& in_buff //-------- // This should be faster for 32 bits //std::memcpy(&mInputPacket[i*mSizeInBytesPerChannel], mInBuffer[i], - // mSizeInBytesPerChannel); + // mSizeInBytesPerChannel); //-------- sample_t* tmp_sample = in_buffer[i]; //sample buffer for channel i sample_t* tmp_process_sample = mOutProcessBuffer[i]; //sample buffer from the output process @@ -428,14 +507,15 @@ void AudioInterface::computeProcessToNetwork(QVarLengthArray<sample_t*>& in_buff tmp_result = tmp_sample[j] + tmp_process_sample[j]; fromSampleToBitConversion( &tmp_result, - &mInputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], + // use interleaved channel layout + //&mInputPacket[(i*mSizeInBytesPerChannel) + (j*mBitResolutionMode)], + &mInputPacket[(j*mBitResolutionMode*mNumOutChans) + (i*mBitResolutionMode)], mBitResolutionMode ); } } // Send Audio buffer to Network mJackTrip->sendNetworkPacket( mInputPacket ); -} - +} // /computeProcessToNetwork //******************************************************************************* // This function quantize from 32 bit to a lower bit resolution @@ -448,22 +528,23 @@ void AudioInterface::fromSampleToBitConversion int8_t tmp_8; uint8_t tmp_u8; // unsigned to quantize the remainder in 24bits int16_t tmp_16; - sample_t tmp_sample; + double tmp_sample; sample_t tmp_sample16; sample_t tmp_sample8; switch (targetBitResolution) { case BIT8 : // 8bit integer between -128 to 127 - tmp_sample = floor( (*input) * 128.0 ); // 2^7 = 128.0 + tmp_sample = std::max(-127.0, std::min(127.0, std::round( (*input) * 127.0 ))); // 2^7 = 128 tmp_8 = static_cast<int8_t>(tmp_sample); std::memcpy(output, &tmp_8, 1); // 8bits = 1 bytes break; case BIT16 : // 16bit integer between -32768 to 32767 - tmp_sample = floor( (*input) * 32768.0 ); // 2^15 = 32768.0 + // original scaling: tmp_sample = floor( (*input) * 32768.0 ); // 2^15 = 32768.0 + tmp_sample = std::max(-32767.0, std::min(32767.0, std::round( (*input) * 32767.0 ))); // 2^15 = 32768 tmp_16 = static_cast<int16_t>(tmp_sample); - std::memcpy(output, &tmp_16, 2); // 16bits = 2 bytes + std::memcpy(output, &tmp_16, 2); // 2 bytes output in Little Endian order (LSB -> smallest address) break; case BIT24 : // To convert to 24 bits, we first quantize the number to 16bit @@ -483,7 +564,10 @@ void AudioInterface::fromSampleToBitConversion std::memcpy(output+2, &tmp_u8, 1); // 8bits = 1 bytes break; case BIT32 : - std::memcpy(output, input, 4); // 32bit = 4 bytes + tmp_sample = *input; + // not necessary yet: + // tmp_sample = std::max(-1.0, std::min(1.0, tmp_sample)); + std::memcpy(output, &tmp_sample, 4); // 32bit = 4 bytes break; } } @@ -532,32 +616,71 @@ void AudioInterface::fromBitToSampleConversion //******************************************************************************* -void AudioInterface::appendProcessPlugin(ProcessPlugin* plugin) +void AudioInterface::appendProcessPluginToNetwork(ProcessPlugin* plugin) +{ + if (not plugin) { return; } + int nTestChans = (mAudioTesterP && mAudioTesterP->getEnabled()) ? 1 : 0; + int nPluginChans = mNumInChans - nTestChans; + assert(nTestChans==0 || (mAudioTesterP->getSendChannel() == mNumInChans-1)); + if (plugin->getNumInputs() < nPluginChans) { + std::cerr << "*** AudioInterface.cpp: appendProcessPluginToNetwork: ProcessPlugin " + << typeid(plugin).name() << " REJECTED due to having " + << plugin->getNumInputs() << " inputs, while the audio to JACK needs " + << nPluginChans << " inputs\n"; + return; + } + mProcessPluginsToNetwork.append(plugin); +} + +void AudioInterface::appendProcessPluginFromNetwork(ProcessPlugin* plugin) { - /// \todo check that channels in ProcessPlugins are less or same that jack channels - if ( plugin->getNumInputs() ) {} - mProcessPlugins.append(plugin); + if (not plugin) { return; } + int nTestChans = (mAudioTesterP && mAudioTesterP->getEnabled()) ? 1 : 0; + int nPluginChans = mNumOutChans - nTestChans; + assert(nTestChans==0 || (mAudioTesterP->getSendChannel() == mNumOutChans-1)); + if (plugin->getNumOutputs() > nPluginChans) { + std::cerr << "*** AudioInterface.cpp: appendProcessPluginFromNetwork: ProcessPlugin " + << typeid(plugin).name() << " REJECTED due to having " + << plugin->getNumOutputs() << " inputs, while the JACK audio output requires " + << nPluginChans << " outputs\n"; + return; + } + mProcessPluginsFromNetwork.append(plugin); } +void AudioInterface::initPlugins() +{ + int nPlugins = mProcessPluginsFromNetwork.size() + mProcessPluginsToNetwork.size(); + if (nPlugins > 0) { + std::cout << "Initializing Faust plugins (have " << nPlugins + << ") at sampling rate " << mSampleRate << "\n"; + for (ProcessPlugin* plugin : mProcessPluginsFromNetwork) { + plugin->init(mSampleRate); + } + for (ProcessPlugin* plugin : mProcessPluginsToNetwork) { + plugin->init(mSampleRate); + } + } +} //******************************************************************************* AudioInterface::samplingRateT AudioInterface::getSampleRateType() const { - uint32_t rate = getSampleRate(); + int32_t rate = getSampleRate(); - if ( rate == 22050 ) { + if ( 100 > qAbs(rate - 22050) ) { return AudioInterface::SR22; } - else if ( rate == 32000 ) { + else if ( 100 > qAbs(rate - 32000) ) { return AudioInterface::SR32; } - else if ( rate == 44100 ) { + else if ( 100 > qAbs(rate - 44100) ) { return AudioInterface::SR44; } - else if ( rate == 48000 ) { + else if ( 100 > qAbs(rate - 48000) ) { return AudioInterface::SR48; } - else if ( rate == 88200 ) { + else if ( 100 > qAbs(rate - 88200) ) { return AudioInterface::SR88; } - else if ( rate == 96000 ) { + else if ( 100 > qAbs(rate - 96000) ) { return AudioInterface::SR96; } - else if ( rate == 19200 ) { + else if ( 100 > qAbs(rate - 19200) ) { return AudioInterface::SR192; } return AudioInterface::UNDEF; diff --git a/src/AudioInterface.h b/src/AudioInterface.h index c4b953c..f5089eb 100644 --- a/src/AudioInterface.h +++ b/src/AudioInterface.h @@ -40,6 +40,7 @@ #include "ProcessPlugin.h" #include "jacktrip_types.h" +#include "AudioTester.h" #include <QVarLengthArray> #include <QVector> @@ -115,16 +116,33 @@ public: * \param in_buffer Array of output audio samplers for each channel. The user * is reponsible to check that each channel has n_frames samplers */ + virtual void broadcastCallback(QVarLengthArray<sample_t*>& mon_buffer, + unsigned int n_frames); virtual void callback(QVarLengthArray<sample_t*>& in_buffer, QVarLengthArray<sample_t*>& out_buffer, unsigned int n_frames); - /** \brief Append a ProcessPlugin. The order of processing is determined by - * the order by which appending is done. - * \param plugin a ProcesPlugin smart pointer. Create the object instance + /** \brief appendProcessPluginToNetwork(): Append a ProcessPlugin for outgoing audio. + * The processing order equals order they were appended. + * This processing is in the JackTrip client before sending to the network. + * \param plugin a ProcessPlugin smart pointer. Create the object instance * using something like:\n * <tt>std::tr1::shared_ptr<ProcessPluginName> loopback(new ProcessPluginName);</tt> */ - virtual void appendProcessPlugin(ProcessPlugin* plugin); + virtual void appendProcessPluginToNetwork(ProcessPlugin* plugin); + /** \brief appendProcessPluginFromNetwork(): + * Same as appendProcessPluginToNetwork() except that these plugins operate + * on the audio received from the network (typically from a JackTrip server). + * The complete processing chain then looks like this: + * audio -> JACK -> JackTrip client -> processPlugin to network + * -> remote JackTrip server + * -> JackTrip client -> processPlugin from network -> JACK -> audio + */ + virtual void appendProcessPluginFromNetwork(ProcessPlugin* plugin); + /** \brief initPlugins(): + * Initialize all ProcessPlugin modules. + * The audio sampling rate (mSampleRate) must be set at this time. + */ + void initPlugins(); virtual void connectDefaultPorts() = 0; /** \brief Convert a 32bit number (sample_t) into one of the bit resolution * supported (audioBitResolutionT). @@ -160,6 +178,9 @@ public: { mBufferSizeInSamples = buf_size; } /// \brief Set Client Name to something different that the default (JackTrip) virtual void setClientName(QString ClientName) = 0; + virtual void setLoopBack(bool b) { mLoopBack = b; } + virtual void enableBroadcastOutput() {} + virtual void setAudioTesterP(AudioTester* atp) { mAudioTesterP = atp; } //------------------------------------------------------------------ //--------------GETTERS--------------------------------------------- @@ -209,17 +230,24 @@ private: QVarLengthArray<sample_t*> mNetInBuffer; ///< Vector of Input buffers/channel read from net QVarLengthArray<sample_t*> mAPInBuffer; ///< Vector of Input buffers/channel for AllPass input #endif // endwhere + QVarLengthArray<sample_t*> mInBufCopy; ///< needed in callback() to modify JACK audio input int mAudioBitResolution; ///< Bit resolution in audio samples AudioInterface::audioBitResolutionT mBitResolutionMode; ///< Bit resolution (audioBitResolutionT) mode uint32_t mSampleRate; ///< Sampling Rate uint32_t mDeviceID; ///< RTAudio DeviceID uint32_t mBufferSizeInSamples; ///< Buffer size in samples size_t mSizeInBytesPerChannel; ///< Size in bytes per audio channel - QVector<ProcessPlugin*> mProcessPlugins; ///< Vector of ProcesPlugin<EM>s</EM> + QVector<ProcessPlugin*> mProcessPluginsFromNetwork; ///< Vector of ProcessPlugin<EM>s</EM> + QVector<ProcessPlugin*> mProcessPluginsToNetwork; ///< Vector of ProcessPlugin<EM>s</EM> QVarLengthArray<sample_t*> mInProcessBuffer;///< Vector of Input buffers/channel for ProcessPlugin QVarLengthArray<sample_t*> mOutProcessBuffer;///< Vector of Output buffers/channel for ProcessPlugin int8_t* mInputPacket; ///< Packet containing all the channels to read from the RingBuffer int8_t* mOutputPacket; ///< Packet containing all the channels to send to the RingBuffer + bool mLoopBack; + AudioTester* mAudioTesterP { nullptr }; +protected: + bool mProcessingAudio; ///< Set when processing an audio callback buffer pair + const uint32_t MAX_AUDIO_BUFFER_SIZE = 8192; }; #endif // __AUDIOINTERFACE_H__ diff --git a/src/AudioTester.cpp b/src/AudioTester.cpp new file mode 100644 index 0000000..136c1d4 --- /dev/null +++ b/src/AudioTester.cpp @@ -0,0 +1,195 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file AudioTester.cpp + * \author Julius Smith + * \license MIT + * \date Aug-Oct 2020 + */ + +#include "AudioTester.h" +#include <assert.h> + +// Called 1st in Audiointerface.cpp +void AudioTester::lookForReturnPulse(QVarLengthArray<sample_t*>& out_buffer, + unsigned int n_frames) { + if (not enabled) { + std::cerr << "*** AudioTester.h: lookForReturnPulse: NOT ENABLED\n"; + return; + } + if (impulsePending) { // look for return impulse in channel sendChannel: + assert(sendChannel<out_buffer.size()); + for (uint n=0; n<n_frames; n++) { + float amp = out_buffer[sendChannel][n]; + if (amp > 0.5 * ampCellHeight) { // got something + int cellNum = getImpulseCellNum(out_buffer[sendChannel][n]); + if (cellNum != pendingCell) { // not our impulse! + std::cerr << + "*** AudioTester.h: computeProcessFromNetwork: Received pulse amplitude " + << amp << " (cell " << cellNum << ") while looking for cell " + << pendingCell << "\n"; + + if (cellNum > pendingCell) { // we missed it + std::cerr << " - ABORTING CURRENT PULSE\n"; + impulsePending = false; + } else { // somehow we got the previous pulse again - repeated packet or underrun-caused repetition (old buffer) + std::cerr << " - IGNORING FOUND PULSE WAITING FURTHER\n"; + } + } else { // found our impulse: + int64_t elapsedSamples = -1; + if (n >= n_frames-1) { + // Impulse timestamp didn't make it so we skip this one. + } else { + float sampleCountWhenImpulseSent = - 32768.0f * out_buffer[sendChannel][n+1]; + elapsedSamples = sampleCountSinceImpulse + n - int64_t(sampleCountWhenImpulseSent); + sampleCountSinceImpulse = 1; // reset sample counter between impulses + roundTripCount += 1.0; + } + // int64_t curTimeUS = timeMicroSec(); // time since launch in us + // int64_t impulseDelayUS = curTimeUS - ImpulseTimeUS; + // float impulseDelaySec = float(impulseDelayUS) * 1.0e-6; + // float impulseDelayBuffers = impulseDelaySec / (float(n_frames)/float(sampleRate)); + // int64_t impulseDelayMS = (int64_t)round(double(impulseDelayUS)/1000.0); + if (elapsedSamples > 0) { // found impulse and reset, time to print buffer results: + double elapsedSamplesMS = 1000.0 * double(elapsedSamples)/double(sampleRate); // ms + extendLatencyHistogram(elapsedSamplesMS); + if (roundTripCount > 1.0) { + double prevSum = roundTripMean * (roundTripCount-1.0); // undo previous normalization + roundTripMean = (prevSum + elapsedSamplesMS) / roundTripCount; // add latest and renormalize + double prevSumSq = roundTripMeanSquare * (roundTripCount-1.0); // undo previous normalization + roundTripMeanSquare = (prevSumSq + elapsedSamplesMS*elapsedSamplesMS) / roundTripCount; + } else { // just getting started: + roundTripMean = elapsedSamplesMS; + roundTripMeanSquare = elapsedSamplesMS * elapsedSamplesMS; + } + if (roundTripCount == 1.0) { + printf("JackTrip Test Mode (option -x printIntervalInSeconds=%0.3f)\n",printIntervalSec); + printf("\tA test impulse-train is output on channel %d (from 0) with repeatedly ramping amplitude\n", + sendChannel); + if (printIntervalSec == 0.0) { + printf("\tPrinting each audio buffer round-trip latency in ms followed by cumulative (mean and [standard deviation])"); + } else { + printf("\tPrinting cumulative mean and [standard deviation] of audio round-trip latency in ms"); + printf(" every %0.3f seconds", printIntervalSec); + } + printf(" after skipping first %d buffers:\n", bufferSkipStart); + // not printing this presently: printf("( * means buffer skipped due missing timestamp or lost impulse)\n"); + lastPrintTimeUS = timeMicroSec(); + } + //printf("%d (%d) ", elapsedSamplesMS, impulseDelayMS); // measured time is "buffer time" not sample time + int64_t curTimeUS = timeMicroSec(); // time since launch in us + double timeSinceLastPrintUS = double(curTimeUS - lastPrintTimeUS); + double stdDev = sqrt(std::max(0.0, (roundTripMeanSquare - (roundTripMean*roundTripMean)))); + if (timeSinceLastPrintUS >= printIntervalSec * 1.0e6) { + if (printIntervalSec == 0.0) { printf("%0.1f (", elapsedSamplesMS); } + printf("%0.1f [%0.1f]", roundTripMean, stdDev); + if (printIntervalSec == 0.0) { printf(") "); } else { printf(" "); } + lastPrintTimeUS = curTimeUS; + if (printIntervalSec >= 1.0) { // print histogram + std::cout << "\n" << getLatencyHistogramString() << "\n"; + } + } + std::cout << std::flush; + } else { + // not printing this presently: printf("* "); // we got the impulse but lost its timestamp in samples + } + impulsePending = false; + } // found our impulse + // remain pending until timeout, hoping to find our return pulse + } // got something + } // loop over samples + sampleCountSinceImpulse += n_frames; // gets reset to 1 when impulse is found, counts freely until then + } // ImpulsePending +} + +// Called 2nd in Audiointerface.cpp +void AudioTester::writeImpulse(QVarLengthArray<sample_t*>& mInBufCopy, + unsigned int n_frames) { + if (not enabled) { + std::cerr << "*** AudioTester.h: writeImpulse: NOT ENABLED\n"; + return; + } + if (bufferSkip <= 0) { // send test signals (-x option) + bool sendImpulse; + if (impulsePending) { + sendImpulse = false; // unless: + const uint64_t timeOut = 500e3; // time out after waiting 500 ms + if (timeMicroSec() > (impulseTimeUS + timeOut)) { + sendImpulse = true; + std::cout << "\n*** Audio Latency Test (-x): TIMED OUT waiting for return impulse *** sending a new one\n"; + } + } else { // time for the next repeating impulse: + sendImpulse = true; + } + if (sendImpulse) { + assert(sendChannel < mInBufCopy.size()); + mInBufCopy[sendChannel][0] = getImpulseAmp(); + for (uint n=1; n<n_frames; n++) { + mInBufCopy[sendChannel][n] = 0; + } + impulsePending = true; + impulseTimeUS = timeMicroSec(); + impulseTimeSamples = sampleCountSinceImpulse; // timer in samples for current impulse loopback test + // Also send impulse time: + if (n_frames>1) { // always true? + mInBufCopy[sendChannel][1] = -float(impulseTimeSamples)/32768.0f; // survives if there is no digital processing at the server + } else { + std::cerr << "\n*** AudioTester.h: Timestamp cannot fit into a lenth " << n_frames << " buffer ***\n"; + } + } else { + mInBufCopy[sendChannel][0] = 0.0f; // send zeros until a new impulse is needed + if (n_frames>1) { + mInBufCopy[sendChannel][1] = 0.0f; + } + } + } else { + bufferSkip--; + } +} + +void AudioTester::printHelp(char* command, [[maybe_unused]] char helpCase) { + std::cout << "HELP for \"" << command << " printIntervalSec\" // (end-of-line comments start with `//'):\n"; + std::cout << "\n"; + std::cout << "Print roundtrip audio delay statistics for the highest-numbered audio channel every printIntervalSec seconds,\n"; + std::cout << "including an ASCII latency histogram if printIntervalSec is 1.0 or more.\n"; + std::cout << "\n"; + std::cout << "A test impulse is sent to the server in the last audio channel,\n"; + std::cout << " the number of samples until it returns is measured, and this repeats.\n"; + std::cout << "The jacktrip server must provide audio loopback (e.g., -p4).\n"; + std::cout << "The cumulative mean and standard-deviation (\"statistics\") are computed for the measured loopback times,\n"; + std::cout << " and printed every printIntervalSec seconds.\n"; + std::cout << "If printIntervalSec is zero, the roundtrip-time and statistics in milliseconds are printed for each individual impulse.\n"; + std::cout << "If printIntervalSec is positive, statistics are printed after each print interval, with no individual measurements.\n"; + std::cout << "If printIntervalSec is 1.0 or larger, a cumulative histogram of all impulse roundtrip-times is printed as well.\n"; + std::cout << "The first 100 audio buffers are skipped in order to measure only steady-state network-audio-delay performance.\n"; + std::cout << "Lower audio channels are not affected, enabling latency measurement and display during normal operation.\n"; +} diff --git a/src/AudioTester.h b/src/AudioTester.h new file mode 100644 index 0000000..4a08f78 --- /dev/null +++ b/src/AudioTester.h @@ -0,0 +1,220 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file AudioTester.h + * \author Julius Smith + * \license MIT + * \date Aug-Oct 2020 + */ + +#pragma once + +#include "jacktrip_types.h" // sample_t + +#include <iostream> +//#include <ctime> +#include <chrono> +#include <cstdint> +#include <cmath> +#include <string> +#include <map> + +#include <QVarLengthArray> + +class AudioTester +{ + bool enabled { false }; + float printIntervalSec { 1.0f }; + int sendChannel { 0 }; + + bool impulsePending { false }; + int64_t lastPrintTimeUS { 0 }; + int64_t impulseTimeUS { 0 }; + int64_t impulseTimeSamples { 0 }; + uint64_t sampleCountSinceImpulse { 1 }; // 0 not used + double roundTripMean { 0.0 }; + double roundTripMeanSquare { 0.0 }; + double roundTripCount { 0.0 }; + const int bufferSkipStart { 100 }; + int bufferSkip { bufferSkipStart }; + const float impulseAmplitude { 0.1f }; + const int numAmpCells { 10 }; + const float ampCellHeight { impulseAmplitude/numAmpCells }; + + const double latencyHistogramCellWidth { 5.0 }; // latency range in ms covered one cell + const double latencyHistogramCellMin { 0.0 }; + const double latencyHistogramCellMax { 19.0 }; // in cells, so 5x this is max latency in ms + const int latencyHistogramPrintCountMax { 72 }; // normalize when asterisks exceed this number + + int pendingCell { 0 }; // 0 is not used + float sampleRate { 48000.0f }; + +public: + AudioTester() {} + ~AudioTester() = default; + + void lookForReturnPulse(QVarLengthArray<sample_t*>& out_buffer, + unsigned int n_frames); + + void writeImpulse(QVarLengthArray<sample_t*>& mInBufCopy, + unsigned int n_frames); + + bool getEnabled() { return enabled; } + void setEnabled(bool e) { enabled = e; } + void setPrintIntervalSec(float s) { printIntervalSec = s; } + void setSendChannel(int c) { sendChannel = c; } + int getSendChannel() { return sendChannel; } + int getPendingCell() { return pendingCell; } + void setPendingCell(int pc) { pendingCell = pc; } + void setSampleRate(float fs) { sampleRate = fs; } + int getBufferSkip() { return bufferSkip; } // used for debugging breakpoints + void printHelp(char* command, char helpCase); + +private: + + float getImpulseAmp() { + pendingCell += 1; // only called when no impulse is pending + if (pendingCell >= numAmpCells) { + pendingCell = 1; // wrap-around, not using zero + } + float imp = float(pendingCell) * (impulseAmplitude/float(numAmpCells)); + return imp; + } + + int getImpulseCellNum(float amp) { + float ch = ampCellHeight; + float cell = amp / ch; + int iCell = int(std::floor(0.5f + cell)); + if (iCell > numAmpCells - 1) { + std::cerr << "*** AudioTester.h: getImpulseCellNum("<<amp<<"): Return pulse amplitude is beyond maximum expected\n"; + iCell = numAmpCells-1; + } else if (iCell < 0) { + std::cerr << "*** AudioTester.h: getImpulseCellNum("<<amp<<"): Return pulse amplitude is below minimum expected\n"; + iCell = 0; + } + return iCell; + } + + uint64_t timeMicroSec() { +#if 1 + using namespace std::chrono; + // return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); + return duration_cast<microseconds>(high_resolution_clock::now().time_since_epoch()).count(); +#else + clock_t tics_since_launch = std::clock(); + double timeUS = double(tics_since_launch)/double(CLOCKS_PER_SEC); + return (uint64_t)timeUS; +#endif + } + + std::map<int, int> latencyHistogram; + + std::map<int, int> getLatencyHistogram() { + return latencyHistogram; + } + + void extendLatencyHistogram(double latencyMS) { + int latencyCell = static_cast<int>(floor(std::max(latencyHistogramCellMin, + std::min(latencyHistogramCellMax, + std::floor(latencyMS / latencyHistogramCellWidth))))); + latencyHistogram[latencyCell] += 1; + } + + int latencyHistogramCountMax() { + int lhMax = 0; + int histStart = latencyHistogramFirstNonzeroCellIndex(); + int histLast = latencyHistogramLastNonzeroCellIndex(); + for (int i = histStart; i <= histLast; ++i) { + int lhi = latencyHistogram[i]; + if (lhi > lhMax) { + lhMax = lhi; + } + } + return lhMax; + } + + int latencyHistogramFirstNonzeroCellIndex() { + for (int i=latencyHistogramCellMin; i <= latencyHistogramCellMax; i++) { + if (latencyHistogram[i]>0) { + return i; + } + } + std::cerr << "*** AudioTester: LATENCY HISTOGRAM IS EMPTY!\n"; + return -1; + } + + int latencyHistogramLastNonzeroCellIndex() { + for (int i=latencyHistogramCellMax; i>=latencyHistogramCellMin; i--) { + if (latencyHistogram[i]>0) { + return i; + } + } + std::cerr << "*** AudioTester: LATENCY HISTOGRAM IS EMPTY!\n"; + return -1; + } + + std::string getLatencyHistogramString() { + int histStart = latencyHistogramFirstNonzeroCellIndex(); + int histLast = latencyHistogramLastNonzeroCellIndex(); + std::string marker = "*"; + double histScale = 1.0; + int lhcm = latencyHistogramCountMax(); + int lhpcm = latencyHistogramPrintCountMax; + bool normalizing = lhpcm < lhcm; + if (normalizing) { + marker = "#"; + histScale = double(lhpcm) / double(lhcm); + } + std::string rows = ""; + for (int i = histStart; i <= histLast; ++i) { + int hi = latencyHistogram[i]; + int hin = int(std::round(histScale * double(hi))); + std::string istrm1 = std::to_string(int(latencyHistogramCellWidth * double(i))); + std::string istr = std::to_string(int(latencyHistogramCellWidth * double(i+1))); + // std::string histr = boost::format("%02d",hi); + std::string histr = std::to_string(hi); + while (histr.length()<3) { + histr = " " + histr; + } + std::string row = "["+istrm1+"-"+istr+"ms]="+histr+":"; + for (int j=0; j<hin; j++) { + row += marker; + } + rows += row + "\n"; + } + if (histLast == latencyHistogramCellMax) { + rows += " and above\n"; + } + return rows; + } + +}; diff --git a/src/Compressor.cpp b/src/Compressor.cpp new file mode 100644 index 0000000..a1ed346 --- /dev/null +++ b/src/Compressor.cpp @@ -0,0 +1,57 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file Compressor.cpp + * \author Julius Smith, based on LoopBack.h + * \date July 2008 + */ + + +#include "Compressor.h" + +#include <iostream> + +//******************************************************************************* +void Compressor::compute(int nframes, float** inputs, float** outputs) +{ + if (not inited) { + std::cerr << "*** Compressor " << this << ": init never called! Doing it now.\n"; + if (fSamplingFreq <= 0) { + fSamplingFreq = 48000; + std::cout << "Compressor " << this << ": *** HAD TO GUESS the sampling rate (chose 48000 Hz) ***\n"; + } + init(fSamplingFreq); + } + for ( int i = 0; i < mNumChannels; i++ ) { + compressorP[i]->compute(nframes, &inputs[i], &outputs[i]); + } +} diff --git a/src/Compressor.h b/src/Compressor.h new file mode 100644 index 0000000..d5f41ea --- /dev/null +++ b/src/Compressor.h @@ -0,0 +1,148 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file Compressor.h + * \author Julius Smith, starting from Limiter.h + * \date August 2020 + */ + + +/** \brief Applies compressor_mono from the faustlibraries distribution, compressors.lib + * + */ +#ifndef __COMPRESSOR_H__ +#define __COMPRESSOR_H__ + +#include "ProcessPlugin.h" +#include "compressordsp.h" +#include "CompressorPresets.h" +#include <vector> + +/** \brief A Compressor reduces the output dynamic range when the + * signal level exceeds the threshold. + */ +class Compressor : public ProcessPlugin +{ +public: + /// \brief The class constructor sets the number of audio channels and default parameters. + Compressor(int numchans, // xtor + bool verboseIn = false, + float ratioIn = 2.0f, + float thresholdDBIn = -24.0f, + float attackMSIn = 15.0f, + float releaseMSIn = 40.0f, + float makeUpGainDBIn = 2.0f) + : mNumChannels(numchans) + , ratio(ratioIn) + , thresholdDB(thresholdDBIn) + , attackMS(attackMSIn) + , releaseMS(releaseMSIn) + , makeUpGainDB(makeUpGainDBIn) + { + setVerbose(verboseIn); + // presets.push_back(std::make_unique<CompressorPreset>(ratio,thresholdDB,attackMS,releaseMS,makeUpGainDB)); + for ( int i = 0; i < mNumChannels; i++ ) { + compressorP.push_back(new compressordsp); + compressorUIP.push_back(new APIUI); // #included in compressordsp.h + compressorP[i]->buildUserInterface(compressorUIP[i]); + } + } + + Compressor(int numchans, // xtor + bool verboseIn = false, + CompressorPreset preset = CompressorPresets::voice) : + Compressor(numchans,verboseIn, + preset.ratio, + preset.thresholdDB, + preset.attackMS, + preset.releaseMS, + preset.makeUpGainDB) + {} + /// \brief The class destructor + virtual ~Compressor() { + for ( int i = 0; i < mNumChannels; i++ ) { + delete compressorP[i]; + delete compressorUIP[i]; + } + compressorP.clear(); + compressorUIP.clear(); + } + + // void setParamAllChannels(std::string& pName, float p) { + void setParamAllChannels(const char pName[], float p) { + for ( int i = 0; i < mNumChannels; i++ ) { + int ndx = compressorUIP[i]->getParamIndex(pName); + if (ndx >= 0) { + compressorUIP[i]->setParamValue(ndx, p); + if (verbose) { + std::cout << "Compressor.h: parameter " << pName << " set to " << p << " on audio channel " << i << "\n"; + } + } else { + std::cerr << "*** Compressor.h: Could not find parameter named " << pName << "\n"; + } + } + } + + void init(int samplingRate) override { + ProcessPlugin::init(samplingRate); + if (samplingRate != fSamplingFreq) { + std::cerr << "Sampling rate not set by superclass!\n"; + std::exit(1); } + fs = float(fSamplingFreq); + for ( int i = 0; i < mNumChannels; i++ ) { + compressorP[i]->init(fs); // compression filter parameters depend on sampling rate + } + setParamAllChannels("Ratio", ratio); + setParamAllChannels("Threshold", thresholdDB); + setParamAllChannels("Attack", attackMS); + setParamAllChannels("Release", releaseMS); + setParamAllChannels("MakeUpGain", makeUpGainDB); + inited = true; + } + + int getNumInputs() override { return(mNumChannels); } + int getNumOutputs() override { return(mNumChannels); } + void compute(int nframes, float** inputs, float** outputs) override; + +private: + float fs; + int mNumChannels; + std::vector<compressordsp*> compressorP; + std::vector<APIUI*> compressorUIP; + float ratio; + float thresholdDB; + float attackMS; + float releaseMS; + float makeUpGainDB; +}; + +#endif diff --git a/src/CompressorPresets.h b/src/CompressorPresets.h new file mode 100644 index 0000000..2a12db1 --- /dev/null +++ b/src/CompressorPresets.h @@ -0,0 +1,81 @@ +#pragma once + +#include <array> + +struct CompressorPreset { + float ratio; + float thresholdDB; + float attackMS; + float releaseMS; + float makeUpGainDB; + CompressorPreset(float r, float t, float a, float rel, float m) + : ratio(r) + , thresholdDB(t) + , attackMS(a) + , releaseMS(rel) + , makeUpGainDB(m) + {} + ~CompressorPreset() = default; +}; + +namespace CompressorPresets +{ + // name ratio thresh attack rel mugain + const CompressorPreset voice { 2.0f, -24.0f, 15.0f, 40.0f, 2.0f }; + const CompressorPreset horns { 3.0f, -10.0f, 100.0f, 250.0f, 2.0f }; + const CompressorPreset snare { 5.0f, -4.0f, 5.0f, 150.0f, 3.0f }; + const uint numPresets { 3 }; + const std::array<CompressorPreset,numPresets> standardPresets { voice, horns, snare }; + enum CompressorPresetNames { CPN_VOICE, CPN_BRASS, CPN_SNARE, CPN_NUMPRESETS }; +} + +#if 0 // not yet using this +// Dynamic extension of CompressorPresets: +struct CompressorPresetList { + std::vector<CompressorPreset*> presets; + CompressorPresetList() { // define some standard presets + presets.push_back( new CompressorPreset(CompressorPresets::voice) ); + presets.push_back( new CompressorPreset(CompressorPresets::horns) ); + presets.push_back( new CompressorPreset(CompressorPresets::snare) ); + } + ~CompressorPresetList() = default; +}; +#endif + +/* Settings from http://www.anythingpeaceful.org/sonar/settings/comp.html + + Name Thresh(dB) Att(ms) Rel(ms) Ratio:1 Gain(dB) Comments + Vocal 1 -20 31 342 2.5 2 Compressor for Solo Vocal + Vocal 2 -8 26 331 2.5 1.5 Variation of Solo 1 + Full Comp 1 -8 60 2500 2.5 0 For Overall Volume Level + Full Comp 2 -18 94 447 3.5 2.5 Variation of Total Comp 1: Harder ratio + Full Comp 3 -16 11 180 6 6 Nearly a limiter effect + Kick Comp -24 9 58 3 5.5 Compressor for Acoustic Bass Drum + Snare Comp -17 8 12 2.5 3.5 Compressor for Acoustic Snare Drum + Guitar -10 5 238 2.5 1.5 Compressor for Acoustic Guitar + Brass Sec -18 18 226 1.7 4 Brass Sounds for Strong Attacks + Bass 1 -12 15 470 2 4.5 Finger Picked Bass Guitar + Bass 2 -12 6 133 1.7 4 Slap Electric Bass + E Guitar -8 7 261 3.5 2.5 Electric Guitar Compressor + Piano 1 -9 17 238 2.5 1 Brightens Piano + Piano 2 -18 7 174 3.5 6 Variation of Piano 1 + Kick -14 2 35 2 3.5 For sampled Bass Drum + Snare -18 8 354 4 8 For sampled Snare Drum + Strings 1 -11 33 749 2 1.5 For String instruments + Strings 2 -12 93 2500 1.5 1.5 For Violas and Cellos + Strings 3 -17 76 186 1.5 2.5 Cellos or DoubleBass + Syn Bass -10 9 250 3.5 3 Adjust level of Synth Bass + Syn Pad -13 58 238 2 2 Prevents diffusion of sound in synth pad + Limiting -1 0.1 325 20 0 Slow release limiter + Chorusing -9 39 225 1.7 2.5 For vocal Chorusing + + From https://www.dummies.com/art-center/music/recording-music/dynamic-music-compression-settings-for-horns-piano-and-percussion/ + + Horns –8 100 300 2.5-3 2 Brasses not normally compressed [jos gain estimate based on above table] + Piano -10 100-105 115 1.5-2 2 Normally not compressed ["] + Kick -6 40-50 200-300 4-6 3 Looks more like a limiter to me [jos] ["] + Snare -4 5-10 125-175 4-6 3 Crucial for a tight, punchy sound + Bongos -6 10-25 100-300 3-6 3 "Hand Drums" - protect against excess "slap" + Perc. -10 10-20 50 3-6 3 Transient overdrive protection in mix + +*/ diff --git a/src/DataProtocol.cpp b/src/DataProtocol.cpp index a23120a..aac86eb 100644 --- a/src/DataProtocol.cpp +++ b/src/DataProtocol.cpp @@ -52,7 +52,7 @@ using std::cout; using std::endl; DataProtocol::DataProtocol(JackTrip* jacktrip, const runModeT runmode, int /*bind_port*/, int /*peer_port*/) : - mStopped(false), mHasPacketsToReceive(false), mRunMode(runmode), mJackTrip(jacktrip) + mStopped(false), mHasPacketsToReceive(false), mRunMode(runmode), mJackTrip(jacktrip), mUseRtPriority(false) {} diff --git a/src/DataProtocol.h b/src/DataProtocol.h index 413263d..6be9a5a 100644 --- a/src/DataProtocol.h +++ b/src/DataProtocol.h @@ -180,11 +180,14 @@ public: }; virtual bool getStats(PktStat*) {return false;} + virtual void setIssueSimulation(double /*loss*/, double /*jitter*/, double /*max_delay*/) {} + void setUseRtPriority(bool use) {mUseRtPriority = use;} + signals: void signalError(const char* error_message); void signalReceivedConnectionFromPeer(); - + void signalCeaseTransmission(const QString &reason = ""); protected: @@ -222,6 +225,7 @@ private: protected: //PacketHeader* mHeader; ///< Packet Header JackTrip* mJackTrip; ///< JackTrip mediator class + bool mUseRtPriority; }; diff --git a/src/Effects.h b/src/Effects.h new file mode 100644 index 0000000..086d40e --- /dev/null +++ b/src/Effects.h @@ -0,0 +1,571 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2020 Julius Smith. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file Effects.h + * \author Julius Smith + * \date Aug 2020 + */ + +#pragma once + +#include "ProcessPlugin.h" +#include "Limiter.h" +#include "Compressor.h" +#include "CompressorPresets.h" +#include "Reverb.h" +#include <assert.h> +#include <vector> + +class Effects +{ + int mNumIncomingChans; + int mNumOutgoingChans; + int gVerboseFlag = 0; +public: + enum LIMITER_MODE { + LIMITER_NONE, + LIMITER_INCOMING, // from network + LIMITER_OUTGOING, // to network + LIMITER_BOTH + }; +private: + LIMITER_MODE mLimit; ///< audio limiter controls + unsigned int mNumClientsAssumed; ///< assumed number of clients (audio sources) + double limiterWarningAmplitude; + + enum InOrOut { IO_NEITHER, IO_IN, IO_OUT } io; + bool inCompressor = false; + bool outCompressor = false; + bool inZitarev = false; + bool outZitarev = false; + bool inFreeverb = false; + bool outFreeverb = false; + bool incomingEffectsAllocated = false; + bool outgoingEffectsAllocated = false; + Compressor* inCompressorP = nullptr; + Compressor* outCompressorP = nullptr; + CompressorPreset inCompressorPreset = CompressorPresets::voice; // ./CompressorPresets.h + CompressorPreset outCompressorPreset = CompressorPresets::voice; + Reverb* inZitarevP = nullptr; + Reverb* outZitarevP = nullptr; + Reverb* inFreeverbP = nullptr; + Reverb* outFreeverbP = nullptr; + int parenLevel = 0; + char lastEffect = '\0'; + float zitarevInLevel = 1.0f; // "Level" = wetness from 0 to 1 + float freeverbInLevel = 1.0f; + float zitarevOutLevel = 1.0f; + float freeverbOutLevel = 1.0f; + float mReverbLevel; // for backward compatibility: 0-1 Freeverb, 1-2 Zitarev + Limiter* inLimiterP = nullptr; + Limiter* outLimiterP = nullptr; + +public: + + Effects(bool outGoingLimiterOn=true) : + mNumIncomingChans(2), + mNumOutgoingChans(2), + mLimit(outGoingLimiterOn ? LIMITER_OUTGOING : LIMITER_NONE), + mNumClientsAssumed(2), + limiterWarningAmplitude(0.0) + {} + + ~Effects() { + /* + Plugin ownership presently passes to JackTrip, + and deletion occurs in AudioInterface.cpp. See + delete mProcessPluginsFromNetwork[i]; + delete mProcessPluginsToNetwork[i]; + there. If/when we ever do it here: + if (inCompressor) { delete inCompressorP; } + if (outCompressor) { delete outCompressorP; } + if (inZitarev) { delete inZitarevP; } + if (outZitarev) { delete outZitarevP; } + if (inFreeverb) { delete inFreeverbP; } + if (outFreeverb) { delete outFreeverbP; } + but if everyone can compile C++11, + let's switch to using std::unique_ptr. + */ + } + + unsigned int getNumClientsAssumed() { return mNumClientsAssumed; } + + LIMITER_MODE getLimit() { return mLimit; } + void setNoLimiters() { mLimit = LIMITER_NONE; } + + ProcessPlugin* getInCompressor() { return inCompressorP; } + ProcessPlugin* getOutCompressor() { return outCompressorP; } + ProcessPlugin* getInZitarev() { return inZitarevP; } + ProcessPlugin* getOutZitarev() { return outZitarevP; } + ProcessPlugin* getInFreeverb() { return inFreeverbP; } + ProcessPlugin* getOutFreeverb() { return outFreeverbP; } + ProcessPlugin* getInLimiter() { return inLimiterP; } + ProcessPlugin* getOutLimiter() { return outLimiterP; } + + bool getHaveEffect() { + return + inCompressor || outCompressor || + inZitarev || outZitarev || + inFreeverb || outFreeverb ; + } + + bool getHaveLimiter() { + return mLimit != LIMITER_NONE; + } + + void setVerboseFlag(int v) { + gVerboseFlag = v; + } + + int getNumIncomingChans() { + return mNumIncomingChans; + } + + int getOutgoingNumChans() { + return mNumOutgoingChans; + } + + // call these next two after it is decided what effects we will be using for the duration: + + std::vector<ProcessPlugin*> allocateIncomingEffects(int nIncomingChans) { + mNumIncomingChans = nIncomingChans; + if (incomingEffectsAllocated) { + std::cerr << "*** Effects.h: attempt to allocate incoming effects more than once\n"; + std::exit(1); + } + std::vector<ProcessPlugin*> incomingEffects; + if (inCompressor) { + assert(inCompressorP == nullptr); + inCompressorP = new Compressor(mNumIncomingChans, gVerboseFlag, inCompressorPreset); + if (gVerboseFlag) { std::cout << "Set up INCOMING COMPRESSOR\n"; } + incomingEffects.push_back(inCompressorP); + } + if (inZitarev) { + assert(inZitarevP == nullptr); + inZitarevP = new Reverb(mNumIncomingChans,mNumIncomingChans, 1.0 + zitarevInLevel); + if (gVerboseFlag) { std::cout << "Set up INCOMING REVERB (Zitarev)\n"; } + incomingEffects.push_back(inZitarevP); + } + if (inFreeverb) { + assert(inFreeverbP == nullptr); + inFreeverbP = new Reverb(mNumIncomingChans, mNumIncomingChans, freeverbInLevel); + if (gVerboseFlag) { std::cout << "Set up INCOMING REVERB (Freeverb)\n"; } + incomingEffects.push_back(inFreeverbP); + } + // LIMITER MUST GO LAST: + if ( mLimit == LIMITER_INCOMING || mLimit == LIMITER_BOTH) { + if (gVerboseFlag) { + std::cout << "Set up INCOMING LIMITER for " << mNumIncomingChans << " input channels\n"; + } + assert(inLimiterP == nullptr); + inLimiterP = new Limiter(mNumIncomingChans, 1, gVerboseFlag); // mNumClientsAssumed not needed this direction + // Never needed in normal practice for incoming limiter: inLimiterP->setWarningAmplitude(limiterWarningAmplitude); + incomingEffects.push_back(inLimiterP); + } + incomingEffectsAllocated = true; + return incomingEffects; + } + + std::vector<ProcessPlugin*> allocateOutgoingEffects(int nOutgoingChans) { + mNumOutgoingChans = nOutgoingChans; + if (outgoingEffectsAllocated) { + std::cerr << "*** Effects.h: attempt to allocate outgoing effects more than once\n"; + std::exit(1); + } + std::vector<ProcessPlugin*> outgoingEffects; + if (outCompressor) { + assert(outCompressorP == nullptr); + outCompressorP = new Compressor(mNumOutgoingChans, gVerboseFlag, outCompressorPreset); + if (gVerboseFlag) { std::cout << "Set up OUTGOING COMPRESSOR\n"; } + outgoingEffects.push_back(outCompressorP); + } + if (outZitarev) { + assert(outZitarevP == nullptr); + outZitarevP = new Reverb(mNumOutgoingChans, mNumOutgoingChans, 1.0 + zitarevOutLevel); + if (gVerboseFlag) { std::cout << "Set up OUTGOING REVERB (Zitarev)\n"; } + outgoingEffects.push_back(outZitarevP); + } + if (outFreeverb) { + assert(outFreeverbP == nullptr); + outFreeverbP = new Reverb(mNumOutgoingChans, mNumOutgoingChans, freeverbOutLevel); + if (gVerboseFlag) { std::cout << "Set up OUTGOING REVERB (Freeverb)\n"; } + outgoingEffects.push_back(outFreeverbP); + } + // LIMITER MUST GO LAST: + if ( mLimit != LIMITER_NONE) { + if ( mLimit == LIMITER_OUTGOING || mLimit == LIMITER_BOTH) { + if (gVerboseFlag) { + std::cout << "Set up OUTGOING LIMITER for " + << mNumOutgoingChans << " output channels and " + << mNumClientsAssumed << " assumed client(s) ...\n"; + } + assert(outLimiterP == nullptr); + outLimiterP = new Limiter(mNumOutgoingChans,mNumClientsAssumed); + outLimiterP->setWarningAmplitude(limiterWarningAmplitude); + // do not have mSampleRate yet, so cannot call limiter->init(mSampleRate) here + outgoingEffects.push_back(outLimiterP); + } + } + outgoingEffectsAllocated = true; + return outgoingEffects; + } + + void printHelp(char* command, char helpCase) { + std::cout << "HELP for `" << command << "' (end-of-line comments start with `//')\n"; + std::cout << "\n"; + std::cout << "Examples:\n"; + std::cout << "\n"; + if (helpCase == 0 || helpCase == 'f') { // + std::cout << command << " 0.3 // add a default outgoing compressor (for voice) and incoming reverb (freeverb) with wetness 0.3 (wetness from 0 to 1)\n"; + std::cout << command << " 1.3 // add a default outgoing compressor (for voice) and incoming reverb (zitarev) with wetness 0.3 = 1.3-1 (i.e., 1+ to 2 is for zitarev)\n"; + std::cout << "\n"; + std::cout << command << " \"o:c i:f(0.3)\" // outgoing-compressor and incoming-freeverb example above using more general string argument\n"; + std::cout << command << " \"o:c i:z(0.3)\" // outgoing-compressor and incoming-zitarev example above using more general string argument\n"; + std::cout << command << " \"o:c(1)\" // outgoing compressor, using preset 1 (designed for voice - see below for details)\n"; + std::cout << command << " \"o:c(2)\" // outgoing compressor, using preset 2 (for horns)\n"; + std::cout << command << " \"o:c(3)\" // outgoing compressor, using preset 3 (for snare)\n"; + std::cout << command << " \"o:c(c:compressionRatio t:thresholdDB a:attackTimeMS r:releaseTimeMS g:makeUpGainDB)\" // general compression parameter specification (all floats)\n"; + std::cout << command << " \"o:c(c:2 t:-24 a:15 r:40 g:2)\" // outgoing compressor, preset 1 details\n"; + std::cout << command << " \"o:c(c:3 t:-10 a:100 r:250 g:2)\" // outgoing compressor, preset 2 details\n"; + std::cout << command << " \"o:c(c:5 t:-4 a:5 r:150 g:3)\" // outgoing compressor, preset 3 details\n"; + std::cout << " For these and more suggested compression settings, see http://www.anythingpeaceful.org/sonar/settings/comp.html\n"; + std::cout << "\n"; + } + if (helpCase == 0 || helpCase == 'O') { // limiter (-O option most likely) + std::cout << command << " i // add limiter to INCOMING audio from network (only helpful for floats, i.e., -b32 used by server)\n"; + std::cout << command << " o // add limiter to OUTGOING audio to network (prevents your sound from harshly clipping going out)\n"; + std::cout << command << " ow // also warn and advise on levels when outgoing limiter compresses audio near clipping\n"; + std::cout << command << " io // add limiter to both INCOMING and OUTGOING audio\n"; + std::cout << command << " iow // limiters both ways and compression warnings on outgoing direction only\n"; + std::cout << "\n"; + } + if (helpCase == 0 || helpCase == 'a') { // assumedNumClients (-a option) + std::cout << command << " 1 // assume 1 client - fine for loopback test, or if only one client plays at a time, or server uses -b32 and -Oi is used\n"; + std::cout << command << " 2 // assume 2 clients possibly playing at the same time\n"; + std::cout << command << " N // any integer N>0 can be used - the outgoing limiter will divide final amplitude by 1/sqrt(N) to reduce overages in server\n"; + std::cout << "\n"; + } + } + + // ----------- Compressor stuff -------------- + + int setCompressorPresetIndexFrom1(unsigned long presetIndexFrom1, InOrOut io) { + int returnCode = 0; + if (presetIndexFrom1 <= 0 || presetIndexFrom1 > CompressorPresets::numPresets) { + std::cerr << "*** Effects.h: setCompressorPresetFrom1: Index " << presetIndexFrom1 << " out of range\n"; + returnCode = 1; + } else { + CompressorPreset stdPreset = CompressorPresets::standardPresets[presetIndexFrom1-1]; + if (io == IO_IN) { + inCompressorPreset = stdPreset; + } else if (io == IO_OUT) { + outCompressorPreset = stdPreset; + } else if (io != IO_NEITHER) { + std::cerr << "*** Effects.h: setCompressorPresetFrom1: Invalid InOrOut value " << io << "\n"; + returnCode = 1; + } + } + return returnCode; + } + + int parseCompresserArgs(char* args, InOrOut inOrOut) { + // args can be integerPresetNumberFrom1 or (all optional, any order): + // c:compressionRatio, a:attackTimeMS, r:releaseTimeMS, g:makeUpGain + int returnCode = 0; + if (not isalpha(args[0])) { + int presetIndexFrom1 = atoi(args); + setCompressorPresetIndexFrom1(presetIndexFrom1,inOrOut); + } else { + // args can be presetIndexFrom1, handled above, or (all optional, any order): + // c(c:compressionRatio, t:thresholdDB, a:attackTimeMS, r:releaseTimeMS, g:makeUpGainDB) + // See ./CompressorPresets.h for example settings. + if (gVerboseFlag) { + std::cout << "parseCompressorArgs = " << args << std::endl; + } + ulong argLen = strlen(args); + char lastParam = '\0'; + + CompressorPreset newPreset(CompressorPresets::voice); // Anything unset gets voice value (most gentle) + + int nSkip = 0; + for (ulong i=0; i<argLen; i++) { + if (nSkip > 0) { + nSkip--; + continue; + } + char ch = args[i]; + switch(ch) { + case ' ': break; + case '\t': break; + case 'c': case 't': case 'a': case 'r': case 'g': + lastParam = ch; + break; + case ':': break; + default: // must be a floating-point number at this point: + if (ch!='-' && isalpha(ch)) { + std::cerr << "*** Effects.h: parseCompressorArgs: " << ch << " not recognized in args = " << args << "\n"; + returnCode = 2; + } else { // must have a digit or '-' or '.' + assert(ch=='-'||ch=='.'||isdigit(ch)); + float paramValue = -1.0e10; + for (ulong j=i; j<argLen; j++) { // scan ahead for end of number + if (args[j] == ',' || args[j] == ' ' || j==argLen-1) { // comma or space required between parameters + char argsj = args[j]; + if (j<argLen-1) { // there's more + args[j] = '\0'; + } + paramValue = atof(&args[i]); + args[j] = argsj; + nSkip = j-i; + break; + } + } + if (paramValue == -1.0e10) { + std::cerr << "*** Effects.h: parseCompressorArgs: Could not find parameter for " + << lastParam << " in args = " << args << "\n"; + returnCode = 2; + } else { + switch (lastParam) { + case 'c': + newPreset.ratio = paramValue; + break; + case 't': + newPreset.thresholdDB = paramValue; + break; + case 'a': + newPreset.attackMS = paramValue; + break; + case 'r': + newPreset.releaseMS = paramValue; + break; + case 'g': + newPreset.makeUpGainDB = paramValue; + break; + default: // cannot happen: + std::cerr << "*** Effects.h: parseCompressorArgs: lastParam " << lastParam << " invalid\n"; + returnCode = 3; // "reality failure" + } // switch(lastParam) + } // have valid parameter from atof + } // have valid non-alpha char for parameter + } // switch(ch) + } // for (ulong i=0; i<argLen; i++) { + if (inOrOut == IO_IN) { + inCompressorPreset = newPreset; + } else if (inOrOut == IO_OUT) { + outCompressorPreset = newPreset; + } else if (inOrOut != IO_NEITHER) { + std::cerr << "*** Effects.h: parseCompressorArgs: invalid InOrOut value " << inOrOut << "\n"; + returnCode = 2; + } + } // long-form compressor args + return returnCode; + } // int parseCompresserArgs(char* args, InOrOut inOrOut) + + // ============== General argument processing for all effects ================= + + int parseEffectsOptArg(char* cmd, char* optarg) { + int returnCode = 0; // 0 means go, 1 means exit without error, higher => error exit + + char c = optarg[0]; + if (c == '-' || c==0) { + // happens when no -f argument specified + returnCode = 2; + } else if (not isalpha(c)) { // backward compatibility why not?, e.g., "-f 0.5" + // -f reverbLevelFloat + mReverbLevel = atof(optarg); + outCompressor = true; + inZitarev = mReverbLevel > 1.0; + inFreeverb = mReverbLevel <= 1.0; + if (inZitarev) { + zitarevInLevel = mReverbLevel - 1.0; // wetness from 0 to 1 + } + if (inFreeverb) { + freeverbInLevel = mReverbLevel; // wetness from 0 to 1 + } + } else { // long-form argument: + // -f "i:[c][f|z][(reverbLevel)]], o:[c][f|z][(rl)]" + // c can be c(integerPresetNumberFrom1) or (all optional, any order): + // c(c:compressionRatio, a:attackTimeMS, r:releaseTimeMS, g:makeUpGain) + if (gVerboseFlag) { + std::cout << cmd << " argument = " << optarg << std::endl; + } + ulong argLen = strlen(optarg); + + for (ulong i=0; i<argLen; i++) { + if (optarg[i]!=')' && parenLevel>0) { continue; } + switch(optarg[i]) { + case ' ': break; + case ',': break; + case ';': break; + case '\t': break; + case 'h': printHelp(cmd,'f'); returnCode = 1; break; + case 'i': io=IO_IN; break; + case 'o': io=IO_OUT; break; + case ':': break; + case 'c': if (io==IO_IN) { inCompressor = true; } else if (io==IO_OUT) { outCompressor = true; } + else { std::cerr << "-f arg `" << optarg << "' malformed\n"; exit(1); } + lastEffect = 'c'; + break; + case 'f': if (io==IO_IN) { inFreeverb = true; } else if (io==IO_OUT) { outFreeverb = true; } + else { std::cerr << "-f arg `" << optarg << "' malformed\n"; exit(1); } + lastEffect = 'f'; + break; + case 'z': if (io==IO_IN) { inZitarev = true; } else if (io==IO_OUT) { outZitarev = true; } + else { std::cerr << "-f arg `" << optarg << "' malformed\n"; exit(1); } + lastEffect = 'z'; + break; + case '(': parenLevel++; + for (ulong j=i+1; j<argLen; j++) { + if (optarg[j] == ')') { + optarg[j] = '\0'; + switch(lastEffect) { + case 'c': { + returnCode += parseCompresserArgs(&optarg[i+1],io); + break; } + case 'z': { + float farg = atof(&optarg[i+1]); + if (io==IO_IN) { + zitarevInLevel = farg; + } else if (io==IO_OUT) { + zitarevOutLevel = farg; + } // else ignore the argument + break; } + case 'f': { + float farg = atof(&optarg[i+1]); + if (io==IO_IN) { + freeverbInLevel = farg; + } else if (io==IO_OUT) { + freeverbOutLevel = farg; + } // else ignore the argument + break; } + default: { // ignore + break; } + } + optarg[j] = ')'; + break; + } + } + break; + case ')': parenLevel--; + break; + default: + break; // ignore + } // switch(optarg[i]) + } + } + return returnCode; + } + + int parseLimiterOptArg(char* cmd, char* optarg) { + int returnCode = 0; + lastEffect = 'O'; // OverflowLimiter + char ch = tolower(optarg[0]); + if (ch == '-' || ch == 0) { + std::cerr << cmd << " argument i, o, or io is REQUIRED\n"; + returnCode = 2; + } else if (ch == 'h') { + printHelp(cmd,'O'); + returnCode = 1; + } else { + bool haveIncoming = false; + bool haveOutgoing = false; + bool haveWarnings = false; + for (int i=0; i<strlen(optarg); i++) { + ch = tolower(optarg[i]); + switch(ch) { + case ' ': break; + case '\t': break; + case 'i': + haveIncoming = true; + break; + case 'o': + haveOutgoing = true; + break; + case 'w': + haveWarnings = true; + break; + case 'n': + haveIncoming = false; + haveOutgoing = false; + break; + default: + std::cerr << "*** Effects.h: parseLimiterOptArg: Unrecognized option " << ch << "\n"; + returnCode = 2; + } // switch(ch) + } // process optarg char ch + mLimit = (haveIncoming && haveOutgoing ? LIMITER_BOTH + : (haveIncoming ? LIMITER_INCOMING + : (haveOutgoing ? LIMITER_OUTGOING : LIMITER_NONE))); + if (haveWarnings) { + limiterWarningAmplitude = 0.5; // KEEP IN SYNC WITH LIMITER THRESHOLD/CEILING 'softClipLevel' in ../faust-src/limiterdsp.dsp + // the warning amplitude and limiter compression threshold can of course be brought as a parameters, e.g. w(0.5) + } + if (gVerboseFlag) { + if(haveIncoming) { + std::cout << "Set up INCOMING Overflow Limiter\n"; + } + if(haveOutgoing) { + std::cout << "Set up OUTGOING Overflow Limiter\n"; + } + if(haveWarnings) { + std::cout << "Enable DISTORTION WARNINGS in Overflow Limiters\n"; + } + if(not haveIncoming and not haveOutgoing) { + std::cout << "Set up NO Overflow Limiters\n"; + } + } // gVerboseFlag + } // optarg cases + return returnCode; + } // parseLimiterOptArg() + + int parseAssumedNumClientsOptArg(char* cmd, char* optarg) { + int returnCode = 0; + lastEffect = 'a'; // assumedNumClients + char ch = optarg[0]; + if (ch == 'h') { + printHelp(cmd,'a'); + returnCode = 1; + } else if (ch == '-' || isalpha(ch) || ch == 0) { + std::cerr << cmd << " argument help or integer > 0 is REQUIRED\n"; + returnCode = 2; + } else { + mNumClientsAssumed = atoi(optarg); + if(mNumClientsAssumed < 1) { + std::cerr << "-p ERROR: Must have at least one assumed sound source: " + << atoi(optarg) << " is not supported." << std::endl; + returnCode = 2; + } + } + return returnCode; + } + +}; diff --git a/src/JMess.cpp b/src/JMess.cpp index 8cdab74..f348617 100644 --- a/src/JMess.cpp +++ b/src/JMess.cpp @@ -78,7 +78,7 @@ JMess::~JMess() * */ //------------------------------------------------------------------------------- -void JMess::writeOutput(QString xmlOutFile) +void JMess::writeOutput(__attribute__((unused)) QString xmlOutFile) { // QDomDocument jmess_xml; QDomElement root; // QDomElement connection; QDomElement output; @@ -171,13 +171,12 @@ void JMess::setConnectedPorts() void JMess::connectSpawnedPorts(int nChans, int hubPatch) // called from UdpHubListener::connectMesh { - QMutexLocker locker(&sJMessMutex); - + QString IPS[gMAX_WAIRS]; int ctr = 0; - const char **ports, **connections; //vector of ports and connections + const char **ports; //, **connections; //vector of ports and connections QVector<QString> OutputInput(2); //helper variable //Get active output ports. @@ -202,7 +201,7 @@ void JMess::connectSpawnedPorts(int nChans, int hubPatch) // qDebug() << ports[out_i] << systemPort << s; } } -// for (int i = 0; i<ctr; i++) qDebug() << IPS[i]; + //for (int i = 0; i<ctr; i++) qDebug() << IPS[i]; disconnectAll(); int k = 0; @@ -217,8 +216,8 @@ void JMess::connectSpawnedPorts(int nChans, int hubPatch) if ((hubPatch == JackTrip::CLIENTECHO)||(hubPatch == JackTrip::FULLMIX)) k = i; else if (hubPatch == JackTrip::CLIENTFOFI) k = (j+(i+1))%ctr; for (int l = 1; l<=nChans; l++) { // chans are 1-based -// qDebug() << "connect " << IPS[i]+":receive_"+QString::number(l) -// <<"with " << IPS[k]+":send_"+QString::number(l); + //qDebug() << "connect " << IPS[i]+":receive_"+QString::number(l) + //<<"with " << IPS[k]+":send_"+QString::number(l); QString left = IPS[i] + ":receive_" + QString::number(l); @@ -244,8 +243,8 @@ void JMess::connectSpawnedPorts(int nChans, int hubPatch) for (int j = 0; j<jLimit; j++) { k = (j+(i+1))%ctr; for (int l = 1; l<=nChans; l++) { // chans are 1-based -// qDebug() << "connect " << IPS[i]+":receive_"+QString::number(l) -// <<"with " << IPS[k]+":send_"+QString::number(l); + //qDebug() << "connect " << IPS[i]+":receive_"+QString::number(l) + //<<"with " << IPS[k]+":send_"+QString::number(l); QString left = IPS[i] + ":receive_" + QString::number(l); @@ -371,7 +370,7 @@ void JMess::disconnectAll() it != mConnectedPorts.end(); ++it) { OutputInput = *it; - if (jack_disconnect(mClient, OutputInput[0].toLatin1(), OutputInput[1].toLatin1())) { + if (jack_disconnect(mClient, OutputInput[0].toUtf8(), OutputInput[1].toUtf8())) { cerr << "WARNING: port: " << qPrintable(OutputInput[0]) << "and port: " << qPrintable(OutputInput[1]) << " could not be disconnected.\n"; @@ -388,7 +387,7 @@ void JMess::disconnectAll() * read the file. */ //------------------------------------------------------------------------------- -int JMess::parseXML(QString xmlInFile) +int JMess::parseXML(__attribute__((unused)) QString xmlInFile) { // mPortsToConnect.clear(); // QString errorStr; @@ -455,7 +454,7 @@ int JMess::parseXML(QString xmlInFile) * */ //------------------------------------------------------------------------------- -void JMess::connectPorts(QString xmlInFile) +void JMess::connectPorts(__attribute__((unused)) QString xmlInFile) { QVector<QString> OutputInput(2); diff --git a/src/JackAudioInterface.cpp b/src/JackAudioInterface.cpp index 70c7632..06ff4c8 100644 --- a/src/JackAudioInterface.cpp +++ b/src/JackAudioInterface.cpp @@ -66,7 +66,7 @@ JackAudioInterface::JackAudioInterface(JackTrip* jacktrip, int NumNetRevChans, #endif // endwhere AudioInterface::audioBitResolutionT AudioBitResolution, - const char* ClientName) : + QString ClientName) : AudioInterface(jacktrip, NumInChans, NumOutChans, #ifdef WAIR // wair @@ -81,6 +81,7 @@ JackAudioInterface::JackAudioInterface(JackTrip* jacktrip, mBitResolutionMode(AudioBitResolution), mClient(NULL), mClientName(ClientName), + mBroadcast(false), mJackTrip(jacktrip) {} @@ -171,6 +172,7 @@ void JackAudioInterface::setupClient() // Initialize Buffer array to read and write audio mInBuffer.resize(mNumInChans); mOutBuffer.resize(mNumOutChans); + mBroadcastBuffer.resize(mNumOutChans); } @@ -198,6 +200,18 @@ void JackAudioInterface::createChannels() JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); } + //Create Broadcast Ports + if (mBroadcast) { + mBroadcastPorts.resize(mNumOutChans); + for (int i = 0; i < mNumInChans; i++) + { + QString outName; + QTextStream (&outName) << "broadcast_" << i+1; + mBroadcastPorts[i] = jack_port_register (mClient, outName.toLatin1(), + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + } + } } @@ -273,7 +287,9 @@ int JackAudioInterface::stopProcess() const void JackAudioInterface::jackShutdown (void*) { //std::cout << "The Jack Server was shut down!" << std::endl; - throw std::runtime_error("The Jack Server was shut down!"); + JackTrip::sJackStopped = true; + std::cout << "The Jack Server was shut down!" << std::endl; + //throw std::runtime_error("The Jack Server was shut down!"); //std::cout << "Exiting program..." << std::endl; //std::exit(1); } @@ -283,10 +299,15 @@ void JackAudioInterface::jackShutdown (void*) //******************************************************************************* int JackAudioInterface::processCallback(jack_nframes_t nframes) { + if(mProcessingAudio) { + std::cerr << "*** JackAudioInterface.cpp: DROPPED A BUFFER because AudioInterface::callback() not finished\n"; + return 1; + } + // Get input and output buffers from JACK //------------------------------------------------------------------- for (int i = 0; i < mNumInChans; i++) { - // Input Ports are READ ONLY + // Input Ports are READ ONLY and change as needed (no locks) - make a copy for debugging mInBuffer[i] = (sample_t*) jack_port_get_buffer(mInPorts[i], nframes); } for (int i = 0; i < mNumOutChans; i++) { @@ -302,6 +323,14 @@ int JackAudioInterface::processCallback(jack_nframes_t nframes) //------------------------------------------------------------------- AudioInterface::callback(mInBuffer, mOutBuffer, nframes); + + if (mBroadcast) { + for (int i = 0; i < mNumOutChans; i++) { + // Broadcast Ports are WRITABLE + mBroadcastBuffer[i] = (sample_t*) jack_port_get_buffer(mBroadcastPorts[i], nframes); + } + AudioInterface::broadcastCallback(mBroadcastBuffer, nframes); + } return 0; } @@ -377,7 +406,7 @@ void JackAudioInterface::connectDefaultPorts() -// OLD CODE +// OLD CODE (some moved to parent class AudioInterface.cpp) // ============================================================================== //******************************************************************************* diff --git a/src/JackAudioInterface.h b/src/JackAudioInterface.h index 7886d82..5a1b4b0 100644 --- a/src/JackAudioInterface.h +++ b/src/JackAudioInterface.h @@ -78,7 +78,7 @@ public: int NumNetRevChans, #endif // endwhere AudioInterface::audioBitResolutionT AudioBitResolution = AudioInterface::BIT16, - const char* ClientName = "JackTrip"); + QString ClientName = "JackTrip"); /// \brief The class destructor virtual ~JackAudioInterface(); @@ -104,6 +104,7 @@ public: { std::cout << "WARNING: Setting the Sample Rate in Jack mode has no effect." << std::endl; } virtual void setBufferSizeInSamples(uint32_t /*buf_size*/) { std::cout << "WARNING: Setting the Sample Rate in Jack mode has no effect." << std::endl; } + virtual void enableBroadcastOutput() {mBroadcast = true;} //------------------------------------------------------------------ //--------------GETTERS--------------------------------------------- @@ -113,7 +114,7 @@ public: virtual uint32_t getBufferSizeInSamples() const; /// \brief Get the Jack Server Buffer Size, in bytes virtual uint32_t getBufferSizeInBytes() const - { return (getBufferSizeInSamples() * getAudioBitResolution()/8); } + { return (getBufferSizeInSamples() * getAudioBitResolution() / 8); } /// \brief Get size of each audio per channel, in bytes virtual size_t getSizeInBytesPerChannel() const; //------------------------------------------------------------------ @@ -177,8 +178,11 @@ private: QString mClientName; ///< Jack Client Name QVarLengthArray<jack_port_t*> mInPorts; ///< Vector of Input Ports (Channels) QVarLengthArray<jack_port_t*> mOutPorts; ///< Vector of Output Ports (Channels) + QVarLengthArray<jack_port_t*> mBroadcastPorts; ///< Vector of Output Ports (Channels) QVarLengthArray<sample_t*> mInBuffer; ///< Vector of Input buffers/channel read from JACK QVarLengthArray<sample_t*> mOutBuffer; ///< Vector of Output buffer/channel to write to JACK + QVarLengthArray<sample_t*> mBroadcastBuffer; ///< Vector of Output buffer/channel to write to JACK + bool mBroadcast; size_t mSizeInBytesPerChannel; ///< Size in bytes per audio channel QVector<ProcessPlugin*> mProcessPlugins; ///< Vector of ProcesPlugin<EM>s</EM> JackTrip* mJackTrip; ///< JackTrip mediator class diff --git a/src/JackTrip.cpp b/src/JackTrip.cpp index 4ba94af..f672984 100644 --- a/src/JackTrip.cpp +++ b/src/JackTrip.cpp @@ -38,6 +38,7 @@ #include "JackTrip.h" #include "UdpDataProtocol.h" #include "RingBufferWavetable.h" +#include "JitterBuffer.h" #include "jacktrip_globals.h" #include "JackAudioInterface.h" #ifdef __RT_AUDIO__ @@ -51,7 +52,6 @@ #include <QHostAddress> #include <QHostInfo> #include <QThread> -#include <QTcpSocket> #include <QTimer> #include <QDateTime> @@ -59,12 +59,15 @@ using std::cout; using std::endl; //the following function has to remain outside the Jacktrip class definition //its purpose is to close the app when control c is hit by the user in rtaudio/asio4all mode -#if defined __WIN_32__ +/*if defined __WIN_32__ void sigint_handler(int sig) { exit(0); } -#endif +#endif*/ + +bool JackTrip::sSigInt = false; +bool JackTrip::sJackStopped = false; //******************************************************************************* JackTrip::JackTrip(jacktripModeT JacktripMode, @@ -89,15 +92,19 @@ JackTrip::JackTrip(jacktripModeT JacktripMode, mNumNetRevChans(NumNetRevChans), #endif // endwhere mBufferQueueLength(BufferQueueLength), + mBufferStrategy(1), + mBroadcastQueueLength(0), mSampleRate(gDefaultSampleRate), mDeviceID(gDefaultDeviceID), mAudioBufferSize(gDefaultBufferSizeInSamples), mAudioBitResolution(AudioBitResolution), + mLoopBack(false), mDataProtocolSender(NULL), mDataProtocolReceiver(NULL), mAudioInterface(NULL), mPacketHeader(NULL), mUnderRunMode(UnderRunMode), + mStopOnTimeout(false), mSendRingBuffer(NULL), mReceiveRingBuffer(NULL), mReceiverBindPort(receiver_bind_port), @@ -108,20 +115,34 @@ JackTrip::JackTrip(jacktripModeT JacktripMode, mRedundancy(redundancy), mJackClientName(gJackDefaultClientName), mConnectionMode(JackTrip::NORMAL), + mTimeoutTimer(this), + mSleepTime(100), + mElapsedTime(0), + mEndTime(0), + mTcpClient(this), + mUdpSockTemp(this), mReceivedConnection(false), mTcpConnectionError(false), mStopped(false), + mHasShutdown(false), mConnectDefaultAudioPorts(true), - mIOStatLogStream(std::cout.rdbuf()) + mIOStatTimeout(0), + mIOStatLogStream(std::cout.rdbuf()), + mSimulatedLossRate(0.0), + mSimulatedJitterRate(0.0), + mSimulatedDelayRel(0.0), + mUseRtUdpPriority(false), + mAudioTesterP(nullptr) { createHeader(mPacketHeaderType); + sJackStopped = false; } //******************************************************************************* JackTrip::~JackTrip() { - wait(); + //wait(); delete mDataProtocolSender; delete mDataProtocolReceiver; delete mAudioInterface; @@ -134,14 +155,14 @@ JackTrip::~JackTrip() //******************************************************************************* void JackTrip::setupAudio( #ifdef WAIRTOHUB // WAIR - int ID + __attribute__((unused)) int ID #endif // endwhere ) { // Check if mAudioInterface has already been created or not if (mAudioInterface != NULL) { // if it has been created, disconnet it from JACK and delete it cout << "WARINING: JackAudio interface was setup already:" << endl; - cout << "It will be errased and setup again." << endl; + cout << "It will be erased and setup again." << endl; cout << gPrintSeparator << endl; closeAudio(); } @@ -157,23 +178,19 @@ void JackTrip::setupAudio( mAudioBitResolution); #ifdef WAIRTOHUB // WAIR -// qDebug() << "mPeerAddress" << mPeerAddress << mPeerAddress.contains(gDOMAIN_TRIPLE); QString VARIABLE_AUDIO_NAME = WAIR_AUDIO_NAME; // legacy for WAIR - QByteArray tmp = QString(mPeerAddress).replace(":", ".").toLatin1(); //Set our Jack client name if we're a hub server or a custom name hasn't been set if (!mPeerAddress.isEmpty() && (mJackClientName.constData() == gJackDefaultClientName.constData())) { mJackClientName = QString(mPeerAddress).replace(":", "_"); } -// if ( mPeerAddress.toStdString() != "" && -// (mJackClientName == gJackDefaultClientName || mJackTripMode == SERVERPINGSERVER)) { -// mJackClientName = QString(mPeerAddress).replace(":", ".").toLatin1().constData(); -// } + //std::cout << "WAIR ID " << ID << " jacktrip client name set to=" << + // mJackClientName.toStdString() << std::endl; -// std::cout << "WAIR ID " << ID << " jacktrip client name set to=" << -// mJackClientName << std::endl; #endif // endwhere - mAudioInterface->setClientName(mJackClientName); + if (0 < mBroadcastQueueLength) { + mAudioInterface->enableBroadcastOutput(); + } if (gVerboseFlag) std::cout << " JackTrip:setupAudio before mAudioInterface->setup" << std::endl; mAudioInterface->setup(); @@ -205,17 +222,27 @@ void JackTrip::setupAudio( #endif } + mAudioInterface->setLoopBack(mLoopBack); + if (mAudioTesterP) { // if we're a hub server, this will be a nullptr - MAJOR REFACTOR NEEDED, in my opinion + mAudioTesterP->setSampleRate(mSampleRate); + } + mAudioInterface->setAudioTesterP(mAudioTesterP); + std::cout << "The Sampling Rate is: " << mSampleRate << std::endl; std::cout << gPrintSeparator << std::endl; int AudioBufferSizeInBytes = mAudioBufferSize*sizeof(sample_t); std::cout << "The Audio Buffer Size is: " << mAudioBufferSize << " samples" << std::endl; std::cout << " or: " << AudioBufferSizeInBytes << " bytes" << std::endl; + if (0 < mBroadcastQueueLength) { + std::cout << gPrintSeparator << std::endl; + cout << "Broadcast Output is enabled, delay = " + << mBroadcastQueueLength * mAudioBufferSize * 1000 / mSampleRate << " ms" + << " (" << mBroadcastQueueLength * mAudioBufferSize << " samples)" << endl; + } std::cout << gPrintSeparator << std::endl; cout << "The Number of Channels is: " << mAudioInterface->getNumInputChannels() << endl; std::cout << gPrintSeparator << std::endl; - cout << "The RTAudio device ID is: " << mAudioInterface->getDeviceID() << endl; - std::cout << gPrintSeparator << std::endl; QThread::usleep(100); } @@ -235,11 +262,11 @@ void JackTrip::closeAudio() //******************************************************************************* void JackTrip::setupDataProtocol() { + double simulated_max_delay = mSimulatedDelayRel * getBufferSizeInSamples() / getSampleRate(); // Create DataProtocol Objects switch (mDataProtocol) { case UDP: std::cout << "Using UDP Protocol" << std::endl; - std::cout << gPrintSeparator << std::endl; QThread::usleep(100); mDataProtocolSender = new UdpDataProtocol(this, DataProtocol::SENDER, //mSenderPeerPort, mSenderBindPort, @@ -248,6 +275,15 @@ void JackTrip::setupDataProtocol() mDataProtocolReceiver = new UdpDataProtocol(this, DataProtocol::RECEIVER, mReceiverBindPort, mReceiverPeerPort, mRedundancy); + if (0.0 < mSimulatedLossRate || 0.0 < mSimulatedJitterRate || 0.0 < simulated_max_delay) { + mDataProtocolReceiver->setIssueSimulation(mSimulatedLossRate, mSimulatedJitterRate, simulated_max_delay); + } + mDataProtocolSender->setUseRtPriority(mUseRtUdpPriority); + mDataProtocolReceiver->setUseRtPriority(mUseRtUdpPriority); + if (mUseRtUdpPriority) { + cout << "Using RT thread priority for UDP data" << endl; + } + std::cout << gPrintSeparator << std::endl; break; case TCP: throw std::invalid_argument("TCP Protocol is not implemented"); @@ -277,6 +313,12 @@ void JackTrip::setupRingBuffers() /// \todo Make all this operations cleaner //int total_audio_packet_size = getTotalAudioPacketSizeInBytes(); int slot_size = getRingBuffersSlotSize(); + if (0 <= mBufferStrategy) { + mUnderRunMode = ZEROS; + } + else if (0 > mBufferQueueLength) { + throw std::invalid_argument("Auto queue is not supported by RingBuffer"); + } switch (mUnderRunMode) { case WAVETABLE: @@ -290,13 +332,23 @@ void JackTrip::setupRingBuffers() mReceiveRingBuffer = new RingBufferWavetable(mAudioInterface->getSizeInBytesPerChannel() * mNumChans, mBufferQueueLength); */ - break; case ZEROS: mSendRingBuffer = new RingBuffer(slot_size, gDefaultOutputQueueLength); - mReceiveRingBuffer = new RingBuffer(slot_size, - mBufferQueueLength); + if (0 > mBufferStrategy) { + mReceiveRingBuffer = new RingBuffer(slot_size, + mBufferQueueLength); + } + else { + cout << "Using JitterBuffer strategy " << mBufferStrategy << endl; + if (0 > mBufferQueueLength) { + cout << "Using AutoQueue 1/" << -mBufferQueueLength << endl; + } + mReceiveRingBuffer = new JitterBuffer(mAudioBufferSize, mBufferQueueLength, + mSampleRate, mBufferStrategy, + mBroadcastQueueLength, mNumChans, mAudioBitResolution); + } /* mSendRingBuffer = new RingBuffer(mAudioInterface->getSizeInBytesPerChannel() * mNumChans, gDefaultOutputQueueLength); @@ -312,17 +364,28 @@ void JackTrip::setupRingBuffers() //******************************************************************************* -void JackTrip::setPeerAddress(const char* PeerHostOrIP) +void JackTrip::setPeerAddress(QString PeerHostOrIP) { mPeerAddress = PeerHostOrIP; } //******************************************************************************* -void JackTrip::appendProcessPlugin(ProcessPlugin* plugin) +void JackTrip::appendProcessPluginToNetwork(ProcessPlugin* plugin) { - mProcessPlugins.append(plugin); - //mAudioInterface->appendProcessPlugin(plugin); + if (plugin) { + mProcessPluginsToNetwork.append(plugin); // ownership transferred + //mAudioInterface->appendProcessPluginToNetwork(plugin); + } +} + +//******************************************************************************* +void JackTrip::appendProcessPluginFromNetwork(ProcessPlugin* plugin) +{ + if (plugin) { + mProcessPluginsFromNetwork.append(plugin); // ownership transferred + //mAudioInterface->appendProcessPluginFromNetwork(plugin); + } } @@ -333,20 +396,20 @@ void JackTrip::startProcess( #endif // endwhere ) { //signal that catches ctrl c in rtaudio-asio mode -#if defined (__WIN_32__) +/*#if defined (__WIN_32__) if (signal(SIGINT, sigint_handler) == SIG_ERR) { perror("signal"); exit(1); } -#endif +#endif*/ // Check if ports are already binded by another process on this machine // ------------------------------------------------------------------ if (gVerboseFlag) std::cout << "step 1" << std::endl; if (gVerboseFlag) std::cout << " JackTrip:startProcess before checkIfPortIsBinded(mReceiverBindPort)" << std::endl; #if defined __WIN_32__ - //cc fixed windows crash with this print statement! hope to delete -// qDebug() << "before mJackTrip->startProcess" << mReceiverBindPort<< mSenderBindPort; + //cc fixed windows crash with this print statement! + //qDebug() << "before mJackTrip->startProcess" << mReceiverBindPort<< mSenderBindPort; #endif checkIfPortIsBinded(mReceiverBindPort); if (gVerboseFlag) std::cout << " JackTrip:startProcess before checkIfPortIsBinded(mSenderBindPort)" << std::endl; @@ -365,18 +428,24 @@ void JackTrip::startProcess( setupRingBuffers(); // Connect Signals and Slots // ------------------------- - QObject::connect(mPacketHeader, SIGNAL(signalError(const char*)), - this, SLOT(slotStopProcesses()), Qt::QueuedConnection); + QObject::connect(mPacketHeader, &PacketHeader::signalError, + this, &JackTrip::slotStopProcessesDueToError, Qt::QueuedConnection); QObject::connect(mDataProtocolReceiver, SIGNAL(signalReceivedConnectionFromPeer()), this, SLOT(slotReceivedConnectionFromPeer()), Qt::QueuedConnection); - QObject::connect(this, SIGNAL(signalUdpTimeOut()), - this, SLOT(slotStopProcesses()), Qt::QueuedConnection); + //QObject::connect(this, SIGNAL(signalUdpTimeOut()), + // this, SLOT(slotStopProcesses()), Qt::QueuedConnection); + QObject::connect((UdpDataProtocol *)mDataProtocolReceiver, &UdpDataProtocol::signalUdpWaitingTooLong, this, + &JackTrip::slotUdpWaitingTooLong, Qt::QueuedConnection); + QObject::connect(mDataProtocolSender, &DataProtocol::signalCeaseTransmission, + this, &JackTrip::slotStopProcessesDueToError, Qt::QueuedConnection); + QObject::connect(mDataProtocolReceiver, &DataProtocol::signalCeaseTransmission, + this, &JackTrip::slotStopProcessesDueToError, Qt::QueuedConnection); //QObject::connect(mDataProtocolSender, SIGNAL(signalError(const char*)), // this, SLOT(slotStopProcesses()), Qt::QueuedConnection); - //QObject::connect(mDataProtocolReceiver, SIGNAL(signalError(const char*)), - // this, SLOT(slotStopProcesses()), Qt::QueuedConnection); + QObject::connect(mDataProtocolReceiver, SIGNAL(signalError(const char*)), + this, SLOT(slotStopProcesses()), Qt::QueuedConnection); // Start the threads for the specific mode // --------------------------------------- @@ -396,8 +465,7 @@ void JackTrip::startProcess( if (gVerboseFlag) std::cout << "step 2C client only" << std::endl; if (gVerboseFlag) std::cout << " JackTrip:startProcess case CLIENTTOPINGSERVER before clientPingToServerStart" << std::endl; if ( clientPingToServerStart() == -1 ) { // if error on server start (-1) we return inmediatly - mTcpConnectionError = true; - slotStopProcesses(); + stop("Peer Address has to be set if you run in CLIENTTOPINGSERVER mode"); return; } break; @@ -405,15 +473,18 @@ void JackTrip::startProcess( if (gVerboseFlag) std::cout << "step 2S server only (same as 2s)" << std::endl; if (gVerboseFlag) std::cout << " JackTrip:startProcess case SERVERPINGSERVER before serverStart" << std::endl; if ( serverStart(true) == -1 ) { // if error on server start (-1) we return inmediatly - slotStopProcesses(); + stop(); return; } break; default: - throw std::invalid_argument("Jacktrip Mode undefined"); + throw std::invalid_argument("Jacktrip Mode undefined"); break; } +} +void JackTrip::completeConnection() +{ // Have the threads share a single socket that operates at full duplex. #if defined (__WIN_32__) SOCKET sock_fd = INVALID_SOCKET; @@ -439,21 +510,27 @@ void JackTrip::startProcess( QThread::msleep(1); if (gVerboseFlag) std::cout << "step 5" << std::endl; if (gVerboseFlag) std::cout << " JackTrip:startProcess before mAudioInterface->startProcess" << std::endl; - mAudioInterface->startProcess(); - - for (int i = 0; i < mProcessPlugins.size(); ++i) { - mAudioInterface->appendProcessPlugin(mProcessPlugins[i]); + for (int i = 0; i < mProcessPluginsFromNetwork.size(); ++i) { + mAudioInterface->appendProcessPluginFromNetwork(mProcessPluginsFromNetwork[i]); } - if (mConnectDefaultAudioPorts) { mAudioInterface->connectDefaultPorts(); } -} + for (int i = 0; i < mProcessPluginsToNetwork.size(); ++i) { + mAudioInterface->appendProcessPluginToNetwork(mProcessPluginsToNetwork[i]); + } + mAudioInterface->initPlugins(); // mSampleRate known now, which plugins require + mAudioInterface->startProcess(); // Tell JACK server we are ready for audio flow now -//******************************************************************************* -void JackTrip::startIOStatTimer(int timeout_sec, const std::ostream& log_stream) -{ - mIOStatLogStream.rdbuf(log_stream.rdbuf()); - QTimer *timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(onStatTimer())); - timer->start(timeout_sec*1000); + if (mConnectDefaultAudioPorts) { mAudioInterface->connectDefaultPorts(); } + + //Start our IO stat timer + if (mIOStatTimeout > 0) { + cout << "STATS" << mIOStatTimeout << endl; + if (!mIOStatStream.isNull()) { + mIOStatLogStream.rdbuf(((std::ostream *)mIOStatStream.data())->rdbuf()); + } + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(onStatTimer())); + timer->start(mIOStatTimeout*1000); + } } //******************************************************************************* @@ -473,11 +550,12 @@ void JackTrip::onStatTimer() return; } QString now = QDateTime::currentDateTime().toString(Qt::ISODate); - int32_t skew = recv_io_stat.underruns - recv_io_stat.overflows - - pkt_stat.lost + pkt_stat.revived; static QMutex mutex; QMutexLocker locker(&mutex); + if (mAudioTesterP && mAudioTesterP->getEnabled()) { + mIOStatLogStream << "\n"; + } mIOStatLogStream << now.toLocal8Bit().constData() << " " << getPeerAddress().toLocal8Bit().constData() << " send: " @@ -492,13 +570,197 @@ void JackTrip::onStatTimer() << "/" << pkt_stat.revived << " tot: " << pkt_stat.tot - << " skew: " << skew + << " sync: " + << recv_io_stat.level + << "/" << recv_io_stat.buf_inc_underrun + << "/" << recv_io_stat.buf_inc_compensate + << "/" << recv_io_stat.buf_dec_overflows + << "/" << recv_io_stat.buf_dec_pktloss + << " skew: " << recv_io_stat.skew + << "/" << recv_io_stat.skew_raw + << " bcast: " << recv_io_stat.broadcast_skew + << "/" << recv_io_stat.broadcast_delta + << " autoq: " << 0.1*recv_io_stat.autoq_corr + << "/" << 0.1*recv_io_stat.autoq_rate << endl; } +void JackTrip::receivedConnectionTCP() +{ + mTimeoutTimer.stop(); + if (gVerboseFlag) cout << "TCP Socket Connected to Server!" << endl; + emit signalTcpClientConnected(); + + // Send Client Port Number to Server + // --------------------------------- + char port_buf[sizeof(mReceiverBindPort) + gMaxRemoteNameLength]; + std::memcpy(port_buf, &mReceiverBindPort, sizeof(mReceiverBindPort)); + std::memset(port_buf + sizeof(mReceiverBindPort), 0, gMaxRemoteNameLength); + if (!mRemoteClientName.isEmpty()) { + //If our remote client name is set, send it too. + QByteArray name = mRemoteClientName.toUtf8(); + // Find a clean place to truncate if we're over length. + // (Make sure we're not in the middle of a multi-byte characetr.) + int length = name.length(); + //Need to take the final null terminator into account here. + if (length > gMaxRemoteNameLength - 1) { + length = gMaxRemoteNameLength - 1; + while ((length > 0) && ((name.at(length) & 0xc0) == 0x80)) { + //We're in the middle of a multi-byte character. Work back. + length--; + } + } + name.truncate(length); + std::memcpy(port_buf + sizeof(mReceiverBindPort), name.data(), length + 1); + } + + mTcpClient.write(port_buf, sizeof(port_buf)); + /*while ( mTcpClient.bytesToWrite() > 0 ) { + mTcpClient.waitForBytesWritten(-1); + }*/ + if (gVerboseFlag) cout << "Port " << mReceiverBindPort << " sent to Server" << endl; + //Continued in receivedDataTCP slot +} + +void JackTrip::receivedDataTCP() +{ + if (mTcpClient.bytesAvailable() < (int)sizeof(uint16_t)) { + return; + } + + // Read the size of the package + // ---------------------------- + if (gVerboseFlag) cout << "Reading UDP port from Server..." << endl; + if (gVerboseFlag) cout << "Ready To Read From Socket!" << endl; + + // Read UDP Port Number from Server + // -------------------------------- + uint32_t udp_port; + int size = sizeof(udp_port); + char port_buf[sizeof(mReceiverBindPort)]; + //char port_buf[size]; + mTcpClient.read(port_buf, size); + std::memcpy(&udp_port, port_buf, size); + //cout << "Received UDP Port Number: " << udp_port << endl; + + // Close the TCP Socket + // -------------------- + mTcpClient.close(); // Close the socket + //cout << "TCP Socket Closed!" << endl; + if (gVerboseFlag) cout << "Connection Succesfull!" << endl; + + // Set with the received UDP port + // ------------------------------ + setPeerPorts(udp_port); + mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().data() ); + mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() ); + mDataProtocolSender->setPeerPort(udp_port); + mDataProtocolReceiver->setPeerPort(udp_port); + cout << "Server Address set to: " << mPeerAddress.toStdString() << " Port: " << udp_port << std::endl; + cout << gPrintSeparator << endl; + completeConnection(); +} + +void JackTrip::receivedDataUDP() +{ + //Stop our timer. + mTimeoutTimer.stop(); + + QHostAddress peerHostAddress; + uint16_t peer_port; + + // IPv6 addition from fyfe + // Get the datagram size to avoid problems with IPv6 + qint64 datagramSize = mUdpSockTemp.pendingDatagramSize(); + char buf[datagramSize]; + // set client address + mUdpSockTemp.readDatagram(buf, datagramSize, &peerHostAddress, &peer_port); + mUdpSockTemp.close(); // close the socket + + // Check for mapped IPv4->IPv6 addresses that look like ::ffff:x.x.x.x + if (peerHostAddress.protocol() == QAbstractSocket::IPv6Protocol) { + bool mappedIPv4; + uint32_t address = peerHostAddress.toIPv4Address(&mappedIPv4); + // If the IPv4 address is mapped to IPv6, convert it to IPv4 + if (mappedIPv4) { + QHostAddress ipv4Address = QHostAddress(address); + mPeerAddress = ipv4Address.toString(); + } else { + mPeerAddress = peerHostAddress.toString(); + } + } + else { + mPeerAddress = peerHostAddress.toString(); + } + + // Set the peer address to send packets (in the protocol sender) + if (gVerboseFlag) std::cout << "JackTrip:serverStart before mDataProtocolSender->setPeerAddress()" << std::endl; + mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().constData() ); + if (gVerboseFlag) std::cout << "JackTrip:serverStart before mDataProtocolReceiver->setPeerAddress()" << std::endl; + mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().constData() ); + // We reply to the same port the peer sent the packets from + // This way we can go through NAT + // Because of the NAT traversal scheme, the portn need to be + // "symetric", e.g.: + // from Client to Server : src = 4474, dest = 4464 + // from Server to Client : src = 4464, dest = 4474 + // no -- all are the same -- 4464 + if (gVerboseFlag) std::cout << "JackTrip:serverStart before setting all peer_port instances to " << peer_port << std::endl; + mDataProtocolSender->setPeerPort(peer_port); + mDataProtocolReceiver->setPeerPort(peer_port); + setPeerPorts(peer_port); + completeConnection(); +} + +void JackTrip::udpTimerTick() +{ + if (mStopped || sSigInt || sJackStopped) { + //Stop everything. + mUdpSockTemp.close(); + mTimeoutTimer.stop(); + stop(); + } + + if (gVerboseFlag) std::cout << mSleepTime << "ms " << std::flush; + mElapsedTime += mSleepTime; + if (mEndTime > 0 && mElapsedTime >= mEndTime) { + mUdpSockTemp.close(); + mTimeoutTimer.stop(); + cout << "JackTrip Server Timed Out!" << endl; + stop("JackTrip Server Timed Out"); + } +} + +void JackTrip::tcpTimerTick() +{ + if (mStopped || sSigInt || sJackStopped) { + //Stop everything. + mTcpClient.close(); + mTimeoutTimer.stop(); + stop(); + } + + mElapsedTime += mSleepTime; + if (mEndTime > 0 && mElapsedTime >= mEndTime) { + mTcpClient.close(); + mTimeoutTimer.stop(); + cout << "JackTrip Server Timed Out!" << endl; + stop("Initial TCP Connection Timed Out"); + } + +} + //******************************************************************************* -void JackTrip::stop() +void JackTrip::stop(QString errorMessage) { + mStopped = true; + //Make sure we're only run once + if (mHasShutdown) { + return; + } + mHasShutdown = true; + std::cout << "Stopping JackTrip..." << std::endl; + // Stop The Sender mDataProtocolSender->stop(); mDataProtocolSender->wait(); @@ -510,12 +772,18 @@ void JackTrip::stop() // Stop the audio processes //mAudioInterface->stopProcess(); closeAudio(); - + cout << "JackTrip Processes STOPPED!" << endl; cout << gPrintSeparator << endl; // Emit the jack stopped signal - emit signalProcessesStopped(); + if (sJackStopped) { + emit signalError("The Jack Server was shut down!"); + } else if (errorMessage.isEmpty()) { + emit signalProcessesStopped(); + } else { + emit signalError(errorMessage); + } } @@ -539,6 +807,7 @@ void JackTrip::clientStart() mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().data() ); cout << "Peer Address set to: " << mPeerAddress.toStdString() << std::endl; cout << gPrintSeparator << endl; + completeConnection(); } } @@ -555,88 +824,34 @@ int JackTrip::serverStart(bool timeout, int udpTimeout) // udpTimeout unused } // Get the client address when it connects - QHostAddress peerHostAddress; - uint16_t peer_port; - if (gVerboseFlag) std::cout << "JackTrip:serverStart before QUdpSocket UdpSockTemp" << std::endl; - QUdpSocket UdpSockTemp;// Create socket to wait for client - - if (gVerboseFlag) std::cout << "JackTrip:serverStart before UdpSockTemp.bind(Any)" << std::endl; + if (gVerboseFlag) std::cout << "JackTrip:serverStart before mUdpSockTemp.bind(Any)" << std::endl; // Bind the socket - if ( !UdpSockTemp.bind(QHostAddress::Any, mReceiverBindPort, + if ( !mUdpSockTemp.bind(QHostAddress::Any, mReceiverBindPort, QUdpSocket::DefaultForPlatform) ) { std::cerr << "in JackTrip: Could not bind UDP socket. It may be already binded." << endl; throw std::runtime_error("Could not bind UDP socket. It may be already binded."); } - // Listen to client - int sleepTime = 100; // ms - int elapsedTime = 0; + connect(&mUdpSockTemp, &QUdpSocket::readyRead, this, &JackTrip::receivedDataUDP); + + // Start timer and then wait for a signal to read datagrams. + mElapsedTime = 0; if (timeout) { - while ( (!UdpSockTemp.hasPendingDatagrams()) && (elapsedTime <= udpTimeout) ) { - if (mStopped == true) { emit signalUdpTimeOut(); UdpSockTemp.close(); return -1; } - QThread::msleep(sleepTime); - elapsedTime += sleepTime; - } - if (!UdpSockTemp.hasPendingDatagrams()) { - emit signalUdpTimeOut(); - cout << "JackTrip Server Timed Out!" << endl; - return -1; - } - } else { - if (gVerboseFlag) std::cout << "JackTrip:serverStart before !UdpSockTemp.hasPendingDatagrams()" << std::endl; - cout << "Waiting for Connection From a Client..." << endl; - while ( !UdpSockTemp.hasPendingDatagrams() ) { - if (mStopped == true) { emit signalUdpTimeOut(); return -1; } - if (gVerboseFlag) std::cout << sleepTime << "ms " << std::flush; - QThread::msleep(sleepTime); - } + mEndTime = udpTimeout; } + mTimeoutTimer.setInterval(mSleepTime); + connect(&mTimeoutTimer, &QTimer::timeout, this, &JackTrip::udpTimerTick); + mTimeoutTimer.start(); + + if (gVerboseFlag) std::cout << "JackTrip:serverStart before !UdpSockTemp.hasPendingDatagrams()" << std::endl; + cout << "Waiting for Connection From a Client..." << endl; + return 0; + // Continued in the receivedDataUDP slot. + // char buf[1]; // // set client address // UdpSockTemp.readDatagram(buf, 1, &peerHostAddress, &peer_port); // UdpSockTemp.close(); // close the socket - - // IPv6 addition from fyfe - // Get the datagram size to avoid problems with IPv6 - qint64 datagramSize = UdpSockTemp.pendingDatagramSize(); - char buf[datagramSize]; - // set client address - UdpSockTemp.readDatagram(buf, datagramSize, &peerHostAddress, &peer_port); - UdpSockTemp.close(); // close the socket - - // Check for mapped IPv4->IPv6 addresses that look like ::ffff:x.x.x.x - if (peerHostAddress.protocol() == QAbstractSocket::IPv6Protocol) { - bool mappedIPv4; - uint32_t address = peerHostAddress.toIPv4Address(&mappedIPv4); - // If the IPv4 address is mapped to IPv6, convert it to IPv4 - if (mappedIPv4) { - QHostAddress ipv4Address = QHostAddress(address); - mPeerAddress = ipv4Address.toString(); - } else { - mPeerAddress = peerHostAddress.toString(); - } - } - else { - mPeerAddress = peerHostAddress.toString(); - } - - // Set the peer address to send packets (in the protocol sender) - if (gVerboseFlag) std::cout << "JackTrip:serverStart before mDataProtocolSender->setPeerAddress()" << std::endl; - mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().constData() ); - if (gVerboseFlag) std::cout << "JackTrip:serverStart before mDataProtocolReceiver->setPeerAddress()" << std::endl; - mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().constData() ); - // We reply to the same port the peer sent the packets from - // This way we can go through NAT - // Because of the NAT traversal scheme, the portn need to be - // "symetric", e.g.: - // from Client to Server : src = 4474, dest = 4464 - // from Server to Client : src = 4464, dest = 4474 - // no -- all are the same -- 4464 - if (gVerboseFlag) std::cout << "JackTrip:serverStart before setting all peer_port instances to " << peer_port << std::endl; - mDataProtocolSender->setPeerPort(peer_port); - mDataProtocolReceiver->setPeerPort(peer_port); - setPeerPorts(peer_port); - return 0; } @@ -656,7 +871,6 @@ int JackTrip::clientPingToServerStart() // Create Socket Objects // -------------------- - QTcpSocket tcpClient; QHostAddress serverHostAddress; if (!serverHostAddress.setAddress(mPeerAddress)) { QHostInfo info = QHostInfo::fromName(mPeerAddress); @@ -668,64 +882,18 @@ int JackTrip::clientPingToServerStart() // Connect Socket to Server and wait for response // ---------------------------------------------- - tcpClient.connectToHost(serverHostAddress, mTcpServerPort); + connect(&mTcpClient, &QTcpSocket::readyRead, this, &JackTrip::receivedDataTCP); + connect(&mTcpClient, &QTcpSocket::connected, this, &JackTrip::receivedConnectionTCP); + mElapsedTime = 0; + mEndTime = 5000; //Timeout after 5 seconds. + mTimeoutTimer.setInterval(mSleepTime); + connect(&mTimeoutTimer, &QTimer::timeout, this, &JackTrip::tcpTimerTick); + mTimeoutTimer.start(); + mTcpClient.connectToHost(serverHostAddress, mTcpServerPort); + if (gVerboseFlag) cout << "Connecting to TCP Server at " << serverHostAddress.toString().toLatin1().constData() << " port " << mTcpServerPort << "..." << endl; - if (!tcpClient.waitForConnected()) { - std::cerr << "TCP Socket ERROR at " << mTcpServerPort << ": " << tcpClient.errorString().toStdString() << endl; - //std::exit(1); - return -1; - } - if (gVerboseFlag) cout << "TCP Socket Connected to Server!" << endl; - emit signalTcpClientConnected(); - - // Send Client Port Number to Server - // --------------------------------- - char port_buf[sizeof(mReceiverBindPort)]; - std::memcpy(port_buf, &mReceiverBindPort, sizeof(mReceiverBindPort)); - - tcpClient.write(port_buf, sizeof(mReceiverBindPort)); - while ( tcpClient.bytesToWrite() > 0 ) { - tcpClient.waitForBytesWritten(-1); - } - if (gVerboseFlag) cout << "Port " << mReceiverBindPort << " sent to Server" << endl; - - // Read the size of the package - // ---------------------------- - if (gVerboseFlag) cout << "Reading UDP port from Server..." << endl; - while (tcpClient.bytesAvailable() < (int)sizeof(uint16_t)) { - if (!tcpClient.waitForReadyRead()) { - std::cerr << "TCP Socket ERROR: " << tcpClient.errorString().toStdString() << endl; - //std::exit(1); - return -1; - } - } - if (gVerboseFlag) cout << "Ready To Read From Socket!" << endl; - - // Read UDP Port Number from Server - // -------------------------------- - uint32_t udp_port; - int size = sizeof(udp_port); - //char port_buf[size]; - tcpClient.read(port_buf, size); - std::memcpy(&udp_port, port_buf, size); - //cout << "Received UDP Port Number: " << udp_port << endl; - - // Close the TCP Socket - // -------------------- - tcpClient.close(); // Close the socket - //cout << "TCP Socket Closed!" << endl; - if (gVerboseFlag) cout << "Connection Successful!" << endl; - - // Set with the received UDP port - // ------------------------------ - setPeerPorts(udp_port); - mDataProtocolReceiver->setPeerAddress( mPeerAddress.toLatin1().data() ); - mDataProtocolSender->setPeerAddress( mPeerAddress.toLatin1().data() ); - mDataProtocolSender->setPeerPort(udp_port); - mDataProtocolReceiver->setPeerPort(udp_port); - cout << "Server Address set to: " << mPeerAddress.toStdString() << " Port: " << udp_port << std::endl; - cout << gPrintSeparator << endl; return 0; + // Continued in the receivedConnectionTCP slot. /* else { @@ -893,7 +1061,6 @@ void JackTrip::parseAudioPacket(int8_t* full_packet, int8_t* audio_packet) std::memcpy(audio_packet, audio_part, getTotalAudioPacketSizeInBytes()); } - //******************************************************************************* void JackTrip::checkPeerSettings(int8_t* full_packet) { diff --git a/src/JackTrip.h b/src/JackTrip.h index 9885a21..50e90ed 100644 --- a/src/JackTrip.h +++ b/src/JackTrip.h @@ -44,6 +44,9 @@ #include <QObject> #include <QString> #include <QUdpSocket> +#include <QTcpSocket> +#include <QTimer> +#include <QSharedPointer> #include "DataProtocol.h" #include "AudioInterface.h" @@ -54,8 +57,9 @@ #include "PacketHeader.h" #include "RingBuffer.h" +#include "AudioTester.h" -#include <signal.h> +//#include <signal.h> /** \brief Main class to creates a SERVER (to listen) or a CLIENT (to connect * to a listening server) to send audio streams in the network. * @@ -64,7 +68,7 @@ * Classes that uses JackTrip methods need to register with it. */ -class JackTrip : public QThread +class JackTrip : public QObject { Q_OBJECT; @@ -80,8 +84,8 @@ public: /// \brief Enum for the JackTrip mode enum jacktripModeT { - SERVER, ///< Run in Server Mode - CLIENT, ///< Run in Client Mode + SERVER, ///< Run in P2P Server Mode + CLIENT, ///< Run in P2P Client Mode CLIENTTOPINGSERVER, ///< Client of the Ping Server Mode SERVERPINGSERVER ///< Server of the MultiThreaded JackTrip }; @@ -111,7 +115,8 @@ public: CLIENTECHO, ///< Client Echo (client self-to-self) CLIENTFOFI, ///< Client Fan Out to Clients and Fan In from Clients (but not self-to-self) RESERVEDMATRIX, ///< Reserved for custom patch matrix (for TUB ensemble) - FULLMIX ///< Client Fan Out to Clients and Fan In from Clients (including self-to-self) + FULLMIX, ///< Client Fan Out to Clients and Fan In from Clients (including self-to-self) + NOAUTO ///< No automatic patching }; //--------------------------------------------------------- @@ -133,9 +138,9 @@ public: int BufferQueueLength = gDefaultQueueLength, unsigned int redundancy = gDefaultRedundancy, AudioInterface::audioBitResolutionT AudioBitResolution = - AudioInterface::BIT16, + AudioInterface::BIT16, DataProtocol::packetHeaderTypeT PacketHeaderType = - DataProtocol::DEFAULT, + DataProtocol::DEFAULT, underrunModeT UnderRunMode = WAVETABLE, int receiver_bind_port = gDefaultPort, int sender_bind_port = gDefaultPort, @@ -145,20 +150,26 @@ public: /// \brief The class destructor virtual ~JackTrip(); + + static void sigIntHandler(__attribute__((unused)) int unused) + { std::cout << std::endl << "Shutting Down..." << std::endl; sSigInt = true; } + static bool sSigInt; + static bool sJackStopped; /// \brief Starting point for the thread - virtual void run() { + /*virtual void run() { if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->run" << std::endl; - } + }*/ /// \brief Set the Peer Address for jacktripModeT::CLIENT mode only - virtual void setPeerAddress(const char* PeerHostOrIP); + virtual void setPeerAddress(QString PeerHostOrIP); /** \brief Append a process plugin. Processes will be appended in order * \param plugin Pointer to ProcessPlugin Class */ //void appendProcessPlugin(const std::tr1::shared_ptr<ProcessPlugin> plugin); - virtual void appendProcessPlugin(ProcessPlugin* plugin); + virtual void appendProcessPluginToNetwork(ProcessPlugin* plugin); + virtual void appendProcessPluginFromNetwork(ProcessPlugin* plugin); /// \brief Start the processing threads virtual void startProcess( @@ -166,9 +177,10 @@ public: int ID #endif // endwhere ); + virtual void completeConnection(); /// \brief Stop the processing threads - virtual void stop(); + virtual void stop(QString errorMessage = ""); /// \brief Wait for all the threads to finish. This functions is used when JackTrip is /// run as a thread @@ -199,12 +211,17 @@ public: /// \brief Sets (override) Buffer Queue Length Mode after construction virtual void setBufferQueueLength(int BufferQueueLength) { mBufferQueueLength = BufferQueueLength; } + virtual void setBufferStrategy(int BufferStrategy) + { mBufferStrategy = BufferStrategy; } /// \brief Sets (override) Audio Bit Resolution after construction virtual void setAudioBitResolution(AudioInterface::audioBitResolutionT AudioBitResolution) { mAudioBitResolution = AudioBitResolution; } /// \brief Sets (override) Underrun Mode virtual void setUnderRunMode(underrunModeT UnderRunMode) { mUnderRunMode = UnderRunMode; } + /// \brief Sets whether to quit on timeout. + virtual void setStopOnTimeout(bool stopOnTimeout) + { mStopOnTimeout = stopOnTimeout; } /// \brief Sets port numbers for the local and peer machine. /// Receive port is <tt>port</tt> virtual void setAllPorts(int port) @@ -229,9 +246,14 @@ public: /// \brief Set Client Name to something different that the default (JackTrip) virtual void setClientName(QString clientName) { mJackClientName = clientName; } + virtual void setRemoteClientName(QString remoteClientName) + { mRemoteClientName = remoteClientName; } /// \brief Set the number of audio channels virtual void setNumChannels(int num_chans) { mNumChans = num_chans; } + + virtual void setIOStatTimeout(int timeout) { mIOStatTimeout = timeout; } + virtual void setIOStatStream(QSharedPointer<std::ofstream> statStream) { mIOStatStream = statStream; } /// Set to connect or not default audio ports (only implemented in Jack) virtual void setConnectDefaultAudioPorts(bool connect) @@ -274,7 +296,9 @@ public: { mAudiointerfaceMode = audiointerface_mode; } virtual void setAudioInterface(AudioInterface* const AudioInterface) { mAudioInterface = AudioInterface; } - + virtual void setLoopBack(bool b) + { mLoopBack = b; } + virtual void setAudioTesterP(AudioTester* atp) { mAudioTesterP = atp; } void setSampleRate(uint32_t sample_rate) { mSampleRate = sample_rate; } @@ -305,6 +329,7 @@ public: bool tcpConnectionError() { return mTcpConnectionError; } + //@} //------------------------------------------------------------------------------------ @@ -318,13 +343,15 @@ public: virtual int getPacketSizeInBytes(); void parseAudioPacket(int8_t* full_packet, int8_t* audio_packet); virtual void sendNetworkPacket(const int8_t* ptrToSlot) - { mSendRingBuffer->insertSlotNonBlocking(ptrToSlot); } + { mSendRingBuffer->insertSlotNonBlocking(ptrToSlot, 0, 0); } + virtual void receiveBroadcastPacket(int8_t* ptrToReadSlot) + { mReceiveRingBuffer->readBroadcastSlot(ptrToReadSlot); } virtual void receiveNetworkPacket(int8_t* ptrToReadSlot) { mReceiveRingBuffer->readSlotNonBlocking(ptrToReadSlot); } virtual void readAudioBuffer(int8_t* ptrToReadSlot) { mSendRingBuffer->readSlotBlocking(ptrToReadSlot); } - virtual void writeAudioBuffer(const int8_t* ptrToSlot) - { mReceiveRingBuffer->insertSlotNonBlocking(ptrToSlot); } + virtual bool writeAudioBuffer(const int8_t* ptrToSlot, int len, int lostLen) + { return mReceiveRingBuffer->insertSlotNonBlocking(ptrToSlot, len, lostLen); } uint32_t getBufferSizeInSamples() const { return mAudioBufferSize; /*return mAudioInterface->getBufferSizeInSamples();*/ } uint32_t getDeviceID() const @@ -393,16 +420,21 @@ public: void printTextTest() {std::cout << "=== JackTrip PRINT ===" << std::endl;} void printTextTest2() {std::cout << "=== JackTrip PRINT2 ===" << std::endl;} - void startIOStatTimer(int timeout_sec, const std::ostream& log_stream); + void setNetIssuesSimulation(double loss, double jitter, double delay_rel) + { + mSimulatedLossRate = loss; + mSimulatedJitterRate = jitter; + mSimulatedDelayRel = delay_rel; + } + void setBroadcast(int broadcast_queue) {mBroadcastQueueLength = broadcast_queue;} + void setUseRtUdpPriority(bool use) {mUseRtUdpPriority = use;} public slots: /// \brief Slot to stop all the processes and threads virtual void slotStopProcesses() - { - std::cout << "Stopping JackTrip..." << std::endl; - mStopped = true; - this->stop(); - } + { this->stop(); } + virtual void slotStopProcessesDueToError(const QString &errorMessage) + { this->stop(errorMessage); } /** \brief This slot emits in turn the signal signalNoUdpPacketsForSeconds * when UDP has waited for more than 30 seconds. @@ -414,25 +446,37 @@ public slots: int wait_time = 10000; // msec if ( !(wait_msec%wait_time) ) { std::cerr << "UDP WAITED MORE THAN 10 seconds." << std::endl; + if (mStopOnTimeout) { + stop("No network data received for 10 seconds"); + } emit signalNoUdpPacketsForSeconds(); } } + void slotUdpWaitingTooLong() + { emit signalUdpWaitingTooLong(); } void slotPrintTest() { std::cout << "=== TESTING ===" << std::endl; } void slotReceivedConnectionFromPeer() - { mReceivedConnection = true; } + { mReceivedConnection = true; emit signalReceivedConnectionFromPeer(); } void onStatTimer(); - + +private slots: + void receivedConnectionTCP(); + void receivedDataTCP(); + void receivedDataUDP(); + void udpTimerTick(); + void tcpTimerTick(); signals: - - void signalUdpTimeOut(); + //void signalUdpTimeOut(); /// \brief Signal emitted when all the processes and threads are stopped void signalProcessesStopped(); /// \brief Signal emitted when no UDP Packets have been received for a while void signalNoUdpPacketsForSeconds(); void signalTcpClientConnected(); - + void signalError(const QString &errorMessage); + void signalReceivedConnectionFromPeer(); + void signalUdpWaitingTooLong(); public: @@ -475,10 +519,13 @@ private: int mNumNetRevChans; ///< Number of Network Audio Channels (net comb filters) #endif // endwhere int mBufferQueueLength; ///< Audio Buffer from network queue length + int mBufferStrategy; + int mBroadcastQueueLength; uint32_t mSampleRate; ///< Sample Rate uint32_t mDeviceID; ///< RTAudio DeviceID uint32_t mAudioBufferSize; ///< Audio buffer size to process on each callback AudioInterface::audioBitResolutionT mAudioBitResolution; ///< Audio Bit Resolutions + bool mLoopBack; QString mPeerAddress; ///< Peer Address to use in jacktripModeT::CLIENT Mode /// Pointer to Abstract Type DataProtocol that sends packets @@ -488,6 +535,7 @@ private: AudioInterface* mAudioInterface; ///< Interface to Jack Client PacketHeader* mPacketHeader; ///< Pointer to Packet Header underrunModeT mUnderRunMode; ///< underrunModeT Mode + bool mStopOnTimeout; ///< Stop on 10 second timeout /// Pointer for the Send RingBuffer RingBuffer* mSendRingBuffer; @@ -502,18 +550,36 @@ private: unsigned int mRedundancy; ///< Redundancy factor in network data QString mJackClientName; ///< JackAudio Client Name + QString mRemoteClientName; ///< Remote JackAudio Client Name for hub client mode JackTrip::connectionModeT mConnectionMode; ///< Connection Mode JackTrip::hubConnectionModeT mHubConnectionModeT; ///< Hub Server Jack Audio Patch Connection Mode - QVector<ProcessPlugin*> mProcessPlugins; ///< Vector of ProcesPlugin<EM>s</EM> + QVector<ProcessPlugin*> mProcessPluginsFromNetwork; ///< Vector of ProcessPlugin<EM>s</EM> + QVector<ProcessPlugin*> mProcessPluginsToNetwork; ///< Vector of ProcessPlugin<EM>s</EM> + + QTimer mTimeoutTimer; + int mSleepTime; + int mElapsedTime; + int mEndTime; + QTcpSocket mTcpClient; + QUdpSocket mUdpSockTemp; volatile bool mReceivedConnection; ///< Bool of received connection from peer volatile bool mTcpConnectionError; volatile bool mStopped; + volatile bool mHasShutdown; bool mConnectDefaultAudioPorts; ///< Connect or not default audio ports + QSharedPointer<std::ofstream> mIOStatStream; + int mIOStatTimeout; std::ostream mIOStatLogStream; + double mSimulatedLossRate; + double mSimulatedJitterRate; + double mSimulatedDelayRel; + bool mUseRtUdpPriority; + + AudioTester* mAudioTesterP; }; #endif diff --git a/src/JackTripThread.cpp b/src/JackTripThread.cpp index abe1e9c..bcaeaf0 100644 --- a/src/JackTripThread.cpp +++ b/src/JackTripThread.cpp @@ -57,14 +57,14 @@ void JackTripThread::run() } NetKS netks; - jacktrip.appendProcessPlugin(&netks); + jacktrip.appendProcessPluginFromNetwork(&netks); //netks.play(); //QThread::sleep(1); - jacktrip.start(); + //jacktrip.start(); //netks.play(); - jacktrip.wait(); + //jacktrip.wait(); cout << "******** AFTER JACKTRIPTHREAD START **************" << endl; diff --git a/src/JackTripWorker.cpp b/src/JackTripWorker.cpp index f006261..995c4a2 100644 --- a/src/JackTripWorker.cpp +++ b/src/JackTripWorker.cpp @@ -45,7 +45,7 @@ #include "JackTripWorker.h" #include "JackTrip.h" #include "UdpHubListener.h" -#include "NetKS.h" +//#include "NetKS.h" #include "LoopBack.h" #include "Settings.h" #ifdef WAIR // wair @@ -58,14 +58,16 @@ using std::cout; using std::endl; //******************************************************************************* -JackTripWorker::JackTripWorker(UdpHubListener* udpmasterlistener, int BufferQueueLength, JackTrip::underrunModeT UnderRunMode) : - mUdpHubListener(udpmasterlistener), +JackTripWorker::JackTripWorker(UdpHubListener* udphublistener, int BufferQueueLength, JackTrip::underrunModeT UnderRunMode, QString clientName) : + mUdpHubListener(udphublistener), m_connectDefaultAudioPorts(false), mBufferQueueLength(BufferQueueLength), mUnderRunMode(UnderRunMode), + mClientName(clientName), mSpawning(false), mID(0), - mNumChans(1) + mNumChans(1), + mIOStatTimeout(0) #ifdef WAIR // wair ,mNumNetRevChans(0), mWAIR(false) @@ -74,6 +76,12 @@ JackTripWorker::JackTripWorker(UdpHubListener* udpmasterlistener, int BufferQueu setAutoDelete(false); // stick around after calling run() //mNetks = new NetKS; //mNetks->play(); + mBufferStrategy = 1; + mBroadcastQueue = 0; + mSimulatedLossRate = 0.0; + mSimulatedJitterRate = 0.0; + mSimulatedDelayRel = 0.0; + mUseRtUdpPriority = false; } @@ -128,7 +136,6 @@ void JackTripWorker::run() // Create and setup JackTrip Object //JackTrip jacktrip(JackTrip::SERVER, JackTrip::UDP, mNumChans, 2); if (gVerboseFlag) cout << "---> JackTripWorker: Creating jacktrip objects..." << endl; - Settings* settings = mUdpHubListener->getSettings(); #ifdef WAIR // WAIR // forces BufferQueueLength to 2 @@ -162,7 +169,7 @@ void JackTripWorker::run() switch ( mNumNetRevChans ) { case 16 : // freeverb - mJackTrip->appendProcessPlugin(new dcblock2gain(mNumChans)); // plugin slot 0 + mJackTrip->appendProcessPluginFromNetwork(new dcblock2gain(mNumChans)); // plugin slot 0 /////////////// // mJackTrip->appendProcessPlugin(new comb16server(mNumNetChans)); // -S LAIR no AP mJackTrip->appendProcessPlugin(new AP8(mNumChans)); @@ -184,6 +191,14 @@ void JackTripWorker::run() // Set our underrun mode jacktrip.setUnderRunMode(mUnderRunMode); + if (mIOStatTimeout > 0) { + jacktrip.setIOStatTimeout(mIOStatTimeout); + jacktrip.setIOStatStream(mIOStatStream); + } + + if (!mClientName.isEmpty()) { + jacktrip.setClientName(mClientName); + } // Connect signals and slots // ------------------------- @@ -195,6 +210,7 @@ void JackTripWorker::run() // Connection to terminate the local eventloop when jacktrip is done QObject::connect(&jacktrip, SIGNAL(signalProcessesStopped()), &event_loop, SLOT(quit()), Qt::QueuedConnection); + QObject::connect(&jacktrip, &JackTrip::signalError, &event_loop, &QEventLoop::quit, Qt::QueuedConnection); QObject::connect(this, SIGNAL(signalRemoveThread()), &jacktrip, SLOT(slotStopProcesses()), Qt::QueuedConnection); @@ -203,9 +219,14 @@ void JackTripWorker::run() // I still haven't figure out why //ClientAddress.toString().toLatin1().constData(); //jacktrip.setPeerAddress(ClientAddress.toString().toLatin1().constData()); - jacktrip.setPeerAddress(mClientAddress.toLatin1().constData()); + jacktrip.setPeerAddress(mClientAddress); jacktrip.setBindPorts(mServerPort); //jacktrip.setPeerPorts(mClientPort); + jacktrip.setBufferStrategy(mBufferStrategy); + jacktrip.setNetIssuesSimulation(mSimulatedLossRate, + mSimulatedJitterRate, mSimulatedDelayRel); + jacktrip.setBroadcast(mBroadcastQueue); + jacktrip.setUseRtUdpPriority(mUseRtUdpPriority); if (gVerboseFlag) cout << "---> JackTripWorker: setJackTripFromClientHeader..." << endl; int PeerConnectionMode = setJackTripFromClientHeader(jacktrip); @@ -222,9 +243,6 @@ void JackTripWorker::run() mID #endif // endwhere ); - if (0 != settings->getIOStatTimeout()) { - jacktrip.startIOStatTimer(settings->getIOStatTimeout(), settings->getIOStatStream()); - } // if (gVerboseFlag) cout << "---> JackTripWorker: start..." << endl; // jacktrip.start(); // ########### JamTest Only ################# @@ -233,11 +251,11 @@ void JackTripWorker::run() event_loop.exec(); // Excecution will block here until exit() the QEventLoop //-------------------------------------------------------------------------- - + { QMutexLocker locker(&mMutex); mSpawning = true; } // wait for jacktrip to be done before exiting the Worker Thread - jacktrip.wait(); + //jacktrip.wait(); } catch ( const std::exception & e ) diff --git a/src/JackTripWorker.h b/src/JackTripWorker.h index 62c65da..98524cf 100644 --- a/src/JackTripWorker.h +++ b/src/JackTripWorker.h @@ -70,7 +70,7 @@ class JackTripWorker : public QObject, public QRunnable public: /// \brief The class constructor - JackTripWorker(UdpHubListener* udpmasterlistener, int BufferQueueLength = gDefaultQueueLength, JackTrip::underrunModeT UnderRunMode = JackTrip::WAVETABLE); + JackTripWorker(UdpHubListener* udphublistener, int BufferQueueLength = gDefaultQueueLength, JackTrip::underrunModeT UnderRunMode = JackTrip::WAVETABLE, QString clientName = ""); /// \brief The class destructor virtual ~JackTripWorker(); @@ -97,7 +97,19 @@ public: return mID; } - + void setBufferStrategy(int BufferStrategy) { mBufferStrategy = BufferStrategy; } + void setNetIssuesSimulation(double loss, double jitter, double delay_rel) + { + mSimulatedLossRate = loss; + mSimulatedJitterRate = jitter; + mSimulatedDelayRel = delay_rel; + } + void setBroadcast(int broadcast_queue) {mBroadcastQueue = broadcast_queue;} + void setUseRtUdpPriority(bool use) {mUseRtUdpPriority = use;} + + void setIOStatTimeout(int timeout) { mIOStatTimeout = timeout; } + void setIOStatStream(QSharedPointer<std::ofstream> statStream) { mIOStatStream = statStream; } + private slots: void slotTest() { std::cout << "--- JackTripWorker TEST SLOT ---" << std::endl; } @@ -106,7 +118,6 @@ private slots: signals: void signalRemoveThread(); - private: int setJackTripFromClientHeader(JackTrip& jacktrip); JackTrip::connectionModeT getConnectionModeFromHeader(); @@ -119,16 +130,28 @@ private: /// Client Outgoing Port. By convention, the receving port will be <tt>mClientPort -1</tt> uint16_t mClientPort; + + int mBufferQueueLength; + JackTrip::underrunModeT mUnderRunMode; + QString mClientName; /// Thread spawning internal lock. /// If true, the prototype is working on creating (spawning) a new thread volatile bool mSpawning; QMutex mMutex; ///< Mutex to protect mSpawning - JackTrip::underrunModeT mUnderRunMode; - int mBufferQueueLength; int mID; ///< ID thread number int mNumChans; ///< Number of Channels + + int mBufferStrategy; + int mBroadcastQueue; + double mSimulatedLossRate; + double mSimulatedJitterRate; + double mSimulatedDelayRel; + bool mUseRtUdpPriority; + + int mIOStatTimeout; + QSharedPointer<std::ofstream> mIOStatStream; #ifdef WAIR // wair int mNumNetRevChans; ///< Number of Net Channels = net combs bool mWAIR; diff --git a/src/JitterBuffer.cpp b/src/JitterBuffer.cpp new file mode 100644 index 0000000..9f73d53 --- /dev/null +++ b/src/JitterBuffer.cpp @@ -0,0 +1,389 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2020 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file JitterBuffer.cpp + * \author Anton Runov + * \date June 2020 + */ + + +#include "JitterBuffer.h" + +#include <iostream> +#include <cstring> +#include <cstdlib> +#include <stdexcept> +#include <cmath> + +using std::cout; using std::endl; + + +//******************************************************************************* +JitterBuffer::JitterBuffer(int buf_samples, int qlen, int sample_rate, int strategy, + int bcast_qlen, int channels, int bit_res) : + RingBuffer(0, 0) +{ + int total_size = sample_rate * channels * bit_res * 2; // 2 secs of audio + int slot_size = buf_samples * channels * bit_res; + mSlotSize = slot_size; + mInSlotSize = slot_size; + if (0 < qlen) { + mMaxLatency = qlen * slot_size; + mAutoQueue = 0; + } + else { + // AutoQueue + mMaxLatency = 3*slot_size; + mAutoQueue = 1; + } + mTotalSize = total_size; + mBroadcastLatency = bcast_qlen * mSlotSize; + mNumChannels = channels; + mAudioBitRes = bit_res; + mMinStepSize = channels * bit_res; + mFPP = buf_samples; + mSampleRate = sample_rate; + mActive = false; + + // Defaults for zero strategy + mUnderrunIncTolerance = -10 * mSlotSize; + mCorrIncTolerance = 100*mMaxLatency; // should be greater than mUnderrunIncTolerance + mOverflowDecTolerance = 100*mMaxLatency; + mWritePosition = mMaxLatency; + mStatUnit = mSlotSize; + mLevelDownRate = std::min(256, mFPP) / (5.0*sample_rate) * mSlotSize; + mOverflowDropStep = mMaxLatency / 2; + mLevelCur = mMaxLatency; + mLevel = mLevelCur; + mMinLevelThreshold = 1.9 * mSlotSize; + mBroadcastPosition = 0; + mBroadcastPositionCorr = 0.0; + mLastCorrCounter = 0; + mLastCorrDirection = 0; + + switch (strategy) { + case 1: + mOverflowDropStep = mSlotSize; + break; + case 2: + mUnderrunIncTolerance = 1.1 * mSlotSize; + mCorrIncTolerance = 1.9 * mSlotSize; // should be greater than mUnderrunIncTolerance + mOverflowDecTolerance = 0.1*mSlotSize; + mOverflowDropStep = mSlotSize; + break; + } + + mRingBuffer = new int8_t[mTotalSize]; + std::memset(mRingBuffer, 0, mTotalSize); + + mAutoQueueCorr = 2*mSlotSize; + if (0 > qlen) { + mAutoQFactor = 1.0/-qlen; + } + else { + mAutoQFactor = 1.0/500; + } + mAutoQRate = mSlotSize * 0.5; + mAutoQRateMin = mSlotSize * 0.0005; + mAutoQRateDecay = 1.0 - std::min(mFPP*1.2e-6, 0.0005); +} + +//******************************************************************************* +bool JitterBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen) +{ + if (0 == len) { + len = mSlotSize; + } + QMutexLocker locker(&mMutex); + mInSlotSize = len; + if (!mActive) { + mActive = true; + } + if (mMaxLatency < len + mSlotSize) { + mMaxLatency = len + mSlotSize; + } + if (0 < lostLen) { + processPacketLoss(lostLen); + } + mSkewRaw += mReadsNew - len; + mReadsNew = 0; + mUnderruns += mUnderrunsNew; + mUnderrunsNew = 0; + mLevel = mSlotSize*std::ceil(mLevelCur/mSlotSize); + + // Update positions if necessary + int32_t available = mWritePosition - mReadPosition; + + int delta = 0; + if (available < -10*mMaxLatency) { + delta = available; + mBufIncUnderrun += -delta; + mLevelCur = len; + //cout << "reset" << endl; + } + else if (available + len > mMaxLatency) { + delta = mOverflowDropStep; + mOverflows += delta; + mBufDecOverflow += delta; + mLevelCur = mMaxLatency; + } + else if (0 > available && + mLevelCur < std::max(mInSlotSize + mMinLevelThreshold, + mMaxLatency - mUnderrunIncTolerance - 2*mSlotSize*lastCorrFactor())) { + delta = -std::min(-available, mSlotSize); + mBufIncUnderrun += -delta; + } + else if (mLevelCur < mMaxLatency - mCorrIncTolerance - 6*mSlotSize*lastCorrFactor()) { + delta = -mSlotSize; + mUnderruns += -delta; + mBufIncCompensate += -delta; + } + + if (0 != delta) { + mReadPosition += delta; + mLastCorrCounter = 0; + mLastCorrDirection = 0 < delta ? 1 : -1; + } + else { + ++mLastCorrCounter; + } + + int wpos = mWritePosition % mTotalSize; + int n = std::min(mTotalSize - wpos, len); + std::memcpy(mRingBuffer+wpos, ptrToSlot, n); + if (n < len) { + //cout << "split write: " << len << "-" << n << endl; + std::memcpy(mRingBuffer, ptrToSlot+n, len-n); + } + mWritePosition += len; + + return true; +} + +//******************************************************************************* +void JitterBuffer::readSlotNonBlocking(int8_t* ptrToReadSlot) +{ + int len = mSlotSize; + QMutexLocker locker(&mMutex); + if (!mActive) { + std::memset(ptrToReadSlot, 0, len); + return; + } + mReadsNew += len; + int32_t available = mWritePosition - mReadPosition; + if (available < mLevelCur) { + mLevelCur = std::max((double)available, mLevelCur-mLevelDownRate); + } + else { + mLevelCur = available; + } + + // auto queue correction + if (0 > available + mAutoQueueCorr - mLevelCur) { + mAutoQueueCorr += mAutoQRate; + } + else if (mInSlotSize + mSlotSize < mAutoQueueCorr) { + mAutoQueueCorr -= mAutoQRate * mAutoQFactor; + } + if (mAutoQRate > mAutoQRateMin) { + mAutoQRate *= mAutoQRateDecay; + } + if (0 != mAutoQueue) { + int PPS = mSampleRate / mFPP; + if (2*PPS == mAutoQueue++ % (4*PPS)) { + double k = 1.0 + 1e-5/mAutoQFactor; + if (12*PPS > mAutoQueue || + std::abs(mAutoQueueCorr*k - mMaxLatency + mSlotSize/2) > 0.6*mSlotSize) { + mMaxLatency = mSlotSize * std::ceil(mAutoQueueCorr*k/mSlotSize); + cout << "AutoQueue: " << mMaxLatency / mSlotSize << endl; + } + } + } + + int read_len = qBound(0, available, len); + int rpos = mReadPosition % mTotalSize; + int n = std::min(mTotalSize - rpos, read_len); + std::memcpy(ptrToReadSlot, mRingBuffer+rpos, n); + if (n < read_len) { + //cout << "split read: " << read_len << "-" << n << endl; + std::memcpy(ptrToReadSlot+n, mRingBuffer, read_len-n); + } + if (read_len < len) { + std::memset(ptrToReadSlot+read_len, 0, len-read_len); + mUnderrunsNew += len-read_len; + } + mReadPosition += len; +} + +//******************************************************************************* +void JitterBuffer::readBroadcastSlot(int8_t* ptrToReadSlot) +{ + int len = mSlotSize; + QMutexLocker locker(&mMutex); + if (mBroadcastLatency + len > mReadPosition) { + std::memset(ptrToReadSlot, 0, len); + return; + } + // latency correction + int32_t d = mReadPosition - mBroadcastLatency - mBroadcastPosition - len; + if (std::abs(d) > mBroadcastLatency / 2) { + mBroadcastPosition = mReadPosition - mBroadcastLatency - len; + mBroadcastPositionCorr = 0.0; + mBroadcastSkew += d / mMinStepSize; + } + else { + mBroadcastPositionCorr += 0.0003 * d; + int delta = mBroadcastPositionCorr / mMinStepSize; + if (0 != delta) { + mBroadcastPositionCorr -= delta * mMinStepSize; + if (2 == mAudioBitRes && (int32_t)(mWritePosition - mBroadcastPosition) > len) { + // interpolate + len += delta * mMinStepSize; + } + else { + // skip + mBroadcastPosition += delta * mMinStepSize; + } + mBroadcastSkew += delta; + } + } + mBroadcastDelta = d / mMinStepSize; + int32_t available = mWritePosition - mBroadcastPosition; + int read_len = qBound(0, available, len); + if (len == mSlotSize) { + int rpos = mBroadcastPosition % mTotalSize; + int n = std::min(mTotalSize - rpos, read_len); + std::memcpy(ptrToReadSlot, mRingBuffer+rpos, n); + if (n < read_len) { + //cout << "split read: " << read_len << "-" << n << endl; + std::memcpy(ptrToReadSlot+n, mRingBuffer, read_len-n); + } + if (read_len < len) { + std::memset(ptrToReadSlot+read_len, 0, len-read_len); + } + } + else { + // interpolation len => mSlotSize + double K = 1.0 * len / mSlotSize; + for (int c=0; c < mMinStepSize; c+=sizeof(int16_t)) { + for (int j=0; j < mSlotSize/mMinStepSize; ++j) { + int j1 = std::floor(j*K); + double a = j*K - j1; + int rpos = (mBroadcastPosition + j1*mMinStepSize + c) % mTotalSize; + int16_t v1 = *(int16_t*)(mRingBuffer + rpos); + rpos = (rpos + mMinStepSize) % mTotalSize; + int16_t v2 = *(int16_t*)(mRingBuffer + rpos); + *(int16_t*)(ptrToReadSlot + j*mMinStepSize + c) = std::round((1-a)*v1 + a*v2); + } + } + } + mBroadcastPosition += len; +} + + +//******************************************************************************* +void JitterBuffer::processPacketLoss(int lostLen) +{ + mSkewRaw -= lostLen; + + int32_t available = mWritePosition - mReadPosition; + int delta = std::min(available + mInSlotSize + lostLen - mMaxLatency, lostLen); + if (0 < delta) { + lostLen -= delta; + mBufDecPktLoss += delta; + mLevelCur = mMaxLatency; + mLastCorrCounter = 0; + mLastCorrDirection = 1; + } + else if (mSlotSize < available + lostLen && ( + mOverflowDecTolerance > mMaxLatency // for strategies 0,1 + || (0 < mLastCorrDirection && mLevelCur > + mMaxLatency - mOverflowDecTolerance*(1.1 - lastCorrFactor())) + )) { + delta = std::min(lostLen, mSlotSize); + lostLen -= delta; + mBufDecPktLoss += delta; + mLevelCur -= delta; + mLastCorrCounter = 0; + mLastCorrDirection = 1; + } + if (lostLen >= mTotalSize) { + std::memset(mRingBuffer, 0, mTotalSize); + mUnderruns += std::max(0, lostLen - std::max(0, -available)); + } + else if (0 < lostLen) { + int wpos = mWritePosition % mTotalSize; + int n = std::min(mTotalSize - wpos, lostLen); + std::memset(mRingBuffer+wpos, 0, n); + if (n < lostLen) { + //cout << "split write: " << lostLen << "-" << n << endl; + std::memset(mRingBuffer, 0, lostLen-n); + } + mUnderruns += std::max(0, lostLen - std::max(0, -available)); + } + mWritePosition += lostLen; +} + +//******************************************************************************* +bool JitterBuffer::getStats(RingBuffer::IOStat* stat, bool reset) +{ + QMutexLocker locker(&mMutex); + if (reset) { + mUnderruns = 0; + mOverflows = 0; + mSkew0 = mLevel; + mSkewRaw = 0; + mBufDecOverflow = 0; + mBufDecPktLoss = 0; + mBufIncUnderrun = 0; + mBufIncCompensate = 0; + mBroadcastSkew = 0; + } + stat->underruns = mUnderruns / mStatUnit; + stat->overflows = mOverflows / mStatUnit; + stat->skew = (int32_t)((mSkew0 - mLevel + mBufIncUnderrun + mBufIncCompensate + - mBufDecOverflow - mBufDecPktLoss)) / mStatUnit; + stat->skew_raw = mSkewRaw / mStatUnit; + stat->level = mLevel / mStatUnit; + + stat->buf_dec_overflows = mBufDecOverflow / mStatUnit; + stat->buf_dec_pktloss = mBufDecPktLoss / mStatUnit; + stat->buf_inc_underrun = mBufIncUnderrun / mStatUnit; + stat->buf_inc_compensate = mBufIncCompensate / mStatUnit; + stat->broadcast_skew = mBroadcastSkew; + stat->broadcast_delta = mBroadcastDelta; + + stat->autoq_corr = mAutoQueueCorr / mStatUnit * 10; + stat->autoq_rate = mAutoQRate / mStatUnit * 1000; + return true; +} + diff --git a/src/JitterBuffer.h b/src/JitterBuffer.h new file mode 100644 index 0000000..6be4069 --- /dev/null +++ b/src/JitterBuffer.h @@ -0,0 +1,90 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2020 Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file JitterBuffer.h + * \author Anton Runov + * \date June 2020 + */ + +#ifndef __JITTERBUFFER_H__ +#define __JITTERBUFFER_H__ + +#include "RingBuffer.h" + +class JitterBuffer : public RingBuffer +{ +public: + JitterBuffer(int buf_samples, int qlen, int sample_rate, int strategy, + int bcast_qlen, int channels, int bit_res); + virtual ~JitterBuffer() {} + + virtual bool insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen); + virtual void readSlotNonBlocking(int8_t* ptrToReadSlot); + virtual void readBroadcastSlot(int8_t* ptrToReadSlot); + + virtual bool getStats(IOStat* stat, bool reset); + +protected: + void processPacketLoss(int lostLen); + +protected: + int mMaxLatency; + int mNumChannels; + int mAudioBitRes; + int mMinStepSize; + int mFPP; + int mSampleRate; + int mInSlotSize; + bool mActive; + uint32_t mBroadcastLatency; + uint32_t mBroadcastPosition; + double mBroadcastPositionCorr; + + double mUnderrunIncTolerance; + double mCorrIncTolerance; + double mOverflowDecTolerance; + int mOverflowDropStep; + uint32_t mLastCorrCounter; + int mLastCorrDirection; + double mMinLevelThreshold; + double lastCorrFactor() const {return 500.0 / std::max(500U, mLastCorrCounter);} + + int mAutoQueue; + double mAutoQueueCorr; + double mAutoQFactor; + double mAutoQRate; + double mAutoQRateMin; + double mAutoQRateDecay; +}; + + +#endif //__JITTERBUFFER_H__ diff --git a/src/Limiter.cpp b/src/Limiter.cpp new file mode 100644 index 0000000..91a918f --- /dev/null +++ b/src/Limiter.cpp @@ -0,0 +1,71 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file Limiter.cpp + * \author Julius Smith, based on LoopBack.h + * \date May-Nov 2020 + * \license MIT + */ + +#include "Limiter.h" +#include "jacktrip_types.h" + +#include <iostream> + +//******************************************************************************* +void Limiter::compute(int nframes, float** inputs, float** outputs) +{ + if (not inited) { + std::cerr << "*** Limiter " << this << ": init never called! Doing it now.\n"; + if (fSamplingFreq <= 0) { + fSamplingFreq = 48000; + std::cout << "Limiter " << this << ": *** HAD TO GUESS the sampling rate (chose 48000 Hz) ***\n"; + } + init(fSamplingFreq); + } +#ifdef SINE_TEST + float sineTestOut[nframes]; + float* faustSigs[1] { sineTestOut }; +#endif + for ( int i = 0; i < mNumChannels; i++ ) { + if (warningAmp > 0.0) { + checkAmplitudes(nframes, inputs[i]); // we presently do one check across all channels + } + limiterP[i]->compute(nframes, &inputs[i], &outputs[i]); +#ifdef SINE_TEST + limiterTestP[i]->compute(nframes, faustSigs, faustSigs); + for ( int n = 0; n < nframes; n++ ) { + outputs[i][n] = outputs[i][n] + sineTestOut[n]; + } +#endif + } +} diff --git a/src/Limiter.h b/src/Limiter.h new file mode 100644 index 0000000..6318d22 --- /dev/null +++ b/src/Limiter.h @@ -0,0 +1,179 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file Limiter.h + * \author Julius Smith, based on LoopBack.h + * \date May-Nov 2020 + * \license MIT + */ + +/** \brief Applies limiter_lad_mono from the faustlibraries distribution, compressors.lib + * + */ +#ifndef __LIMITER_H__ +#define __LIMITER_H__ + +//#define SINE_TEST + +#ifdef SINE_TEST +#include "limitertest.h" +#endif + +#include "ProcessPlugin.h" +#include "limiterdsp.h" +#include <vector> +#include "assert.h" + +/** \brief The Limiter class confines the output dynamic range to a + * "dynamic range lane" determined by the assumed number of clients. + */ +class Limiter : public ProcessPlugin +{ +public: + /// \brief The class constructor sets the number of channels to limit + Limiter(int numchans, int numclients, bool verboseFlag = false) // xtor + : mNumChannels(numchans), mNumClients(numclients) + , warningAmp(0.0), warnCount(0), peakMagnitude(0.0), nextWarning(1) + { + setVerbose(verboseFlag); + for ( int i = 0; i < mNumChannels; i++ ) { + limiterP.push_back(new limiterdsp); + limiterUIP.push_back(new APIUI); // #included in limiterdsp.h + limiterP[i]->buildUserInterface(limiterUIP[i]); +#ifdef SINE_TEST + limiterTestP.push_back(new limitertest); + limiterTestUIP.push_back(new APIUI); // #included in limitertest.h + limiterTestP[i]->buildUserInterface(limiterTestUIP[i]); +#endif + } + // std::cout << "Limiter: constructed for " + // << mNumChannels << " channels and " + // << mNumClients << " assumed clients\n"; + } + + /// \brief The class destructor + virtual ~Limiter() { + for ( int i = 0; i < mNumChannels; i++ ) { + delete limiterP[i]; + delete limiterUIP[i]; + } + limiterP.clear(); + limiterUIP.clear(); + } + + void init(int samplingRate) override { + ProcessPlugin::init(samplingRate); + if (samplingRate != fSamplingFreq) { + std::cerr << "Sampling rate not set by superclass!\n"; + std::exit(1); } + fs = float(fSamplingFreq); + for ( int i = 0; i < mNumChannels; i++ ) { + limiterP[i]->init(fs); // compression filter parameters depend on sampling rate + int ndx = limiterUIP[i]->getParamIndex("NumClientsAssumed"); + limiterUIP[i]->setParamValue(ndx, mNumClients); +#ifdef SINE_TEST + limiterTestP[i]->init(fs); // oscillator parameters depend on sampling rate + ndx = limiterTestUIP[i]->getParamIndex("Amp"); + limiterTestUIP[i]->setParamValue(ndx, 0.2); + ndx = limiterTestUIP[i]->getParamIndex("Freq"); + float sineFreq = 110.0 * pow(1.5,double(i)) * (mNumClients>1?1.25:1.0); // Maj 7 chord for stereo in & out + limiterTestUIP[i]->setParamValue(ndx, sineFreq); +#endif + } + inited = true; + } + int getNumInputs() override { return(mNumChannels); } + int getNumOutputs() override { return(mNumChannels); } + void compute(int nframes, float** inputs, float** outputs) override; + + void setWarningAmplitude(double wa) { // setting to 0 turns off warnings + warningAmp = std::max(0.0,std::min(1.0,wa)); + } + + private: + + void checkAmplitudes(int nframes, float* buf) { + const int maxWarningInterval { 10000 }; // this could become an option + assert(warningAmp > 0.0); + assert(mNumClients > 0); + for (int i=0; i<nframes; i++) { + double tmp_sample = double(buf[i]); + double limiterAmp = fabs(tmp_sample)/sqrt(double(mNumClients)); // KEEP IN SYNC with gain in ../faust-src/limiterdsp.dsp + if (limiterAmp >= warningAmp) { + warnCount++; + peakMagnitude = std::max(peakMagnitude,limiterAmp); + if (warnCount==nextWarning) { + double peakMagnitudeDB = 20.0 * std::log10(peakMagnitude); + double warningAmpDB = 20.0 * std::log10(warningAmp); + if (warnCount==1) { + if (warningAmp == 1.0) { + std::cerr << "*** Limiter.cpp: Audio HARD-CLIPPED!\n"; + fprintf(stderr, "\tReduce your audio input level(s) by %0.1f dB to avoid this.\n", peakMagnitudeDB); + } else { + fprintf(stderr, + "*** Limiter.cpp: Amplitude levels must stay below %0.1f dBFS to avoid compression.\n", + warningAmpDB); + fprintf(stderr, "\tReduce input level(s) by %0.1f dB to achieve this.\n", + peakMagnitudeDB-warningAmpDB); + } + } else { + fprintf(stderr, "\tReduce audio input level(s) by %0.1f dB to avoid limiter compression distortion.\n", + peakMagnitudeDB-warningAmpDB); + } + peakMagnitude = 0.0; // reset for next group measurement + if (nextWarning < maxWarningInterval) { // don't let it stop reporting for too long + nextWarning *= 10; + } else { + warnCount=0; + } + } // warnCount==nextWarning + } // above warningAmp + } // loop over frames + } // checkAmplitudes() + +private: + float fs; + int mNumChannels; + int mNumClients; + std::vector<limiterdsp*> limiterP; + std::vector<APIUI*> limiterUIP; +#ifdef SINE_TEST + std::vector<limitertest*> limiterTestP; + std::vector<APIUI*> limiterTestUIP; +#endif + double warningAmp; + uint32_t warnCount; + double peakMagnitude; + uint32_t nextWarning; +}; + +#endif diff --git a/src/PacketHeader.cpp b/src/PacketHeader.cpp index 56ac606..5511f94 100644 --- a/src/PacketHeader.cpp +++ b/src/PacketHeader.cpp @@ -133,11 +133,11 @@ void DefaultHeader::checkPeerSettings(int8_t* full_packet) // Check Buffer Size if ( peer_header->BufferSize != mHeader.BufferSize ) { - std::cerr << "ERROR: Peer Buffer Size is : " << peer_header->BufferSize << endl; - std::cerr << " Local Buffer Size is : " << mHeader.BufferSize << endl; - std::cerr << "Make sure both machines use same buffer size" << endl; - std::cerr << gPrintSeparator << endl; - error = true; + std::cerr << "WARNING: Peer Buffer Size is : " << peer_header->BufferSize << endl; + std::cerr << " Local Buffer Size is : " << mHeader.BufferSize << endl; + //std::cerr << "Make sure both machines use same buffer size" << endl; + //std::cerr << gPrintSeparator << endl; + //error = true; } // Check Sampling Rate diff --git a/src/PacketHeader.h b/src/PacketHeader.h index 094d424..80f38b8 100644 --- a/src/PacketHeader.h +++ b/src/PacketHeader.h @@ -165,7 +165,7 @@ public: signals: - void signalError(const char* error_message); + void signalError(const QString &error_message); private: diff --git a/src/ProcessPlugin.h b/src/ProcessPlugin.h index e0b23f3..fb48cca 100644 --- a/src/ProcessPlugin.h +++ b/src/ProcessPlugin.h @@ -65,17 +65,33 @@ public: //virtual void buildUserInterface(UI* interface) = 0; + virtual char* getName() { + char* pluginName { const_cast<char*>(typeid(*this).name()) }; // get name of DERIVED class + while (isdigit(*pluginName)) { pluginName++; } + return pluginName; + } + /** \brief Do proper Initialization of members and class instances. By default this * initializes the Sampling Frequency. If a class instance depends on the * sampling frequency, it should be initialize here. */ - virtual void init(int samplingRate) { fSamplingFreq = samplingRate; }; + virtual void init(int samplingRate) { + fSamplingFreq = samplingRate; + if (verbose) { + char* derivedClassName = getName(); + printf("%s: init(%d)\n",derivedClassName,samplingRate); + } + } + virtual bool getInited() { return inited; } + virtual void setVerbose(bool v) { verbose = v; } /// \brief Compute process virtual void compute(int nframes, float** inputs, float** outputs) = 0; protected: int fSamplingFreq; ///< Faust Data member, Sampling Rate + bool inited = false; + bool verbose = false; }; #endif diff --git a/src/Reverb.cpp b/src/Reverb.cpp new file mode 100644 index 0000000..abc2c5f --- /dev/null +++ b/src/Reverb.cpp @@ -0,0 +1,70 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file Reverb.cpp + * \author Julius Smith, based on Limiter.h + * \date August 2020 + */ + + +#include "Reverb.h" +#include "jacktrip_types.h" + +#include <iostream> + +//******************************************************************************* +void Reverb::compute(int nframes, float** inputs, float** outputs) +{ + if (not inited) { + std::cerr << "*** Reverb " << this << ": init never called! Doing it now.\n"; + if (fSamplingFreq <= 0) { + fSamplingFreq = 48000; + std::cout << "Reverb " << this << ": *** HAD TO GUESS the sampling rate (chose 48000 Hz) ***\n"; + } + init(fSamplingFreq); + } + if (mReverbLevel <= 1.0) { + if (mNumInChannels == 1) { + freeverbMonoP->compute(nframes, inputs, outputs); + } else { + assert(mNumInChannels == 2); + freeverbStereoP->compute(nframes, inputs, outputs); + } + } else { + if (mNumInChannels == 1) { + zitarevMonoP->compute(nframes, inputs, outputs); + } else { + assert(mNumInChannels == 2); + zitarevStereoP->compute(nframes, inputs, outputs); + } + } +} diff --git a/src/Reverb.h b/src/Reverb.h new file mode 100644 index 0000000..3a7fd84 --- /dev/null +++ b/src/Reverb.h @@ -0,0 +1,158 @@ +//***************************************************************** +/* + JackTrip: A System for High-Quality Audio Network Performance + over the Internet + + Copyright (c) 2020 Julius Smith, Juan-Pablo Caceres, Chris Chafe. + SoundWIRE group at CCRMA, Stanford University. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ +//***************************************************************** + +/** + * \file Reverb.h + * \author Julius Smith, based on Limiter.h + * \date August 2020 + */ + + +/** \brief Applies freeverb or zitarev from the faustlibraries distribution: reverbs.lib + * + */ +#ifndef __REVERB_H__ +#define __REVERB_H__ + +//#define SINE_TEST + +#include "ProcessPlugin.h" +#include "freeverbdsp.h" // stereo in and out +#include "freeverbmonodsp.h" // mono in and out (there is no mono to stereo case in jacktrip as yet) +#include "zitarevdsp.h" // stereo in and out +#include "zitarevmonodsp.h" // mono in and out + +/** \brief A Reverb is an echo-based delay effect, + * providing a virtual acoustic listening space. + */ +class Reverb : public ProcessPlugin +{ +public: + /// \brief The class constructor sets the number of channels to limit + Reverb(int numInChans, int numOutChans, float reverbLevel = 1.0, bool verboseFlag = false) // xtor + : mNumInChannels(numInChans), mNumOutChannels(numOutChans), mReverbLevel(reverbLevel) + { + setVerbose(verboseFlag); + if ( mNumInChannels < 1 ) { + std::cerr << "*** Reverb.h: must have at least one input audio channels\n"; + mNumInChannels = 1; + } + if ( mNumInChannels > 2 ) { + std::cerr << "*** Reverb.h: limiting number of audio output channels to 2\n"; + mNumInChannels = 2; + } +#if 0 + std::cout << "Reverb: constructed for " + << mNumInChannels << " input channels and " + << mNumOutChannels << " output channels with reverb level = " + << mReverbLevel << "\n"; +#endif + + if (mReverbLevel <= 1.0) { // freeverb: + freeverbStereoP = new freeverbdsp; // stereo input and output + freeverbMonoP = new freeverbmonodsp; // mono input, stereo output + freeverbStereoUIP = new APIUI; // #included in *dsp.h + freeverbMonoUIP = new APIUI; + freeverbStereoP->buildUserInterface(freeverbStereoUIP); + freeverbMonoP->buildUserInterface(freeverbMonoUIP); + // std::cout << "Using freeverb\n"; + } else { + zitarevStereoP = new zitarevdsp; // stereo input and output + zitarevMonoP = new zitarevmonodsp; // mono input, stereo output + zitarevStereoUIP = new APIUI; + zitarevMonoUIP = new APIUI; + zitarevStereoP->buildUserInterface(zitarevStereoUIP); + zitarevMonoP->buildUserInterface(zitarevMonoUIP); + // std::cout << "Using zitarev\n"; + } + } + + /// \brief The class destructor + virtual ~Reverb() { + if (mReverbLevel <= 1.0) { // freeverb: + delete freeverbStereoP; + delete freeverbMonoP; + delete freeverbStereoUIP; + delete freeverbMonoUIP; + } else { + delete zitarevStereoP; + delete zitarevMonoP; + delete zitarevStereoUIP; + delete zitarevMonoUIP; + } + } + + void init(int samplingRate) override { + ProcessPlugin::init(samplingRate); + // std::cout << "Reverb: init(" << samplingRate << ")\n"; + if (samplingRate != fSamplingFreq) { + std::cerr << "Sampling rate not set by superclass!\n"; + std::exit(1); } + fs = float(fSamplingFreq); + if (mReverbLevel <= 1.0) { // freeverb: + freeverbStereoP->init(fs); // compression filter parameters depend on sampling rate + freeverbMonoP->init(fs); // compression filter parameters depend on sampling rate + int ndx = freeverbStereoUIP->getParamIndex("Wet"); + freeverbStereoUIP->setParamValue(ndx, mReverbLevel); + freeverbMonoUIP->setParamValue(ndx, mReverbLevel); + } else { // zitarev: + zitarevStereoP->init(fs); // compression filter parameters depend on sampling rate + zitarevMonoP->init(fs); // compression filter parameters depend on sampling rate + int ndx = zitarevStereoUIP->getParamIndex("Wet"); + float zitaLevel = mReverbLevel-1.0f; // range within zitarev is 0 to 1 (our version only) + zitarevStereoUIP->setParamValue(ndx, zitaLevel); + zitarevMonoUIP->setParamValue(ndx, zitaLevel); + } + inited = true; + } + int getNumInputs() override { return(mNumInChannels); } + int getNumOutputs() override { return(mNumOutChannels); } + void compute(int nframes, float** inputs, float** outputs) override; + +private: + float fs; + int mNumInChannels; + int mNumOutChannels; + + float mReverbLevel; + + freeverbdsp* freeverbStereoP; + freeverbmonodsp* freeverbMonoP; + APIUI* freeverbStereoUIP; + APIUI* freeverbMonoUIP; + + zitarevdsp* zitarevStereoP; + zitarevmonodsp* zitarevMonoP; + APIUI* zitarevStereoUIP; + APIUI* zitarevMonoUIP; +}; + +#endif diff --git a/src/RingBuffer.cpp b/src/RingBuffer.cpp index 124399e..e9ff28c 100644 --- a/src/RingBuffer.cpp +++ b/src/RingBuffer.cpp @@ -42,6 +42,8 @@ #include <cstring> #include <cstdlib> #include <stdexcept> +#include <cmath> +#include "JackTrip.h" using std::cout; using std::endl; @@ -54,40 +56,55 @@ RingBuffer::RingBuffer(int SlotSize, int NumSlots) : mReadPosition(0), mWritePosition(0), mFullSlots(0), - mRingBuffer(new int8_t[mTotalSize]), - mLastReadSlot(new int8_t[mSlotSize]) + mRingBuffer(NULL), + mLastReadSlot(NULL) { - //QMutexLocker locker(&mMutex); // lock the mutex - - // Verify if there's enough space to for the buffers - if ( (mRingBuffer == NULL) || (mLastReadSlot == NULL) ) { - //std::cerr << "ERROR: RingBuffer out of memory!" << endl; - //std::cerr << "Exiting program..." << endl; - //std::exit(1); - throw std::length_error("RingBuffer out of memory!"); + if (0 < mTotalSize) { + mRingBuffer = new int8_t[mTotalSize]; + mLastReadSlot = new int8_t[mSlotSize]; + //QMutexLocker locker(&mMutex); // lock the mutex + + // Verify if there's enough space to for the buffers + if ( (mRingBuffer == NULL) || (mLastReadSlot == NULL) ) { + //std::cerr << "ERROR: RingBuffer out of memory!" << endl; + //std::cerr << "Exiting program..." << endl; + //std::exit(1); + throw std::length_error("RingBuffer out of memory!"); + } + + // Set the buffers to zeros + /* + for (int i=0; i<mTotalSize; i++) { + mRingBuffer[i] = 0; // Initialize all elements to zero. + } + */ + std::memset(mRingBuffer, 0, mTotalSize); // set buffer to 0 + /* + for (int i=0; i<mSlotSize; i++) { + mLastReadSlot[i] = 0; // Initialize all elements to zero. + } + */ + std::memset(mLastReadSlot, 0, mSlotSize); // set buffer to 0 + mWritePosition = ( (NumSlots/2) * SlotSize ) % mTotalSize; } - // Set the buffers to zeros - /* - for (int i=0; i<mTotalSize; i++) { - mRingBuffer[i] = 0; // Initialize all elements to zero. - } - */ - std::memset(mRingBuffer, 0, mTotalSize); // set buffer to 0 - /* - for (int i=0; i<mSlotSize; i++) { - mLastReadSlot[i] = 0; // Initialize all elements to zero. - } - */ - std::memset(mLastReadSlot, 0, mSlotSize); // set buffer to 0 - - // Advance write position to half of the RingBuffer - mWritePosition = ( (NumSlots/2) * SlotSize ) % mTotalSize; // Udpate Full Slots accordingly mFullSlots = (NumSlots/2); + mLevelDownRate = 0.01; + mStatUnit = 1; mUnderruns = 0; mOverflows = 0; + mSkew0 = 0; + mSkewRaw = 0; + mLevelCur = mFullSlots; + mLevel = mLevelCur; + mBufDecOverflow = 0; + mBufDecPktLoss = 0; + mBufIncUnderrun = 0; + mBufIncCompensate = 0; + mBroadcastSkew = 0; + mBroadcastDelta = 0; } @@ -105,6 +122,7 @@ RingBuffer::~RingBuffer() void RingBuffer::insertSlotBlocking(const int8_t* ptrToSlot) { QMutexLocker locker(&mMutex); // lock the mutex + updateReadStats(); // Check if there is space available to write a slot // If the Ringbuffer is full, it waits for the bufferIsNotFull condition @@ -127,12 +145,16 @@ void RingBuffer::insertSlotBlocking(const int8_t* ptrToSlot) void RingBuffer::readSlotBlocking(int8_t* ptrToReadSlot) { QMutexLocker locker(&mMutex); // lock the mutex + ++mReadsNew; // Check if there are slots available to read // If the Ringbuffer is empty, it waits for the bufferIsNotEmpty condition while (mFullSlots == 0) { //std::cerr << "READ UNDER-RUN BLOCKING before" << endl; - mBufferIsNotEmpty.wait(&mMutex); + mBufferIsNotEmpty.wait(&mMutex, 200); + if (JackTrip::sJackStopped) { + return; + } } // Copy mSlotSize bytes to ReadSlot @@ -148,9 +170,20 @@ void RingBuffer::readSlotBlocking(int8_t* ptrToReadSlot) //******************************************************************************* -void RingBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot) +bool RingBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen) { + if (len != mSlotSize && 0 != len) { + // RingBuffer does not suppport mixed buf sizes + return false; + } QMutexLocker locker(&mMutex); // lock the mutex + if (0 < lostLen) { + int lostCount = lostLen / mSlotSize; + mBufDecPktLoss += lostCount; + mSkewRaw -= lostCount; + mLevelCur -= lostCount; + } + updateReadStats(); // Check if there is space available to write a slot // If the Ringbuffer is full, it returns without writing anything @@ -160,7 +193,7 @@ void RingBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot) if (mFullSlots == mNumSlots) { //std::cout << "OUPUT OVERFLOW NON BLOCKING = " << mNumSlots << std::endl; overflowReset(); - return; + return true; } // Copy mSlotSize bytes to mRingBuffer @@ -170,6 +203,7 @@ void RingBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot) mFullSlots++; //update full slots // Wake threads waitng for bufferIsNotFull condition mBufferIsNotEmpty.wakeAll(); + return true; } @@ -177,11 +211,17 @@ void RingBuffer::insertSlotNonBlocking(const int8_t* ptrToSlot) void RingBuffer::readSlotNonBlocking(int8_t* ptrToReadSlot) { QMutexLocker locker(&mMutex); // lock the mutex - + ++mReadsNew; + if (mFullSlots < mLevelCur) { + mLevelCur = std::max((double)mFullSlots, mLevelCur-mLevelDownRate); + } + else { + mLevelCur = mFullSlots; + } // Check if there are slots available to read // If the Ringbuffer is empty, it returns a buffer of zeros and rests the buffer - if (mFullSlots == 0) { + if (mFullSlots <= 0) { // Returns a buffer of zeros if there's nothing to read //std::cerr << "READ UNDER-RUN NON BLOCKING = " << mNumSlots << endl; //std::memset(ptrToReadSlot, 0, mSlotSize); @@ -203,6 +243,14 @@ void RingBuffer::readSlotNonBlocking(int8_t* ptrToReadSlot) //******************************************************************************* +// Not supported in RingBuffer +void RingBuffer::readBroadcastSlot(int8_t* ptrToReadSlot) +{ + std::memset(ptrToReadSlot, 0, mSlotSize); +} + + +//******************************************************************************* void RingBuffer::setUnderrunReadSlot(int8_t* ptrToReadSlot) { std::memset(ptrToReadSlot, 0, mSlotSize); @@ -228,7 +276,7 @@ void RingBuffer::underrunReset() //mFullSlots += mNumSlots/2; // There's nothing new to read, so we clear the whole buffer (Set the entire buffer to 0) std::memset(mRingBuffer, 0, mTotalSize); - ++mUnderruns; + ++mUnderrunsNew; } @@ -238,9 +286,12 @@ void RingBuffer::overflowReset() { // Advance the read pointer 1/2 the ring buffer //mReadPosition = ( mWritePosition + ( (mNumSlots/2) * mSlotSize ) ) % mTotalSize; - mReadPosition = ( mReadPosition + ( (mNumSlots/2) * mSlotSize ) ) % mTotalSize; - mFullSlots -= mNumSlots/2; - mOverflows += mNumSlots/2 + 1; + int d = mNumSlots / 2; + mReadPosition = ( mReadPosition + ( d * mSlotSize ) ) % mTotalSize; + mFullSlots -= d; + mOverflows += d + 1; + mBufDecOverflow += d + 1; + mLevelCur -= d; } @@ -256,11 +307,45 @@ void RingBuffer::debugDump() const //******************************************************************************* bool RingBuffer::getStats(RingBuffer::IOStat* stat, bool reset) { + QMutexLocker locker(&mMutex); if (reset) { mUnderruns = 0; mOverflows = 0; + mSkew0 = mLevel; + mSkewRaw = 0; + mBufDecOverflow = 0; + mBufDecPktLoss = 0; + mBufIncUnderrun = 0; + mBufIncCompensate = 0; + mBroadcastSkew = 0; } - stat->underruns = mUnderruns; - stat->overflows = mOverflows; + stat->underruns = mUnderruns / mStatUnit; + stat->overflows = mOverflows / mStatUnit; + stat->skew = (int32_t)((mSkew0 - mLevel + mBufIncUnderrun + mBufIncCompensate + - mBufDecOverflow - mBufDecPktLoss)) / mStatUnit; + stat->skew_raw = mSkewRaw / mStatUnit; + stat->level = mLevel / mStatUnit; + + stat->buf_dec_overflows = mBufDecOverflow / mStatUnit; + stat->buf_dec_pktloss = mBufDecPktLoss / mStatUnit; + stat->buf_inc_underrun = mBufIncUnderrun / mStatUnit; + stat->buf_inc_compensate = mBufIncCompensate / mStatUnit; + stat->broadcast_skew = mBroadcastSkew; + stat->broadcast_delta = mBroadcastDelta; + + stat->autoq_corr = 0; + stat->autoq_rate = 0; return true; } + +//******************************************************************************* +void RingBuffer::updateReadStats() +{ + --mSkewRaw; + mSkewRaw += mReadsNew; + mReadsNew = 0; + mUnderruns += mUnderrunsNew; + mBufIncUnderrun += mUnderrunsNew; + mUnderrunsNew = 0; + mLevel = std::ceil(mLevelCur); +} diff --git a/src/RingBuffer.h b/src/RingBuffer.h index 78e137f..e68d625 100644 --- a/src/RingBuffer.h +++ b/src/RingBuffer.h @@ -95,16 +95,29 @@ public: /** \brief Same as insertSlotBlocking but non-blocking (asynchronous) * \param ptrToSlot Pointer to slot to insert into the RingBuffer */ - void insertSlotNonBlocking(const int8_t* ptrToSlot); + virtual bool insertSlotNonBlocking(const int8_t* ptrToSlot, int len, int lostLen); /** \brief Same as readSlotBlocking but non-blocking (asynchronous) * \param ptrToReadSlot Pointer to read slot from the RingBuffer */ - void readSlotNonBlocking(int8_t* ptrToReadSlot); + virtual void readSlotNonBlocking(int8_t* ptrToReadSlot); + virtual void readBroadcastSlot(int8_t* ptrToReadSlot); struct IOStat { uint32_t underruns; uint32_t overflows; + int32_t skew; + int32_t skew_raw; + int32_t level; + uint32_t buf_dec_overflows; + uint32_t buf_dec_pktloss; + uint32_t buf_inc_underrun; + uint32_t buf_inc_compensate; + int32_t broadcast_skew; + int32_t broadcast_delta; + + int32_t autoq_corr; + int32_t autoq_rate; }; virtual bool getStats(IOStat* stat, bool reset); @@ -124,20 +137,19 @@ protected: */ virtual void setMemoryInReadSlotWithLastReadSlot(int8_t* ptrToReadSlot); -private: - /// \brief Resets the ring buffer for reads under-runs non-blocking void underrunReset(); /// \brief Resets the ring buffer for writes over-flows non-blocking void overflowReset(); /// \brief Helper method to debug, prints member variables to terminal void debugDump() const; + void updateReadStats(); - const int mSlotSize; ///< The size of one slot in byes - const int mNumSlots; ///< Number of Slots - const int mTotalSize; ///< Total size of the mRingBuffer = mSlotSize*mNumSlotss - int mReadPosition; ///< Read Positions in the RingBuffer (Tail) - int mWritePosition; ///< Write Position in the RingBuffer (Head) + /*const*/ int mSlotSize; ///< The size of one slot in byes + /*const*/ int mNumSlots; ///< Number of Slots + /*const*/ int mTotalSize; ///< Total size of the mRingBuffer = mSlotSize*mNumSlotss + uint32_t mReadPosition; ///< Read Positions in the RingBuffer (Tail) + uint32_t mWritePosition; ///< Write Position in the RingBuffer (Head) int mFullSlots; ///< Number of used (full) slots, in slot-size int8_t* mRingBuffer; ///< 8-bit array of data (1-byte) int8_t* mLastReadSlot; ///< Last slot read @@ -146,8 +158,29 @@ private: QMutex mMutex; ///< Mutex to protect read and write operations QWaitCondition mBufferIsNotFull; ///< Buffer not full condition to monitor threads QWaitCondition mBufferIsNotEmpty; ///< Buffer not empty condition to monitor threads - std::atomic<uint32_t> mUnderruns; - std::atomic<uint32_t> mOverflows; + + // IO stat + int mStatUnit; + uint32_t mUnderruns; + uint32_t mOverflows; + int32_t mSkewRaw; + double mLevelCur; + double mLevelDownRate; + int32_t mLevel; + + uint32_t mBufDecOverflow; + uint32_t mBufDecPktLoss; + uint32_t mBufIncUnderrun; + uint32_t mBufIncCompensate; + + // temp counters for reads + uint32_t mReadsNew; + uint32_t mUnderrunsNew; + int32_t mSkew0; + + // broadcast counters + int32_t mBroadcastSkew; + int32_t mBroadcastDelta; }; #endif diff --git a/src/Settings.cpp b/src/Settings.cpp index bbc386b..4d08021 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -37,40 +37,49 @@ #include "Settings.h" #include "LoopBack.h" -#include "NetKS.h" +//#include "NetKS.h" +#include "Effects.h" #ifdef WAIR // wair #include "ap8x2.dsp.h" #include "Stk16.dsp.h" #endif // endwhere -#include "UdpHubListener.h" -#include "JackTripWorker.h" +//#include "JackTripWorker.h" #include "jacktrip_globals.h" #include <iostream> #include <getopt.h> // for command line parsing #include <cstdlib> +#include <assert.h> +#include <ctype.h> -#include "ThreadPoolTest.h" +//#include "ThreadPoolTest.h" using std::cout; using std::endl; int gVerboseFlag = 0; +enum JTLongOptIDS { + OPT_BUFSTRATEGY = 1001, + OPT_SIMLOSS, + OPT_SIMJITTER, + OPT_BROADCAST, + OPT_RTUDPPRIORITY, +}; //******************************************************************************* Settings::Settings() : - mJackTrip(NULL), mJackTripMode(JackTrip::SERVER), mDataProtocol(JackTrip::UDP), mNumChans(2), mBufferQueueLength(gDefaultQueueLength), mAudioBitResolution(AudioInterface::BIT16), mBindPortNum(gDefaultPort), mPeerPortNum(gDefaultPort), - mServerUdpPortNum(NULL), - mClientName(NULL), - mUnderrrunZero(false), + mServerUdpPortNum(0), + mUnderrunMode(JackTrip::WAVETABLE), + mStopOnTimeout(false), + mBufferStrategy(1), mLoopBack(false), #ifdef WAIR // WAIR mNumNetRevChans(0), @@ -87,19 +96,23 @@ Settings::Settings() : mChanfeDefaultBS(false), mHubConnectionMode(JackTrip::SERVERTOCLIENT), mConnectDefaultAudioPorts(true), - mIOStatTimeout(0) + mIOStatTimeout(0), + mEffects(false), // outgoing limiter OFF by default + mSimulatedLossRate(0.0), + mSimulatedJitterRate(0.0), + mSimulatedDelayRel(0.0), + mBroadcastQueue(0), + mUseRtUdpPriority(false) {} //******************************************************************************* -Settings::~Settings() -{ - stopJackTrip(); - delete mJackTrip; -} +Settings::~Settings() = default; //******************************************************************************* void Settings::parseInput(int argc, char** argv) { + // Always use decimal point for floating point numbers + setlocale( LC_NUMERIC, "C" ); // If no command arguments are given, print instructions if(argc == 1) { printUsage(); @@ -112,49 +125,60 @@ void Settings::parseInput(int argc, char** argv) //---------------------------------------------------------------------------- static struct option longopts[] = { // These options don't set a flag. - { "numchannels", required_argument, NULL, 'n' }, // Number of input and output channels + { "numchannels", required_argument, NULL, 'n' }, // Number of input and output channels #ifdef WAIR // WAIR - { "wair", no_argument, NULL, 'w' }, // Run in LAIR mode, sets numnetrevchannels - { "addcombfilterlength", required_argument, NULL, 'N' }, // added comb filter length - { "combfilterfeedback", required_argument, NULL, 'H' }, // comb filter feedback + { "wair", no_argument, NULL, 'w' }, // Run in LAIR mode, sets numnetrevchannels + { "addcombfilterlength", required_argument, NULL, 'N' }, // added comb filter length + { "combfilterfeedback", required_argument, NULL, 'H' }, // comb filter feedback #endif // endwhere - { "server", no_argument, NULL, 's' }, // Run in server mode - { "client", required_argument, NULL, 'c' }, // Run in client mode, set server IP address - { "localaddress", required_argument, NULL, 'L' }, // set local address e.g., 127.0.0.2 for second instance on same host - { "jacktripserver", no_argument, NULL, 'S' }, // Run in JamLink mode - { "pingtoserver", required_argument, NULL, 'C' }, // Run in ping to server mode, set server IP address - { "portoffset", required_argument, NULL, 'o' }, // Port Offset from 4464 - { "bindport", required_argument, NULL, 'B' }, // Port Offset from 4464 - { "peerport", required_argument, NULL, 'P' }, // Port Offset from 4464 - { "udpbaseport", required_argument, NULL, 'U' }, // Server udp base port (defaults to 61002) - { "queue", required_argument, NULL, 'q' }, // Queue Length - { "redundancy", required_argument, NULL, 'r' }, // Redundancy - { "bitres", required_argument, NULL, 'b' }, // Audio Bit Resolution - { "zerounderrun", no_argument, NULL, 'z' }, // Use Underrun to Zeros Mode - { "loopback", no_argument, NULL, 'l' }, // Run in loopback mode - { "jamlink", no_argument, NULL, 'j' }, // Run in JamLink mode - { "emptyheader", no_argument, NULL, 'e' }, // Run in JamLink mode - { "clientname", required_argument, NULL, 'J' }, // Run in JamLink mode - { "rtaudio", no_argument, NULL, 'R' }, // Run in JamLink mode - { "srate", required_argument, NULL, 'T' }, // Set Sample Rate - { "deviceid", required_argument, NULL, 'd' }, // Set RTAudio device id to use - { "bufsize", required_argument, NULL, 'F' }, // Set buffer Size - { "nojackportsconnect" , no_argument, NULL, 'D'}, // Don't connect default Audio Ports - { "version", no_argument, NULL, 'v' }, // Version Number - { "verbose", no_argument, NULL, 'V' }, // Verbose mode - { "hubpatch", required_argument, NULL, 'p' }, // Set hubConnectionMode for auto patch in Jack - { "iostat", required_argument, NULL, 'I' }, // Set IO stat timeout - { "iostatlog", required_argument, NULL, 'G' }, // Set IO stat log file - { "help", no_argument, NULL, 'h' }, // Print Help - { NULL, 0, NULL, 0 } -}; + { "server", no_argument, NULL, 's' }, // Run in P2P server mode + { "client", required_argument, NULL, 'c' }, // Run in P2P client mode, set server IP address + { "localaddress", required_argument, NULL, 'L' }, // set local address e.g., 127.0.0.2 for second instance on same host + { "jacktripserver", no_argument, NULL, 'S' }, // Run in JamLink mode + { "pingtoserver", required_argument, NULL, 'C' }, // Run in ping to server mode, set server IP address + { "portoffset", required_argument, NULL, 'o' }, // Port Offset from 4464 + { "bindport", required_argument, NULL, 'B' }, // Port Offset from 4464 + { "peerport", required_argument, NULL, 'P' }, // Port Offset from 4464 + { "udpbaseport", required_argument, NULL, 'U' }, // Server udp base port (defaults to 61002) + { "queue", required_argument, NULL, 'q' }, // Queue Length + { "redundancy", required_argument, NULL, 'r' }, // Redundancy + { "bitres", required_argument, NULL, 'b' }, // Audio Bit Resolution + { "zerounderrun", no_argument, NULL, 'z' }, // Use Underrun to Zeros Mode + { "timeout", no_argument, NULL, 't' }, // Quit after 10 second network timeout + { "loopback", no_argument, NULL, 'l' }, // Run in loopback mode + { "jamlink", no_argument, NULL, 'j' }, // Run in JamLink mode + { "emptyheader", no_argument, NULL, 'e' }, // Run in JamLink mode + { "clientname", required_argument, NULL, 'J' }, // Run in JamLink mode + { "remotename", required_argument, NULL, 'K' }, // Client name on hub server + { "rtaudio", no_argument, NULL, 'R' }, // Run in JamLink mode + { "srate", required_argument, NULL, 'T' }, // Set Sample Rate + { "deviceid", required_argument, NULL, 'd' }, // Set RTAudio device id to use + { "bufsize", required_argument, NULL, 'F' }, // Set buffer Size + { "nojackportsconnect" , no_argument, NULL, 'D'}, // Don't connect default Audio Ports + { "version", no_argument, NULL, 'v' }, // Version Number + { "verbose", no_argument, NULL, 'V' }, // Verbose mode + { "hubpatch", required_argument, NULL, 'p' }, // Set hubConnectionMode for auto patch in Jack + { "iostat", required_argument, NULL, 'I' }, // Set IO stat timeout + { "iostatlog", required_argument, NULL, 'G' }, // Set IO stat log file + { "effects", required_argument, NULL, 'f' }, // Turn on outgoing compressor and incoming reverb, reverbLevel arg + { "overflowlimiting", required_argument, NULL, 'O' }, // Turn On limiter, cases 'i', 'o', 'io' + { "assumednumclients", required_argument, NULL, 'a' }, // assumed number of clients (sound sources) (otherwise 2) + { "bufstrategy", required_argument, NULL, OPT_BUFSTRATEGY }, // Set bufstrategy + { "simloss", required_argument, NULL, OPT_SIMLOSS }, + { "simjitter", required_argument, NULL, OPT_SIMJITTER }, + { "broadcast", required_argument, NULL, OPT_BROADCAST }, + { "udprt", no_argument, NULL, OPT_RTUDPPRIORITY }, + { "help", no_argument, NULL, 'h' }, // Print Help + { "examine-audio-delay", required_argument, NULL, 'x' }, // test mode - measure audio round-trip latency statistics + { NULL, 0, NULL, 0 } + }; // Parse Command Line Arguments //---------------------------------------------------------------------------- /// \todo Specify mandatory arguments int ch; - while ( (ch = getopt_long(argc, argv, - "n:N:H:sc:SC:o:B:P:U:q:r:b:zlwjeJ:RTd:F:p:DvVh", longopts, NULL)) != -1 ) + while ((ch = getopt_long(argc, argv, + "n:N:H:sc:SC:o:B:P:U:q:r:b:ztlwjeJ:K:RTd:F:p:DvVhI:G:f:O:a:x:", longopts, NULL)) != -1) switch (ch) { case 'n': // Number of input and output channels @@ -179,15 +203,15 @@ void Settings::parseInput(int argc, char** argv) mClientRoomSize = atof(optarg); // cmd line comb feedback adjustment break; #endif // endwhere - case 's': // Run in server mode + case 's': // Run in P2P server mode //------------------------------------------------------- mJackTripMode = JackTrip::SERVER; break; - case 'S': // Run in jacktripserver mode + case 'S': // Run in Hub server mode //------------------------------------------------------- mJackTripServer = true; break; - case 'c': // Client mode + case 'c': // P2P client mode //------------------------------------------------------- mJackTripMode = JackTrip::CLIENT; mPeerAddress = optarg; @@ -220,25 +244,32 @@ void Settings::parseInput(int argc, char** argv) break; case 'b': //------------------------------------------------------- - if ( atoi(optarg) == 8 ) { - mAudioBitResolution = AudioInterface::BIT8; } - else if ( atoi(optarg) == 16 ) { - mAudioBitResolution = AudioInterface::BIT16; } - else if ( atoi(optarg) == 24 ) { - mAudioBitResolution = AudioInterface::BIT24; } - else if ( atoi(optarg) == 32 ) { - mAudioBitResolution = AudioInterface::BIT32; } - else { - std::cerr << "--bitres ERROR: Wrong bit resolution: " - << atoi(optarg) << " is not supported." << endl; + if (atoi(optarg) == 8) { + mAudioBitResolution = AudioInterface::BIT8; + } else if (atoi(optarg) == 16) { + mAudioBitResolution = AudioInterface::BIT16; + } else if (atoi(optarg) == 24) { + mAudioBitResolution = AudioInterface::BIT24; + } else if (atoi(optarg) == 32) { + mAudioBitResolution = AudioInterface::BIT32; + } else { printUsage(); - std::exit(1); } + std::cerr << "--bitres ERROR: Bit resolution: " + << atoi(optarg) << " is not supported." << endl; + std::exit(1); + } break; case 'q': //------------------------------------------------------- - if ( atoi(optarg) <= 0 ) { - std::cerr << "--queue ERROR: The queue has to be equal or greater than 2" << endl; + if (0 == strncmp(optarg, "auto", 4)) { + mBufferQueueLength = -atoi(optarg+4); + if (0 == mBufferQueueLength) { + mBufferQueueLength = -500; + } + } + else if ( atoi(optarg) <= 0 ) { printUsage(); + std::cerr << "--queue ERROR: The queue has to be equal or greater than 2" << endl; std::exit(1); } else { mBufferQueueLength = atoi(optarg); @@ -247,8 +278,8 @@ void Settings::parseInput(int argc, char** argv) case 'r': //------------------------------------------------------- if ( atoi(optarg) <= 0 ) { - std::cerr << "--redundancy ERROR: The reduncancy has to be a positive integer" << endl; printUsage(); + std::cerr << "--redundancy ERROR: The redundancy has to be a positive integer" << endl; std::exit(1); } else { mRedundancy = atoi(optarg); @@ -256,7 +287,10 @@ void Settings::parseInput(int argc, char** argv) break; case 'z': // underrun to zero //------------------------------------------------------- - mUnderrrunZero = true; + mUnderrunMode = JackTrip::ZEROS; + break; + case 't': // quit on timeout + mStopOnTimeout = true; break; case 'l': // loopback //------------------------------------------------------- @@ -274,6 +308,10 @@ void Settings::parseInput(int argc, char** argv) //------------------------------------------------------- mClientName = optarg; break; + case 'K': // Set Remote client Name + //------------------------------------------------------- + mRemoteClientName = optarg; + break; case 'R': // RtAudio //------------------------------------------------------- mUseJack = false; @@ -309,70 +347,204 @@ void Settings::parseInput(int argc, char** argv) //------------------------------------------------------- gVerboseFlag = true; if (gVerboseFlag) std::cout << "Verbose mode" << std::endl; + mEffects.setVerboseFlag(gVerboseFlag); break; case 'p': //------------------------------------------------------- - if ( atoi(optarg) == 0 ) { - mHubConnectionMode = JackTrip::SERVERTOCLIENT; } - else if ( atoi(optarg) == 1 ) { - mHubConnectionMode = JackTrip::CLIENTECHO; } - else if ( atoi(optarg) == 2 ) { - mHubConnectionMode = JackTrip::CLIENTFOFI; } - else if ( atoi(optarg) == 3 ) { - mHubConnectionMode = JackTrip::RESERVEDMATRIX; } - else if ( atoi(optarg) == 4 ) { - mHubConnectionMode = JackTrip::FULLMIX; } - else { + if ( atoi(optarg) == 0 ) { + mHubConnectionMode = JackTrip::SERVERTOCLIENT; + } else if ( atoi(optarg) == 1 ) { + mHubConnectionMode = JackTrip::CLIENTECHO; + } else if ( atoi(optarg) == 2 ) { + mHubConnectionMode = JackTrip::CLIENTFOFI; + } else if ( atoi(optarg) == 3 ) { + mHubConnectionMode = JackTrip::RESERVEDMATRIX; + } else if ( atoi(optarg) == 4 ) { + mHubConnectionMode = JackTrip::FULLMIX; + } else if ( atoi(optarg) == 5 ) { + mHubConnectionMode = JackTrip::NOAUTO; + } else { + printUsage(); std::cerr << "-p ERROR: Wrong HubConnectionMode: " << atoi(optarg) << " is not supported." << endl; - printUsage(); - std::exit(1); } + std::exit(1); + } break; case 'I': // IO Stat timeout //------------------------------------------------------- mIOStatTimeout = atoi(optarg); if (0 > mIOStatTimeout) { - std::cerr << "--iostat ERROR: negative timeout." << endl; printUsage(); + std::cerr << "--iostat ERROR: negative timeout." << endl; std::exit(1); } break; case 'G': // IO Stat log file //------------------------------------------------------- - mIOStatStream.open(optarg); - if (!mIOStatStream.is_open()) { + mIOStatStream.reset(new std::ofstream(optarg)); + if (!mIOStatStream->is_open()) { + printUsage(); std::cerr << "--iostatlog FAILED to open " << optarg << " for writing." << endl; + std::exit(1); + } + break; + case OPT_BUFSTRATEGY: // Buf strategy + mBufferStrategy = atoi(optarg); + if (-1 > mBufferStrategy || 2 < mBufferStrategy) { + std::cerr << "Unsupported buffer strategy " << optarg << endl; printUsage(); std::exit(1); } break; + case OPT_SIMLOSS: // Simulate packet loss + mSimulatedLossRate = atof(optarg); + break; + case OPT_SIMJITTER: // Simulate jitter + char* endp; + mSimulatedJitterRate = strtod(optarg, &endp); + if (0 == *endp) { + mSimulatedDelayRel = 1.0; + } + else { + mSimulatedDelayRel = atof(endp+1); + } + break; + case OPT_BROADCAST: // Broadcast output + mBroadcastQueue = atoi(optarg); + break; + case OPT_RTUDPPRIORITY: // Use RT priority for UDPDataProtocol thread + mUseRtUdpPriority = true; + break; case 'h': //------------------------------------------------------- printUsage(); std::exit(0); break; - default: + case 'O': { // Overflow limiter (i, o, or io) + //------------------------------------------------------- + char cmd[] { "--overflowlimiting (-O)" }; + if (gVerboseFlag) { + printf("%s argument = %s\n",cmd,optarg); + } + int returnCode = mEffects.parseLimiterOptArg(cmd,optarg); + if (returnCode > 1) { + mEffects.printHelp(cmd,ch); + std::cerr << cmd << " required argument `" << optarg << "' is malformed\n"; + std::exit(1); + } else if (returnCode == 1) { + std::exit(0); // benign but not continuing such as "help" + } + break; } + case 'a': { // assumed number of clients (applies to outgoing limiter) + //------------------------------------------------------- + char cmd[] { "--assumednumclients (-a)" }; + if (gVerboseFlag) { + printf("%s argument = %s\n",cmd,optarg); + } + int returnCode = mEffects.parseAssumedNumClientsOptArg(cmd,optarg); + if (returnCode > 1) { + mEffects.printHelp(cmd,ch); + std::cerr << cmd << " required argument `" << optarg << "' is malformed\n"; + std::exit(1); + } else if (returnCode == 1) { + std::exit(0); // help printed + } + break; } + case 'f': { // --effects (-f) effectsSpecArg + //------------------------------------------------------- + char cmd[] { "--effects (-f)" }; + int returnCode = mEffects.parseEffectsOptArg(cmd,optarg); + if (returnCode > 1) { + mEffects.printHelp(cmd,ch); + std::cerr << cmd << " required argument `" << optarg << "' is malformed\n"; + std::exit(1); + } else if (returnCode == 1) { + std::exit(0); // something benign but non-continuing like "help" + } + break; } + case 'x': { // examine connection (test mode) + //------------------------------------------------------- + char cmd[] { "--examine-audio-delay (-x)" }; + if (tolower(optarg[0])=='h') { + mAudioTester.printHelp(cmd,ch); + std::exit(0); + } + mAudioTester.setEnabled(true); + if (optarg == 0 || optarg[0] == '-' || optarg[0] == 0) { // happens when no -f argument specified + printUsage(); + std::cerr << cmd << " ERROR: Print-interval argument REQUIRED (set to 0.0 to see every delay)\n"; + std::exit(1); + } + mAudioTester.setPrintIntervalSec(atof(optarg)); + break; } + case ':': { + printUsage(); + printf("*** Missing option argument *** see above for usage\n\n"); + break; } + case '?': { + printUsage(); + printf("*** Unknown, missing, or ambiguous option argument *** see above for usage\n\n"); + std::exit(1); + break; } + default: { //------------------------------------------------------- printUsage(); - std::exit(0); - break; + printf("*** Unrecognized option -%c *** see above for usage\n",ch); + std::exit(1); + break; } } // Warn user if undefined options where entered //---------------------------------------------------------------------------- if (optind < argc) { + if (strcmp(argv[optind],"help")!=0) { cout << gPrintSeparator << endl; - cout << "WARINING: The following entered options have no effect." << endl; - cout << " They will be ignored!" << endl; - cout << " Type 'jacktrip' to see options." << endl; + cout << "*** Unexpected command-line argument(s): "; for( ; optind < argc; optind++) { - cout << "argument: " << argv[optind] << endl; + cout << argv[optind] << " "; } - cout << gPrintSeparator << endl; + cout << endl << gPrintSeparator << endl; + } + printUsage(); + std::exit(1); } -} + assert(mNumChans>0); + mAudioTester.setSendChannel(mNumChans-1); // use last channel for latency testing + // Originally, testing only in the last channel was adopted + // because channel 0 ("left") was a clap track on CCRMA loopback + // servers. Now, however, we also do it in order to easily keep + // effects in all but the last channel, enabling silent testing + // in the last channel in parallel with normal operation of the others. + + // Exit if options are incompatible + //---------------------------------------------------------------------------- + bool haveSomeServerMode = not ((mJackTripMode == JackTrip::CLIENT) || (mJackTripMode == JackTrip::CLIENTTOPINGSERVER)); + if (mEffects.getHaveEffect() && haveSomeServerMode) { + std::cerr << "*** --effects (-f) ERROR: Effects not yet supported server modes (-S and -s).\n\n"; + std::exit(1); + } + if (mEffects.getHaveLimiter() && haveSomeServerMode) { + if (mEffects.getLimit() != Effects::LIMITER_MODE::LIMITER_OUTGOING) { // default case + std::cerr << "*** --overflowlimiting (-O) ERROR: Limiters not yet supported server modes (-S and -s).\n\n"; + } + mEffects.setNoLimiters(); + // don't exit since an outgoing limiter should be the default (could exit for incoming case): + // std::exit(1); + } + if (mAudioTester.getEnabled() && haveSomeServerMode) { + std::cerr << "*** --examine-audio-delay (-x) ERROR: Audio latency measurement not supported in server modes (-S and -s)\n\n"; + std::exit(1); + } + if (mAudioTester.getEnabled() + && (mAudioBitResolution != AudioInterface::BIT16) + && (mAudioBitResolution != AudioInterface::BIT32) ) { // BIT32 not tested but should be ok + // BIT24 should work also, but there's a comment saying it's broken right now, so exclude it + std::cerr << "*** --examine-audio-delay (-x) ERROR: Only --bitres (-b) 16 and 32 presently supported for audio latency measurement.\n\n"; + std::exit(1); + } +} //******************************************************************************* void Settings::printUsage() @@ -384,12 +556,12 @@ void Settings::printUsage() cout << "SoundWIRE group at CCRMA, Stanford University" << endl; cout << "VERSION: " << gVersion << endl; cout << "" << endl; - cout << "Usage: jacktrip [-s|-c host] [options]" << endl; + cout << "Usage: jacktrip [-s|-c|-S|-C hostIPAddressOrURL] [options]" << endl; cout << "" << endl; cout << "Options: " << endl; - cout << "REQUIRED ARGUMENTS: " << endl; - cout << " -s, --server Run in Server Mode" << endl; - cout << " -c, --client <peer_hostname_or_IP_num> Run in Client Mode" << endl; + cout << "REQUIRED ARGUMENTS: One of:" << endl; + cout << " -s, --server Run in P2P Server Mode" << endl; + cout << " -c, --client <peer_hostname_or_IP_num> Run in P2P Client Mode" << endl; cout << " -S, --jacktripserver Run in Hub Server Mode" << endl; cout << " -C, --pingtoserver <peer_name_or_IP> Run in Hub Client Mode" << endl; cout << endl; @@ -400,7 +572,7 @@ void Settings::printUsage() cout << " -w, --wair Run in WAIR Mode" << endl; cout << " -N, --addcombfilterlength # comb length adjustment for WAIR (default " << gDefaultAddCombFilterLength << ")" << endl; - cout << " -H, --combfilterfeedback # comb feedback adjustment for WAIR (default " + cout << " -H, --combfilterfeedback # (roomSize) comb feedback adjustment for WAIR (default " << gDefaultCombFilterFeedback << ")" << endl; #endif // endwhere cout << " -q, --queue # (2 or more) Queue Buffer Length, in Packet Size (default: " @@ -411,24 +583,41 @@ void Settings::printUsage() cout << " -B, --bindport # Set only the bind port number (default: " << gDefaultPort << ")" << endl; cout << " -P, --peerport # Set only the peer port number (default: " << gDefaultPort << ")" << endl; cout << " -U, --udpbaseport Set only the server udp base port number (default: 61002)" << endl; - cout << " -b, --bitres # (8, 16, 24, 32) Audio Bit Rate Resolutions (default: 16)" << endl; - cout << " -p, --hubpatch # (0, 1, 2, 3, 4) Hub auto audio patch, only has effect if running HUB SERVER mode, 0=server-to-clients, 1=client loopback, 2=clients can hear all clients except themselves, 3=reserved for TUB, 4=full mix (default: 0), i.e. clients auto-connect and hear all clients including themselves" << endl; + cout << " -b, --bitres # (8, 16, 24, 32) Audio Bit Rate Resolutions (default: 16, 32 uses floating-point)" << endl; + cout << " -p, --hubpatch # (0, 1, 2, 3, 4, 5) Hub auto audio patch, only has effect if running HUB SERVER mode, 0=server-to-clients, 1=client loopback, 2=client fan out/in but not loopback, 3=reserved for TUB, 4=full mix, 5=no auto patching (default: 0)" << endl; cout << " -z, --zerounderrun Set buffer to zeros when underrun occurs (default: wavetable)" << endl; + cout << " -t, --timeout Quit after 10 seconds of no network activity" << endl; cout << " -l, --loopback Run in Loop-Back Mode" << endl; cout << " -j, --jamlink Run in JamLink Mode (Connect to a JamLink Box)" << endl; - cout << " --clientname Change default client name (default: JackTrip)" << endl; - cout << " --localaddress Change default local host IP address (default: 127.0.0.1)" << endl; - cout << " --nojackportsconnect Don't connect default audio ports in jack, including not doing hub auto audio patch in HUB SERVER mode." << endl; + cout << " -J, --clientname Change default client name (default: JackTrip)" << endl; + cout << " -K, --remotename Change default remote client name when connecting to a hub server (the default is derived from this computer's external facing IP address)" << endl; + cout << " -L, --localaddress Change default local host IP address (default: 127.0.0.1)" << endl; + cout << " -D, --nojackportsconnect Don't connect default audio ports in jack" << endl; + cout << " --bufstrategy # (0, 1, 2) Use alternative jitter buffer" << endl; + cout << " --broadcast <broadcast_queue> Turn on broadcast output ports with extra queue (requires new jitter buffer)" << endl; + cout << " --udprt Use RT thread priority for network I/O" << endl; + cout << endl; + cout << "OPTIONAL SIGNAL PROCESSING: " << endl; + cout << " -f, --effects # | paramString | help Turn on incoming and/or outgoing compressor and/or reverb in Client - see `-f help' for details" << endl; + cout << " -O, --overflowlimiting i|o[w]|io[w]|n|help" << endl; + cout << " Use audio limiter(s) in Client, i=incoming from network, o=outgoing to network, io=both, n=no limiters, w=warn if limiting (default=n). Say -O help for more." << endl; + cout << " -a, --assumednumclients help|# (1,2,...) Assumed number of Clients (sources) mixing at Hub Server (otherwise 2 assumed by -O)" << endl; cout << endl; cout << "ARGUMENTS TO USE JACKTRIP WITHOUT JACK:" << endl; - cout << " --rtaudio Use system's default sound system instead of Jack" << endl; - cout << " --srate # Set the sampling rate, works on --rtaudio mode only (default: 48000)" << endl; - cout << " --bufsize # Set the buffer size, works on --rtaudio mode only (default: 128)" << endl; - cout << " --deviceid # The rtaudio device id --rtaudio mode only (default: 0)" << endl; + cout << " -R, --rtaudio Use system's default sound system instead of Jack" << endl; + cout << " -T, --srate # Set the sampling rate, works on --rtaudio mode only (default: 48000)" << endl; + cout << " -F, --bufsize # Set the buffer size, works on --rtaudio mode only (default: 128)" << endl; + cout << " -d, --deviceid # The rtaudio device id --rtaudio mode only (default: 0)" << endl; cout << endl; cout << "ARGUMENTS TO DISPLAY IO STATISTICS:" << endl; cout << " -I, --iostat <time_in_secs> Turn on IO stat reporting with specified interval (in seconds)" << endl; cout << " -G, --iostatlog <log_file> Save stat log into a file (default: print in stdout)" << endl; + cout << " -x, --examine-audio-delay <print_interval_in_secs> | help\n"; + cout << " Print round-trip audio delay statistics. See `-x help' for details." << endl; + cout << endl; + cout << "ARGUMENTS TO SIMULATE NETWORK ISSUES:" << endl; + cout << " --simloss <rate> Simulate packet loss" << endl; + cout << " --simjitter <rate>,<d> Simulate jitter, d is max delay in packets" << endl; cout << endl; cout << "HELP ARGUMENTS: " << endl; cout << " -v, --version Prints Version Number" << endl; @@ -439,211 +628,208 @@ void Settings::printUsage() //******************************************************************************* -void Settings::startJackTrip() +UdpHubListener *Settings::getConfiguredHubServer() { - if ((mBindPortNum < gBindPortLow) || (mBindPortNum > gBindPortHigh)) std::cout << "BindPort: "<< mBindPortNum << " outside range" << std::endl; - /// \todo Change this, just here to test - if ( mJackTripServer ) { - if (gVerboseFlag) std::cout << "JackTrip HUB SERVER TCP Bind Port: " << mBindPortNum << std::endl; - UdpHubListener* udpmaster = new UdpHubListener(mBindPortNum,mServerUdpPortNum); - udpmaster->setSettings(this); + if (gVerboseFlag) std::cout << "JackTrip HUB SERVER TCP Bind Port: " << mBindPortNum << std::endl; + UdpHubListener *udpHub = new UdpHubListener(mBindPortNum, mServerUdpPortNum); + //udpHub->setSettings(this); #ifdef WAIR // WAIR - udpmaster->setWAIR(mWAIR); + udpHub->setWAIR(mWAIR); #endif // endwhere - udpmaster->setHubPatch(mHubConnectionMode); - udpmaster->setConnectDefaultAudioPorts(mConnectDefaultAudioPorts); - if (gVerboseFlag) std::cout << "Settings:startJackTrip before udpmaster->start" << std::endl; - // Set buffers to zero when underrun - if ( mUnderrrunZero ) { - cout << "Setting buffers to zero when underrun..." << endl; - cout << gPrintSeparator << std::endl; - udpmaster->setUnderRunMode(JackTrip::ZEROS); - } - udpmaster->setBufferQueueLength(mBufferQueueLength); - udpmaster->start(); - - //---Thread Pool Test-------------------------------------------- - /* - cout << "BEFORE START" << endl; - ThreadPoolTest* thtest = new ThreadPoolTest(); - // QThreadPool takes ownership and deletes 'hello' automatically - QThreadPool::globalInstance()->start(thtest); - - cout << "AFTER START" << endl; - sleep(2); - thtest->stop(); - QThreadPool::globalInstance()->waitForDone(); - */ - //--------------------------------------------------------------- + udpHub->setHubPatch(mHubConnectionMode); + if (mHubConnectionMode == JackTrip::NOAUTO) { + udpHub->setConnectDefaultAudioPorts(false); + } else { + udpHub->setConnectDefaultAudioPorts(mConnectDefaultAudioPorts); } + // Set buffers to zero when underrun + if ( mUnderrunMode == JackTrip::ZEROS ) { + cout << "Setting buffers to zero when underrun..." << endl; + cout << gPrintSeparator << std::endl; + udpHub->setUnderRunMode(mUnderrunMode); + } + udpHub->setBufferQueueLength(mBufferQueueLength); + + udpHub->setBufferStrategy(mBufferStrategy); + udpHub->setNetIssuesSimulation(mSimulatedLossRate, + mSimulatedJitterRate, mSimulatedDelayRel); + udpHub->setBroadcast(mBroadcastQueue); + udpHub->setUseRtUdpPriority(mUseRtUdpPriority); + + if (mIOStatTimeout > 0) { + udpHub->setIOStatTimeout(mIOStatTimeout); + udpHub->setIOStatStream(mIOStatStream); + } + return udpHub; +} - else { - - //JackTrip jacktrip(mJackTripMode, mDataProtocol, mNumChans, - // mBufferQueueLength, mAudioBitResolution); +JackTrip *Settings::getConfiguredJackTrip() +{ #ifdef WAIR // WAIR - if (gVerboseFlag) std::cout << "Settings:startJackTrip mNumNetRevChans = " << mNumNetRevChans << std::endl; + if (gVerboseFlag) std::cout << "Settings:startJackTrip mNumNetRevChans = " << mNumNetRevChans << std::endl; #endif // endwhere - if (gVerboseFlag) std::cout << "Settings:startJackTrip before new JackTrip" << std::endl; - mJackTrip = new JackTrip(mJackTripMode, mDataProtocol, mNumChans, - #ifdef WAIR // wair - mNumNetRevChans, - #endif // endwhere - mBufferQueueLength, mRedundancy, mAudioBitResolution, - /*DataProtocol::packetHeaderTypeT PacketHeaderType = */DataProtocol::DEFAULT, - /*underrunModeT UnderRunMode = */ mUnderRunMode, - /* int receiver_bind_port = */ gDefaultPort, - /*int sender_bind_port = */ gDefaultPort, - /*int receiver_peer_port = */ gDefaultPort, - /* int sender_peer_port = */ gDefaultPort, - mPeerPortNum - ); - - // Set connect or not default audio ports. Only work for jack - mJackTrip->setConnectDefaultAudioPorts(mConnectDefaultAudioPorts); - - // Connect Signals and Slots - QObject::connect(mJackTrip, SIGNAL( signalProcessesStopped() ), - this, SLOT( slotExitProgram() )); - - // Change client name if different from default - if (mClientName != NULL) { - mJackTrip->setClientName(mClientName); - } + if (gVerboseFlag) std::cout << "Settings:startJackTrip before new JackTrip" << std::endl; + JackTrip *jackTrip = new JackTrip(mJackTripMode, mDataProtocol, mNumChans, +#ifdef WAIR // wair + mNumNetRevChans, +#endif // endwhere + mBufferQueueLength, mRedundancy, mAudioBitResolution, + /*DataProtocol::packetHeaderTypeT PacketHeaderType = */DataProtocol::DEFAULT, + /*underrunModeT UnderRunMode = */ mUnderrunMode, + /* int receiver_bind_port = */ mBindPortNum, + /*int sender_bind_port = */ mBindPortNum, + /*int receiver_peer_port = */ mPeerPortNum, + /* int sender_peer_port = */ mPeerPortNum, + mPeerPortNum + ); + // Set connect or not default audio ports. Only work for jack + jackTrip->setConnectDefaultAudioPorts(mConnectDefaultAudioPorts); + + // Change client name if different from default + if (!mClientName.isEmpty()) { + jackTrip->setClientName(mClientName); + } - // Set buffers to zero when underrun - if ( mUnderrrunZero ) { - cout << "Setting buffers to zero when underrun..." << endl; - cout << gPrintSeparator << std::endl; - mJackTrip->setUnderRunMode(JackTrip::ZEROS); - } else { - cout << "Setting buffers to wavetable when underrun..." << endl; - cout << gPrintSeparator << std::endl; - mJackTrip->setUnderRunMode(JackTrip::WAVETABLE); - } + if (!mRemoteClientName.isEmpty() && (mJackTripMode == JackTrip::CLIENTTOPINGSERVER)) { + jackTrip->setRemoteClientName(mRemoteClientName); + } - // Set peer address in server mode - if ( mJackTripMode == JackTrip::CLIENT || mJackTripMode == JackTrip::CLIENTTOPINGSERVER ) { - mJackTrip->setPeerAddress(mPeerAddress.toLatin1().data()); } - - // if(mLocalAddress!=QString()) // default - // mJackTrip->setLocalAddress(QHostAddress(mLocalAddress.toLatin1().data())); - // else - // mJackTrip->setLocalAddress(QHostAddress::Any); - - // Set Ports - //cout << "SETTING ALL PORTS" << endl; - if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->setBindPorts" << std::endl; - mJackTrip->setBindPorts(mBindPortNum); - if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->setPeerPorts" << std::endl; - mJackTrip->setPeerPorts(mPeerPortNum); - - // Set in JamLink Mode - if ( mJamLink ) { - cout << "Running in JamLink Mode..." << endl; - cout << gPrintSeparator << std::endl; - mJackTrip->setPacketHeaderType(DataProtocol::JAMLINK); - } + // Set buffers to zero when underrun (Actual setting is handled in constructor.) + if (mUnderrunMode == JackTrip::ZEROS) { + cout << "Setting buffers to zero when underrun..." << endl; + cout << gPrintSeparator << std::endl; + } - // Set in EmptyHeader Mode - if ( mEmptyHeader ) { - cout << "Running in EmptyHeader Mode..." << endl; - cout << gPrintSeparator << std::endl; - mJackTrip->setPacketHeaderType(DataProtocol::EMPTY); - } + jackTrip->setStopOnTimeout(mStopOnTimeout); + + // Set peer address in server mode + if (mJackTripMode == JackTrip::CLIENT || mJackTripMode == JackTrip::CLIENTTOPINGSERVER) { + jackTrip->setPeerAddress(mPeerAddress); } + + // if(mLocalAddress!=QString()) // default + // mJackTrip->setLocalAddress(QHostAddress(mLocalAddress.toLatin1().data())); + // else + // mJackTrip->setLocalAddress(QHostAddress::Any); + + // Set Ports - Done in constructor now. + //cout << "SETTING ALL PORTS" << endl; + /*if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->setBindPorts" << std::endl; + jackTrip->setBindPorts(mBindPortNum); + if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->setPeerPorts" << std::endl; + jackTrip->setPeerPorts(mPeerPortNum);*/ + + // Set in JamLink Mode + if ( mJamLink ) { + cout << "Running in JamLink Mode..." << endl; + cout << gPrintSeparator << std::endl; + jackTrip->setPacketHeaderType(DataProtocol::JAMLINK); + } - // Set RtAudio + // Set in EmptyHeader Mode + if (mEmptyHeader) { + cout << "Running in EmptyHeader Mode..." << endl; + cout << gPrintSeparator << std::endl; + jackTrip->setPacketHeaderType(DataProtocol::EMPTY); + } + + // Set RtAudio #ifdef __RT_AUDIO__ - if (!mUseJack) { - mJackTrip->setAudiointerfaceMode(JackTrip::RTAUDIO); - } + if (!mUseJack) { + mJackTrip->setAudiointerfaceMode(JackTrip::RTAUDIO); + } #endif - // Chanfe default Sampling Rate - if (mChanfeDefaultSR) { - mJackTrip->setSampleRate(mSampleRate); - } + // Chanfe default Sampling Rate + if (mChanfeDefaultSR) { + jackTrip->setSampleRate(mSampleRate); + } - // Chanfe defualt device ID - if (mChanfeDefaultID) { - mJackTrip->setDeviceID(mDeviceID); - } + // Chanfe defualt device ID + if (mChanfeDefaultID) { + jackTrip->setDeviceID(mDeviceID); + } - // Chanfe default Buffer Size - if (mChanfeDefaultBS) { - mJackTrip->setAudioBufferSizeInSamples(mAudioBufferSize); - } + // Chanfe default Buffer Size + if (mChanfeDefaultBS) { + jackTrip->setAudioBufferSizeInSamples(mAudioBufferSize); + } + jackTrip->setBufferStrategy(mBufferStrategy); + jackTrip->setNetIssuesSimulation(mSimulatedLossRate, + mSimulatedJitterRate, mSimulatedDelayRel); + jackTrip->setBroadcast(mBroadcastQueue); + jackTrip->setUseRtUdpPriority(mUseRtUdpPriority); + + // Add Plugins + if (mLoopBack) { + cout << "Running in Loop-Back Mode..." << endl; + cout << gPrintSeparator << std::endl; + //std::tr1::shared_ptr<LoopBack> loopback(new LoopBack(mNumChans)); + //mJackTrip->appendProcessPlugin(loopback.get()); + +#if 0 // previous technique: + LoopBack* loopback = new LoopBack(mNumChans); + jackTrip->appendProcessPlugin(loopback); +#else // simpler method ( see AudioInterface.cpp callback() ): + jackTrip->setLoopBack(true); +#endif - // Add Plugins - if ( mLoopBack ) { - cout << "Running in Loop-Back Mode..." << endl; - cout << gPrintSeparator << std::endl; - //std::tr1::shared_ptr<LoopBack> loopback(new LoopBack(mNumChans)); - //mJackTrip->appendProcessPlugin(loopback.get()); - - LoopBack* loopback = new LoopBack(mNumChans); - mJackTrip->appendProcessPlugin(loopback); - - // ----- Test Karplus Strong ----------------------------------- - //std::tr1::shared_ptr<NetKS> loopback(new NetKS()); - //mJackTrip->appendProcessPlugin(loopback); - //loopback->play(); - //NetKS* netks = new NetKS; - //mJackTrip->appendProcessPlugin(netks); - //netks->play(); - // ------------------------------------------------------------- - } + // ----- Test Karplus Strong ----------------------------------- + //std::tr1::shared_ptr<NetKS> loopback(new NetKS()); + //mJackTrip->appendProcessPlugin(loopback); + //loopback->play(); + //NetKS* netks = new NetKS; + //mJackTrip->appendProcessPlugin(netks); + //netks->play(); + // ------------------------------------------------------------- + } + + if (mIOStatTimeout > 0) { + jackTrip->setIOStatTimeout(mIOStatTimeout); + jackTrip->setIOStatStream(mIOStatStream); + } + + jackTrip->setAudioTesterP(&mAudioTester); + + // Allocate audio effects in client, if any: + int nReservedChans = mAudioTester.getEnabled() ? 1 : 0; // no fx allowed on tester channel + std::vector<ProcessPlugin*> outgoingEffects = mEffects.allocateOutgoingEffects(mNumChans-nReservedChans); + for (auto p : outgoingEffects) { + jackTrip->appendProcessPluginToNetwork( p ); + } + std::vector<ProcessPlugin*> incomingEffects = mEffects.allocateIncomingEffects(mNumChans-nReservedChans); + for (auto p : incomingEffects) { + jackTrip->appendProcessPluginFromNetwork( p ); + } #ifdef WAIR // WAIR - if ( mWAIR ) { - cout << "Running in WAIR Mode..." << endl; - cout << gPrintSeparator << std::endl; - switch ( mNumNetRevChans ) - { - case 16 : - { - mJackTrip->appendProcessPlugin(new ap8x2(mNumChans)); // plugin slot 0 - ///////////////////////////////////////////////////////// - Stk16* plugin = new Stk16(mNumNetRevChans); - plugin->Stk16::initCombClient(mClientAddCombLen, mClientRoomSize); - mJackTrip->appendProcessPlugin(plugin); // plugin slot 1 - } - break; - default: - throw std::invalid_argument("Settings: mNumNetRevChans doesn't correspond to Faust plugin"); - break; - } + if ( mWAIR ) { + cout << "Running in WAIR Mode..." << endl; + cout << gPrintSeparator << std::endl; + switch ( mNumNetRevChans ) + { + case 16 : + { + jackTrip->appendProcessPluginFromNetwork(new ap8x2(mNumChans)); // plugin slot 0 + ///////////////////////////////////////////////////////// + Stk16* plugin = new Stk16(mNumNetRevChans); + plugin->Stk16::initCombClient(mClientAddCombLen, mClientRoomSize); + jackTrip->appendProcessPluginFromNetwork(plugin); // plugin slot 1 } -#endif // endwhere - - // Start JackTrip - if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->startProcess" << std::endl; - mJackTrip->startProcess( - #ifdef WAIRTOHUB // WAIR - 0 // for WAIR compatibility, ID in jack client name - #endif // endwhere - ); - if (0 < getIOStatTimeout()) { - mJackTrip->startIOStatTimer(getIOStatTimeout(), getIOStatStream()); + break; + default: + throw std::invalid_argument("Settings: mNumNetRevChans doesn't correspond to Faust plugin"); + break; + } + break; + default: + throw std::invalid_argument("Settings: mNumNetRevChans doesn't correspond to Faust plugin"); + break; } - // if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->start" << std::endl; - // this is a noop - // mJackTrip->start(); - - /* - sleep(10); - cout << "Stoping JackTrip..." << endl; - mJackTrip->stop(); - */ } -} - +#endif // endwhere -//******************************************************************************* -void Settings::stopJackTrip() -{ - mJackTrip->stop(); + return jackTrip; } diff --git a/src/Settings.h b/src/Settings.h index 9856bf2..569b451 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -41,6 +41,7 @@ #include <cstdlib> #include <fstream> +#include <vector> #include "DataProtocol.h" @@ -49,10 +50,14 @@ #endif //__NO_JACK__ #include "JackTrip.h" +#include "UdpHubListener.h" + +#include "Effects.h" +#include "AudioTester.h" /** \brief Class to set usage options and parse settings from input */ -class Settings : public QThread +class Settings : public QObject { Q_OBJECT; @@ -63,29 +68,16 @@ public: /// \brief Parses command line input void parseInput(int argc, char** argv); - void startJackTrip(); - void stopJackTrip(); + UdpHubListener *getConfiguredHubServer(); + JackTrip *getConfiguredJackTrip(); /// \brief Prints usage help void printUsage(); bool getLoopBack() { return mLoopBack; } - int getIOStatTimeout() const {return mIOStatTimeout;} - const std::ostream& getIOStatStream() const - { - return mIOStatStream.is_open() ? (std::ostream&)mIOStatStream : std::cout; - } - - -public slots: - void slotExitProgram() - { - std::cerr << "Exiting JackTrip..." << std::endl; - std::exit(1); - } + bool isHubServer() { return mJackTripServer; } private: - JackTrip* mJackTrip; ///< JackTrip class JackTrip::jacktripModeT mJackTripMode; ///< JackTrip::jacktripModeT JackTrip::dataProtocolT mDataProtocol; ///< Data Protocol int mNumChans; ///< Number of Channels (inputs = outputs) @@ -95,9 +87,11 @@ private: int mBindPortNum; ///< Bind Port Number int mPeerPortNum; ///< Peer Port Number int mServerUdpPortNum; - char* mClientName; ///< JackClient Name - bool mUnderrrunZero; ///< Use Underrun to Zero mode - JackTrip::underrunModeT mUnderRunMode; + QString mClientName; ///< JackClient Name + QString mRemoteClientName; + JackTrip::underrunModeT mUnderrunMode; ///< Underrun mode + bool mStopOnTimeout; /// < Stop jacktrip after 10 second network timeout + int mBufferStrategy; #ifdef WAIR // wair int mNumNetRevChans; ///< Number of Network Audio Channels (net comb filters) @@ -122,7 +116,14 @@ private: unsigned int mHubConnectionMode; bool mConnectDefaultAudioPorts; ///< Connect or not jack audio ports int mIOStatTimeout; - std::ofstream mIOStatStream; + QSharedPointer<std::ofstream> mIOStatStream; + Effects mEffects; + double mSimulatedLossRate; + double mSimulatedJitterRate; + double mSimulatedDelayRel; + int mBroadcastQueue; + bool mUseRtUdpPriority; + AudioTester mAudioTester; }; #endif diff --git a/src/UdpDataProtocol.cpp b/src/UdpDataProtocol.cpp index 85738a6..3680185 100644 --- a/src/UdpDataProtocol.cpp +++ b/src/UdpDataProtocol.cpp @@ -53,6 +53,8 @@ #endif #if defined (__LINUX__) || (__MAC_OSX__) #include <sys/socket.h> // for POSIX Sockets +#include <unistd.h> +#include <sys/fcntl.h> #endif using std::cout; using std::endl; @@ -72,7 +74,9 @@ UdpDataProtocol::UdpDataProtocol(JackTrip* jacktrip, const runModeT runmode, mBindPort(bind_port), mPeerPort(peer_port), mRunMode(runmode), mAudioPacket(NULL), mFullPacket(NULL), - mUdpRedundancyFactor(udp_redundancy_factor) + mUdpRedundancyFactor(udp_redundancy_factor), + mControlPacketSize(63), + mStopSignalSent(false) { mStopped = false; mIPv6 = false; @@ -85,6 +89,9 @@ UdpDataProtocol::UdpDataProtocol(JackTrip* jacktrip, const runModeT runmode, QObject::connect(this, SIGNAL(signalWaitingTooLong(int)), jacktrip, SLOT(slotUdpWaitingTooLongClientGoneProbably(int)), Qt::QueuedConnection); } + mSimulatedLossRate = 0.0; + mSimulatedJitterRate = 0.0; + mSimulatedJitterMaxDelay = 0.0; } @@ -93,6 +100,13 @@ UdpDataProtocol::~UdpDataProtocol() { delete[] mAudioPacket; delete[] mFullPacket; + if (mRunMode == RECEIVER) { +#ifdef __WIN_32__ + closesocket(mSocket); +#else + ::close(mSocket); +#endif + } wait(); } @@ -121,7 +135,7 @@ void UdpDataProtocol::setPeerAddress(const char* peerHostOrIP) if ( mPeerAddress.protocol() == QAbstractSocket::IPv6Protocol ) { mIPv6 = true; } else if ( mPeerAddress.protocol() != QAbstractSocket::IPv4Protocol ) { - QString error_message = "Incorrect presentation format address\n '"; + QString error_message = "Incorrect presentation format address\n'"; error_message.append(peerHostOrIP); error_message.append("' is not a valid IP address or Host Name"); //std::cerr << "ERROR: Incorrect presentation format address" << endl; @@ -163,7 +177,7 @@ void UdpDataProtocol::setSocket(int &socket) if (socket == -1) { #endif try { - if (gVerboseFlag) std::cout << " UdpDataProtocol:run" << mRunMode << " before bindSocket(UdpSocket)" << std::endl; + if (gVerboseFlag) std::cout << " UdpDataProtocol:run" << mRunMode << " before bindSocket" << std::endl; socket = bindSocket(); // Bind Socket } catch ( const std::exception & e ) { emit signalError( e.what() ); @@ -316,11 +330,29 @@ int UdpDataProtocol::bindSocket() //******************************************************************************* -int UdpDataProtocol::receivePacket(QUdpSocket& UdpSocket, char* buf, const size_t n) +int UdpDataProtocol::receivePacket(char* buf, const size_t n) { // Block until There's something to read - while ( (UdpSocket.pendingDatagramSize() < n) && !mStopped ) { QThread::usleep(100); } - int n_bytes = UdpSocket.readDatagram(buf, n); + while ( !datagramAvailable() && !mStopped ) { + QThread::usleep(100); + } + int n_bytes = ::recv(mSocket, buf, n, 0); + if (n_bytes == mControlPacketSize) { + //Control signal (currently just check for exit packet); + bool exit = true; + for (int i = 0; i < mControlPacketSize; i++) { + if (buf[i] != char(0xff)) { + exit = false; + i = mControlPacketSize; + } + } + if (exit && !mStopSignalSent) { + mStopSignalSent = true; + emit signalCeaseTransmission("Peer Stopped"); + std::cout << "Peer Stopped" <<std::endl; + } + return 0; + } return n_bytes; } @@ -358,15 +390,24 @@ int UdpDataProtocol::sendPacket(const char* buf, const size_t n) //******************************************************************************* -void UdpDataProtocol::getPeerAddressFromFirstPacket(QUdpSocket& UdpSocket, - QHostAddress& peerHostAddress, +void UdpDataProtocol::getPeerAddressFromFirstPacket(QHostAddress& peerHostAddress, uint16_t& port) { - while ( !UdpSocket.hasPendingDatagrams() ) { + while ( !datagramAvailable() ) { msleep(100); } char buf[1]; - UdpSocket.readDatagram(buf, 1, &peerHostAddress, &port); + + struct sockaddr_storage addr; + std::memset(&addr, 0, sizeof(addr)); + socklen_t sa_len = sizeof(addr); + ::recvfrom(mSocket, buf, 1, 0, (struct sockaddr*) &addr, &sa_len); + peerHostAddress.setAddress((struct sockaddr*) &addr); + if (mIPv6) { + port = ((struct sockaddr_in6*) &addr)->sin6_port; + } else { + port = ((struct sockaddr_in*) &addr)->sin_port; + } } @@ -388,19 +429,17 @@ void UdpDataProtocol::run() // mJackTrip, SLOT(slotStopProcesses()), // Qt::QueuedConnection); - //Wrap our socket in a QUdpSocket object if we're the receiver, for convenience. - //If we're the sender, we'll just write directly to our socket. - QUdpSocket UdpSocket; if (mRunMode == RECEIVER) { - if (mIPv6) { - UdpSocket.setSocketDescriptor(mSocket, QUdpSocket::BoundState, - QUdpSocket::ReadOnly); - } else { - UdpSocket.setSocketDescriptor(mSocket, QUdpSocket::ConnectedState, - QUdpSocket::ReadOnly); - } cout << "UDP Socket Receiving in Port: " << mBindPort << endl; cout << gPrintSeparator << endl; + //Make sure our socket is in non-blocking mode. +#ifdef __WIN_32__ + u_long nonblock = 1; + ioctlsocket(mSocket, FIONBIO, &nonblock); +#else + int flags = ::fcntl(mSocket, F_GETFL, 0); + ::fcntl(mSocket, F_SETFL, flags | O_NONBLOCK); +#endif } if (gVerboseFlag) std::cout << " UdpDataProtocol:run" << mRunMode << " before Setup Audio Packet buffer, Full Packet buffer, Redundancy Variables" << std::endl; @@ -409,6 +448,9 @@ void UdpDataProtocol::run() //cout << "audio_packet_size: " << audio_packet_size << endl; mAudioPacket = new int8_t[audio_packet_size]; std::memset(mAudioPacket, 0, audio_packet_size); // set buffer to 0 + mBuffer.resize(audio_packet_size, 0); + mChans = mJackTrip->getNumChannels(); + mSmplSize = mJackTrip->getAudioBitResolution() / 8; // Setup Full Packet buffer int full_packet_size = mJackTrip->getPacketSizeInBytes(); @@ -425,14 +467,15 @@ void UdpDataProtocol::run() // (Algorithm explained at the end of this file) // --------------------------------------------- int full_redundant_packet_size = full_packet_size * mUdpRedundancyFactor; - int8_t* full_redundant_packet; - full_redundant_packet = new int8_t[full_redundant_packet_size]; - std::memset(full_redundant_packet, 0, full_redundant_packet_size); // Initialize to 0 + int8_t* full_redundant_packet = NULL; // Set realtime priority (function in jacktrip_globals.h) if (gVerboseFlag) std::cout << " UdpDataProtocol:run" << mRunMode << " before setRealtimeProcessPriority()" << std::endl; //std::cout << "Experimental version -- not using setRealtimeProcessPriority()" << std::endl; - //setRealtimeProcessPriority(); + // Anton Runov: making setRealtimeProcessPriority optional + if (mUseRtPriority) { + setRealtimeProcessPriority(); + } ///////////////////// // to see thread priorities @@ -507,24 +550,31 @@ void UdpDataProtocol::run() if (gVerboseFlag) std::cout << " UdpDataProtocol:run" << mRunMode << " before !UdpSocket.hasPendingDatagrams()" << std::endl; std::cout << "Waiting for Peer..." << std::endl; // This blocks waiting for the first packet - while ( !UdpSocket.hasPendingDatagrams() ) { + while ( !datagramAvailable() ) { if (mStopped) { return; } QThread::msleep(100); if (gVerboseFlag) std::cout << "100ms " << std::flush; } - int first_packet_size = UdpSocket.pendingDatagramSize(); - // The following line is the same as - int8_t* first_packet = new int8_t[first_packet_size]; - /// \todo fix this to avoid memory leaks - // but avoids memory leaks - //std::tr1::shared_ptr<int8_t> first_packet(new int8_t[first_packet_size]); - receivePacket( UdpSocket, reinterpret_cast<char*>(first_packet), first_packet_size); + full_redundant_packet_size = 0x10000; // max UDP datagram size + full_redundant_packet = new int8_t[full_redundant_packet_size]; + full_redundant_packet_size = receivePacket(reinterpret_cast<char*>(full_redundant_packet), full_redundant_packet_size); // Check that peer has the same audio settings if (gVerboseFlag) std::cout << std::endl << " UdpDataProtocol:run" << mRunMode << " before mJackTrip->checkPeerSettings()" << std::endl; - mJackTrip->checkPeerSettings(first_packet); + mJackTrip->checkPeerSettings(full_redundant_packet); + + int peer_chans = mJackTrip->getPeerNumChannels(full_redundant_packet); + full_packet_size = mJackTrip->getHeaderSizeInBytes() + + mJackTrip->getPeerBufferSize(full_redundant_packet) * peer_chans * mSmplSize; + /* + cout << "peer sizes: " << mJackTrip->getHeaderSizeInBytes() + << " + " << mJackTrip->getPeerBufferSize(full_redundant_packet) + << " * " << mJackTrip->getNumChannels() << " * " << (int)mJackTrip->getAudioBitResolution()/8 << endl; + cout << "full_packet_size: " << full_packet_size << " / " << mJackTrip->getPacketSizeInBytes() << endl; + cout << "full_redundant_packet_size: " << full_redundant_packet_size << endl; + // */ + if (gVerboseFlag) std::cout << "step 7" << std::endl; if (gVerboseFlag) std::cout << " UdpDataProtocol:run" << mRunMode << " before mJackTrip->parseAudioPacket()" << std::endl; - mJackTrip->parseAudioPacket(mFullPacket, mAudioPacket); std::cout << "Received Connection from Peer!" << std::endl; emit signalReceivedConnectionFromPeer(); @@ -538,6 +588,8 @@ void UdpDataProtocol::run() mTotCount = 0; mLostCount = 0; mOutOfOrderCount = 0; + mLastOutOfOrderCount = 0; + mInitialState = true; mRevivedCount = 0; mStatCount = 0; @@ -550,7 +602,7 @@ void UdpDataProtocol::run() // arrive for a longer time //timeout = UdpSocket.waitForReadyRead(30); // timeout = cc unused! - waitForReady(UdpSocket, 60000); //60 seconds + waitForReady(60000); //60 seconds // OLD CODE WITHOUT REDUNDANCY---------------------------------------------------- /* @@ -565,8 +617,7 @@ void UdpDataProtocol::run() mJackTrip->writeAudioBuffer(mAudioPacket); */ //---------------------------------------------------------------------------------- - receivePacketRedundancy(UdpSocket, - full_redundant_packet, + receivePacketRedundancy(full_redundant_packet, full_redundant_packet_size, full_packet_size, current_seq_num, @@ -576,7 +627,9 @@ void UdpDataProtocol::run() break; } case SENDER : { - while ( !mStopped ) + full_redundant_packet = new int8_t[full_redundant_packet_size]; + std::memset(full_redundant_packet, 0, full_redundant_packet_size); // Initialize to 0 + while ( !mStopped && !JackTrip::sSigInt && !JackTrip::sJackStopped ) { // OLD CODE WITHOUT REDUNDANCY ----------------------------------------------------- /* @@ -592,24 +645,34 @@ void UdpDataProtocol::run() full_redundant_packet_size, full_packet_size); } + + // Send exit packet (with 1 redundant packet). + cout << "sending exit packet" << endl; + QByteArray exitPacket = QByteArray(mControlPacketSize, 0xff); + sendPacket(exitPacket.constData(), mControlPacketSize); + sendPacket(exitPacket.constData(), mControlPacketSize); + emit signalCeaseTransmission(); break; } } + + if (NULL != full_redundant_packet) { + delete[] full_redundant_packet; + full_redundant_packet = NULL; + } } //******************************************************************************* //bool -void UdpDataProtocol::waitForReady(QUdpSocket& UdpSocket, int timeout_msec) +void UdpDataProtocol::waitForReady(int timeout_msec) { int loop_resolution_usec = 100; // usecs to wait on each loop int emit_resolution_usec = 10000; // 10 milliseconds int timeout_usec = timeout_msec * 1000; int elapsed_time_usec = 0; // Ellapsed time in milliseconds - while ( ( !( - UdpSocket.hasPendingDatagrams() && - (UdpSocket.pendingDatagramSize() > 0) - ) && (elapsed_time_usec <= timeout_usec) ) + while ( !datagramAvailable() + && (elapsed_time_usec <= timeout_usec) && !mStopped ){ // if (mStopped) { return false; } QThread::usleep(loop_resolution_usec); @@ -635,13 +698,13 @@ void UdpDataProtocol::printUdpWaitedTooLong(int wait_msec) int wait_time = 30; // msec if ( !(wait_msec%wait_time) ) { std::cerr << "UDP waiting too long (more than " << wait_time << "ms) for " << mPeerAddress.toString().toStdString() << "..." << endl; + emit signalUdpWaitingTooLong(); } } //******************************************************************************* -void UdpDataProtocol::receivePacketRedundancy(QUdpSocket& UdpSocket, - int8_t* full_redundant_packet, +void UdpDataProtocol::receivePacketRedundancy(int8_t* full_redundant_packet, int full_redundant_packet_size, int full_packet_size, uint16_t& current_seq_num, @@ -649,19 +712,41 @@ void UdpDataProtocol::receivePacketRedundancy(QUdpSocket& UdpSocket, uint16_t& newer_seq_num) { // This is blocking until we get a packet... - receivePacket( UdpSocket, reinterpret_cast<char*>(full_redundant_packet), - full_redundant_packet_size); + if (receivePacket( reinterpret_cast<char*>(full_redundant_packet), + full_redundant_packet_size) == 0) { + return; + } + + if (0.0 < mSimulatedLossRate || 0.0 < mSimulatedJitterRate) { + double x = mUniformDist(mRndEngine); + // Drop packets + x -= mSimulatedLossRate; + if (0 > x) { + return; + } + // Delay packets + x -= mSimulatedJitterRate; + if (0 > x) { + usleep(mUniformDist(mRndEngine) * mSimulatedJitterMaxDelay * 1e6); + } + } // Get Packet Sequence Number newer_seq_num = mJackTrip->getPeerSequenceNumber(full_redundant_packet); current_seq_num = newer_seq_num; - if (0 != last_seq_num) { - int16_t lost = newer_seq_num - last_seq_num - 1; - if (0 > lost) { + int16_t lost = 0; + if (!mInitialState) { + lost = newer_seq_num - last_seq_num - 1; + if (0 > lost || 1000 < lost) { // Out of order packet, should be ignored ++mOutOfOrderCount; + if (5 < ++mLastOutOfOrderCount) { + mInitialState = true; + mStatCount = 0; + mTotCount = 0; + } return; } else if (0 != lost) { @@ -669,6 +754,8 @@ void UdpDataProtocol::receivePacketRedundancy(QUdpSocket& UdpSocket, } mTotCount += 1 + lost; } + mLastOutOfOrderCount = 0; + mInitialState = false; //cout << current_seq_num << " "; int redun_last_index = 0; @@ -687,15 +774,38 @@ void UdpDataProtocol::receivePacketRedundancy(QUdpSocket& UdpSocket, mRevivedCount += redun_last_index; //cout << endl; + int peer_chans = mJackTrip->getPeerNumChannels(full_redundant_packet); + int N = mJackTrip->getPeerBufferSize(full_redundant_packet); + int host_buf_size = N * mChans * mSmplSize; + int hdr_size = mJackTrip->getHeaderSizeInBytes(); + int gap_size = mInitialState ? 0 : (lost - redun_last_index) * host_buf_size; + last_seq_num = newer_seq_num; // Save last read packet + if ((int)mBuffer.size() < host_buf_size) { + mBuffer.resize(host_buf_size, 0); + } // Send to audio all available audio packets, in order for (int i = redun_last_index; i>=0; i--) { - memcpy(mFullPacket, - full_redundant_packet + (i*full_packet_size), - full_packet_size); - mJackTrip->parseAudioPacket(mFullPacket, mAudioPacket); - mJackTrip->writeAudioBuffer(mAudioPacket); + int8_t* src = full_redundant_packet + (i*full_packet_size) + hdr_size; + if (1 != mChans) { + // Convert packet's non-interleaved layout to interleaved one used internally + int8_t* dst = mBuffer.data(); + int C = qMin(mChans, peer_chans); + for (int n=0; n<N; ++n) { + for (int c=0; c<C; ++c) { + memcpy(dst + (n*mChans + c)*mSmplSize, src + (n + c*N)*mSmplSize, mSmplSize); + } + } + src = dst; + } + if (!mJackTrip->writeAudioBuffer(src, host_buf_size, gap_size)) { + emit signalError("Local and Peer buffer settings are incompatible"); + cout << "ERROR: Local and Peer buffer settings are incompatible" << endl; + mStopped = true; + break; + } + gap_size = 0; } } @@ -716,12 +826,39 @@ bool UdpDataProtocol::getStats(DataProtocol::PktStat* stat) } //******************************************************************************* +void UdpDataProtocol::setIssueSimulation(double loss, double jitter, double max_delay) +{ + mSimulatedLossRate = loss; + mSimulatedJitterRate = jitter; + mSimulatedJitterMaxDelay = max_delay; + + std::random_device r; + mRndEngine = std::default_random_engine(r()); + mUniformDist = std::uniform_real_distribution<double>(0.0, 1.0); + + cout << "Simulating network issues: " + "loss_rate=" << loss << ", jitter_rate=" << jitter << ", jitter_max_delay=" << max_delay << endl; +} + +//******************************************************************************* void UdpDataProtocol::sendPacketRedundancy(int8_t* full_redundant_packet, int full_redundant_packet_size, int full_packet_size) { mJackTrip->readAudioBuffer( mAudioPacket ); - mJackTrip->putHeaderInPacket(mFullPacket, mAudioPacket); + int8_t* src = mAudioPacket; + if (1 != mChans) { + // Convert internal interleaved layout to non-interleaved + int N = getAudioPacketSizeInBites() / mChans / mSmplSize; + int8_t* dst = mBuffer.data(); + for (int n=0; n<N; ++n) { + for (int c=0; c<mChans; ++c) { + memcpy(dst + (n + c*N)*mSmplSize, src + (n*mChans + c)*mSmplSize, mSmplSize); + } + } + src = dst; + } + mJackTrip->putHeaderInPacket(mFullPacket, src); // Move older packets to end of array of redundant packets std::memmove(full_redundant_packet+full_packet_size, @@ -784,3 +921,32 @@ void UdpDataProtocol::sendPacketRedundancy(int8_t* full_redundant_packet, If it has more than one packet that it hasn't yet received, it sends it to the soundcard one by one. */ + +bool UdpDataProtocol::datagramAvailable() +{ + //Currently using a simplified version of the way QUdpSocket checks for datagrams. + //TODO: Consider changing to use poll() or select(). + char c; +#if defined (__WIN_32__) + //Need to use the winsock version of the function for MSG_PEEK + WSABUF buffer; + buffer.buf = &c; + buffer.len = sizeof(c); + DWORD n = 0; + DWORD flags = MSG_PEEK; + int ret = WSARecv(mSocket, &buffer, 1, &n, &flags, NULL, NULL); + if (ret == 0) { + //True if no error, + return true; + } else { + //or if our error is that our buffer is too small. + int err = WSAGetLastError(); + return (err == WSAEMSGSIZE); + } +#else + ssize_t n; + n = ::recv(mSocket, &c, sizeof(c), MSG_PEEK); + //We have a datagram if our buffer is too small or if no error. + return (n != -1 || errno == EMSGSIZE); +#endif +} diff --git a/src/UdpDataProtocol.h b/src/UdpDataProtocol.h index 13d58f9..f6f570c 100644 --- a/src/UdpDataProtocol.h +++ b/src/UdpDataProtocol.h @@ -41,9 +41,10 @@ #include <stdexcept> #include <QThread> -#include <QUdpSocket> #include <QHostAddress> #include <QMutex> +#include <vector> +#include <random> #include "DataProtocol.h" #include "jacktrip_types.h" @@ -104,7 +105,7 @@ public: * \return number of bytes read, -1 on error */ //virtual int receivePacket(char* buf, const size_t n); - virtual int receivePacket(QUdpSocket& UdpSocket, char* buf, const size_t n); + virtual int receivePacket(char* buf, const size_t n); /** \brief Sends a packet * @@ -121,8 +122,7 @@ public: * \param peerHostAddress QHostAddress to store the peer address * \param port Receiving port */ - virtual void getPeerAddressFromFirstPacket(QUdpSocket& UdpSocket, - QHostAddress& peerHostAddress, + virtual void getPeerAddressFromFirstPacket(QHostAddress& peerHostAddress, uint16_t& port); /** \brief Sets the bind port number @@ -143,17 +143,18 @@ public: virtual void run(); virtual bool getStats(PktStat* stat); + virtual void setIssueSimulation(double loss, double jitter, double max_delay); private slots: void printUdpWaitedTooLong(int wait_msec); - + signals: /// \brief Signals when waiting every 10 milliseconds, with the total wait on wait_msec /// \param wait_msec Total wait in milliseconds void signalWaitingTooLong(int wait_msec); - + void signalUdpWaitingTooLong(); //private: protected: @@ -167,7 +168,7 @@ protected: #endif /** \brief This function blocks until data is available for reading in the - * QUdpSocket. The function will timeout after timeout_msec microseconds. + * socket. The function will timeout after timeout_msec microseconds. * * This function is intended to replace QAbstractSocket::waitForReadyRead which has * some problems with multithreading. @@ -175,12 +176,11 @@ protected: * \return returns true if there is data available for reading; * otherwise it returns false (if an error occurred or the operation timed out) */ - void waitForReady(QUdpSocket& UdpSocket, int timeout_msec); + void waitForReady(int timeout_msec); /** \brief Redundancy algorythm at the receiving end */ - virtual void receivePacketRedundancy(QUdpSocket& UdpSocket, - int8_t* full_redundant_packet, + virtual void receivePacketRedundancy(int8_t* full_redundant_packet, int full_redundant_packet_size, int full_packet_size, uint16_t& current_seq_num, @@ -193,9 +193,9 @@ protected: int full_redundant_packet_size, int full_packet_size); - private: - + bool datagramAvailable(); + int mBindPort; ///< Local Port number to Bind int mPeerPort; ///< Peer Port number const runModeT mRunMode; ///< Run mode, either SENDER or RECEIVER @@ -212,6 +212,11 @@ private: int8_t* mAudioPacket; ///< Buffer to store Audio Packets int8_t* mFullPacket; ///< Buffer to store Full Packet (audio+header) + std::vector<int8_t> mBuffer; + int mChans; + int mSmplSize; + int mLastOutOfOrderCount; + bool mInitialState; unsigned int mUdpRedundancyFactor; ///< Factor of redundancy static QMutex sUdpMutex; ///< Mutex to make thread safe the binding process @@ -221,6 +226,16 @@ private: std::atomic<uint32_t> mOutOfOrderCount; std::atomic<uint32_t> mRevivedCount; uint32_t mStatCount; + + uint8_t mControlPacketSize; + bool mStopSignalSent; + + // packet loss/jitter simulation + double mSimulatedLossRate; + double mSimulatedJitterRate; + double mSimulatedJitterMaxDelay; + std::default_random_engine mRndEngine; + std::uniform_real_distribution<double> mUniformDist; }; #endif // __UDPDATAPROTOCOL_H__ diff --git a/src/UdpHubListener.cpp b/src/UdpHubListener.cpp index 4cbd649..ec1b508 100644 --- a/src/UdpHubListener.cpp +++ b/src/UdpHubListener.cpp @@ -51,10 +51,12 @@ using std::cout; using std::endl; +bool UdpHubListener::sSigInt = false; //******************************************************************************* UdpHubListener::UdpHubListener(int server_port, int server_udp_port) : //mJTWorker(NULL), + mTcpServer(this), mServerPort(server_port), mServerUdpPort(server_udp_port),//final udp base port number mStopped(false), @@ -62,9 +64,12 @@ UdpHubListener::UdpHubListener(int server_port, int server_udp_port) : mWAIR(false), #endif // endwhere mTotalRunningThreads(0), - m_connectDefaultAudioPorts(false) + mHubPatchDescriptions({"server-to-clients", "client loopback", "client fan out/in but not loopback", + "reserved for TUB", "full mix", "no auto patching"}), + m_connectDefaultAudioPorts(false), + mIOStatTimeout(0) { - // Register JackTripWorker with the master listener + // Register JackTripWorker with the hub listener //mJTWorker = new JackTripWorker(this); mJTWorkers = new QVector<JackTripWorker*>; for (int i = 0; i<gMaxThreads; i++) { @@ -88,7 +93,7 @@ UdpHubListener::UdpHubListener(int server_port, int server_udp_port) : // SoundWIRE ports open are UDP 61002-62000 // (server_port - gDefaultPort) apply TCP offset to UDP too - if (mServerUdpPort != NULL){ + if (mServerUdpPort != 0){ mBasePort = mServerUdpPort; } else { mBasePort = 61002 + (server_port - gDefaultPort); @@ -98,6 +103,14 @@ UdpHubListener::UdpHubListener(int server_port, int server_udp_port) : mUnderRunMode = JackTrip::WAVETABLE; mBufferQueueLength = gDefaultQueueLength; + + mBufferStrategy = 1; + mBroadcastQueue = 0; + mSimulatedLossRate = 0.0; + mSimulatedJitterRate = 0.0; + mSimulatedDelayRel = 0.0; + + mUseRtUdpPriority = false; } @@ -119,149 +132,161 @@ UdpHubListener::~UdpHubListener() // the client is already on the thread pool, it means that a new connection is // requested (the old was desconnected). So we have to remove that thread from // the pool and then connect again. -void UdpHubListener::run() +void UdpHubListener::start() { mStopped = false; - QHostAddress PeerAddress; // Object to store peer address - uint16_t peer_udp_port; // Peer listening port - int server_udp_port; // Server assigned udp port - - // Create and bind the TCP server + // Bind the TCP server // ------------------------------ - QTcpServer TcpServer; - if ( !TcpServer.listen(QHostAddress::Any, mServerPort) ) { - std::cerr << "TCP Socket Server ERROR on Port " << mServerPort << ": " << TcpServer.errorString().toStdString() << endl; - std::exit(1); + QObject::connect(&mTcpServer, &QTcpServer::newConnection, this, &UdpHubListener::receivedNewConnection); + if ( !mTcpServer.listen(QHostAddress::Any, mServerPort) ) { + QString error_message = QString("TCP Socket Server on Port %1 ERROR: %2").arg(mServerPort).arg(mTcpServer.errorString()); + std::cerr << error_message.toStdString() << endl; + emit signalError(error_message); + return; } + + cout << "JackTrip HUB SERVER: Waiting for client connections..." << endl; + cout << "JackTrip HUB SERVER: Hub auto audio patch setting = " << mHubPatch + << " (" << mHubPatchDescriptions.at(mHubPatch).toStdString() << ")" << endl; + cout << "=======================================================" << endl; + + // Start our monitoring timer + mStopCheckTimer.setInterval(200); + connect(&mStopCheckTimer, &QTimer::timeout, this, &UdpHubListener::stopCheck); + mStopCheckTimer.start(); +} + +void UdpHubListener::receivedNewConnection() +{ + QTcpSocket *clientSocket = mTcpServer.nextPendingConnection(); + connect(clientSocket, &QAbstractSocket::readyRead, this, [=]{ + receivedClientInfo(clientSocket); + }); + cout << "JackTrip HUB SERVER: Client Connection Received!" << endl; +} - const int tcpTimeout = 5*1000; - - - cout << "JackTrip HUB SERVER: TCP Server Listening in Port = " << TcpServer.serverPort() << endl; - while ( !mStopped ) - { - cout << "JackTrip HUB SERVER: Waiting for client connections..." << endl; - if(m_connectDefaultAudioPorts) - { - cout << "JackTrip HUB SERVER: Hub auto audio patch setting = " << mHubPatch << endl; - } else { - cout << "JackTrip HUB SERVER: Hub auto audio patch disabled " << endl; +void UdpHubListener::receivedClientInfo(QTcpSocket *clientConnection) +{ + QHostAddress PeerAddress = clientConnection->peerAddress(); + cout << "JackTrip HUB SERVER: Client Connect Received from Address : " + << PeerAddress.toString().toStdString() << endl; + + // Get UDP port from client + // ------------------------ + QString clientName = QString(); + cout << "JackTrip HUB SERVER: Reading UDP port from Client..." << endl; + if (clientConnection->bytesAvailable() < (qint64)sizeof(uint16_t)) { + // We don't have enough data. Wait for the next readyRead notification. + return; + } + uint16_t peer_udp_port= readClientUdpPort(clientConnection, clientName); + + cout << "JackTrip HUB SERVER: Client UDP Port is = " << peer_udp_port << endl; + if ( peer_udp_port == 0 || peer_udp_port < gBindPortLow || peer_udp_port > gBindPortHigh ) { + cout << "JackTrip HUB SERVER: Exiting " << endl; + clientConnection->close(); + clientConnection->deleteLater(); + return; + } + + // Check is client is new or not + // ----------------------------- + // Check if Address is not already in the thread pool + // check by comparing address strings (To handle IPv4 and IPv6.) + int id = isNewAddress(PeerAddress.toString(), peer_udp_port); + // If the address is not new, we need to remove the client from the pool + // before re-starting the connection + + if (id == -1) { + int id_remove; + id_remove = getPoolID(PeerAddress.toString(), peer_udp_port); + // stop the thread + mJTWorkers->at(id_remove)->stopThread(); + // block until the thread has been removed from the pool + while ( isNewAddress(PeerAddress.toString(), peer_udp_port) == -1 ) { + cout << "JackTrip HUB SERVER: Removing JackTripWorker from pool..." << endl; + QThread::msleep(10); } - cout << "=======================================================" << endl; - while ( !TcpServer.hasPendingConnections() && !TcpServer.waitForNewConnection(1000) ) - { if (mStopped) { return; } } // block until a new connection is received - cout << "JackTrip HUB SERVER: Client Connection Received!" << endl; - - // Control loop to be able to exit if UDPs or TCPs error ocurr - for (int dum = 0; dum<1; dum++) { - QTcpSocket *clientConnection = TcpServer.nextPendingConnection(); - if ( !clientConnection->waitForConnected(tcpTimeout) ) { - std::cerr << clientConnection->errorString().toStdString() << endl; - break; - } - PeerAddress = clientConnection->peerAddress(); - cout << "JackTrip HUB SERVER: Client Connect Received from Address : " - << PeerAddress.toString().toStdString() << endl; - - // Get UDP port from client - // ------------------------ - peer_udp_port = readClientUdpPort(clientConnection); - if ( peer_udp_port == 0 ) { - cout << "JackTrip HUB SERVER: Client UDP Port is = " << peer_udp_port << endl; - cout << "JackTrip HUB SERVER: Exiting " << endl; - break; - } - if ( peer_udp_port < gBindPortLow ) { - cout << "JackTrip HUB SERVER: Client UDP Port is = " << peer_udp_port << endl; - cout << "JackTrip HUB SERVER: Exiting " << endl; - break; - } - if ( peer_udp_port > gBindPortHigh ) { - cout << "JackTrip HUB SERVER: Client UDP Port is = " << peer_udp_port << endl; - cout << "JackTrip HUB SERVER: Exiting " << endl; - break; - } - cout << "JackTrip HUB SERVER: Client UDP Port is = " << peer_udp_port << endl; - - // Check is client is new or not - // ----------------------------- - // Check if Address is not already in the thread pool - // check by comparing address strings (To handle IPv4 and IPv6.) - int id = isNewAddress(PeerAddress.toString(), peer_udp_port); - // If the address is not new, we need to remove the client from the pool - // before re-starting the connection - - if (id == -1) { - int id_remove; - id_remove = getPoolID(PeerAddress.toString(), peer_udp_port); - // stop the thread - mJTWorkers->at(id_remove)->stopThread(); - // block until the thread has been removed from the pool - while ( isNewAddress(PeerAddress.toString(), peer_udp_port) == -1 ) { - cout << "JackTrip HUB SERVER: Removing JackTripWorker from pool..." << endl; - QThread::msleep(10); - } - // Get a new ID for this client - //id = isNewAddress(PeerAddress.toIPv4Address(), peer_udp_port); - id = getPoolID(PeerAddress.toString(), peer_udp_port); - } - // Assign server port and send it to Client - server_udp_port = mBasePort+id; - cout << "JackTrip HUB SERVER: Sending Final UDP Port to Client: " << server_udp_port << endl; - - if ( sendUdpPort(clientConnection, server_udp_port) == 0 ) { - clientConnection->close(); - delete clientConnection; - releaseThread(id); - break; - } - - // Close and Delete the socket - // --------------------------- - clientConnection->close(); - delete clientConnection; - cout << "JackTrip HUB SERVER: Client TCP Connection Closed!" << endl; - - // Spawn Thread to Pool - // -------------------- - // Register JackTripWorker with the master listener - delete mJTWorkers->at(id); // just in case the Worker was previously created - mJTWorkers->replace(id, new JackTripWorker(this, mBufferQueueLength, mUnderRunMode)); - // redirect port and spawn listener - cout << "JackTrip HUB SERVER: Spawning JackTripWorker..." << endl; - { - QMutexLocker lock(&mMutex); - mJTWorkers->at(id)->setJackTrip(id, - mActiveAddress[id].address, - server_udp_port, - mActiveAddress[id].port, - 1, - m_connectDefaultAudioPorts - ); /// \todo temp default to 1 channel - -// qDebug() << "mPeerAddress" << id << mActiveAddress[id].address << mActiveAddress[id].port; - } - //send one thread to the pool - cout << "JackTrip HUB SERVER: Starting JackTripWorker..." << endl; - mThreadPool.start(mJTWorkers->at(id), QThread::TimeCriticalPriority); - // wait until one is complete before another spawns - while (mJTWorkers->at(id)->isSpawning()) { QThread::msleep(10); } - //mTotalRunningThreads++; - cout << "JackTrip HUB SERVER: Total Running Threads: " << mTotalRunningThreads << endl; - cout << "===============================================================" << endl; - QThread::msleep(100); + // Get a new ID for this client + //id = isNewAddress(PeerAddress.toIPv4Address(), peer_udp_port); + id = getPoolID(PeerAddress.toString(), peer_udp_port); + } + // Assign server port and send it to Client + int server_udp_port = mBasePort+id; + cout << "JackTrip HUB SERVER: Sending Final UDP Port to Client: " << server_udp_port << endl; + + if ( sendUdpPort(clientConnection, server_udp_port) == 0 ) { + clientConnection->close(); + clientConnection->deleteLater(); + releaseThread(id); + return; + } + + // Close and mark socket for deletion + // ---------------------------------- + clientConnection->close(); + clientConnection->deleteLater(); + cout << "JackTrip HUB SERVER: Client TCP Connection Closed!" << endl; + + // Spawn Thread to Pool + // -------------------- + // Register JackTripWorker with the hub listener + delete mJTWorkers->at(id); // just in case the Worker was previously created + mJTWorkers->replace(id, new JackTripWorker(this, mBufferQueueLength, mUnderRunMode, clientName)); + if (mIOStatTimeout > 0) { + mJTWorkers->at(id)->setIOStatTimeout(mIOStatTimeout); + mJTWorkers->at(id)->setIOStatStream(mIOStatStream); + } + mJTWorkers->at(id)->setBufferStrategy(mBufferStrategy); + mJTWorkers->at(id)->setNetIssuesSimulation(mSimulatedLossRate, + mSimulatedJitterRate, mSimulatedDelayRel); + mJTWorkers->at(id)->setBroadcast(mBroadcastQueue); + mJTWorkers->at(id)->setUseRtUdpPriority(mUseRtUdpPriority); + // redirect port and spawn listener + cout << "JackTrip HUB SERVER: Spawning JackTripWorker..." << endl; + { + QMutexLocker lock(&mMutex); + mJTWorkers->at(id)->setJackTrip(id, + mActiveAddress[id].address, + server_udp_port, + mActiveAddress[id].port, + 1, + m_connectDefaultAudioPorts + ); /// \todo temp default to 1 channel + + //qDebug() << "mPeerAddress" << id << mActiveAddress[id].address << mActiveAddress[id].port; + } + //send one thread to the pool + cout << "JackTrip HUB SERVER: Starting JackTripWorker..." << endl; + mThreadPool.start(mJTWorkers->at(id), QThread::TimeCriticalPriority); + // wait until one is complete before another spawns + while (mJTWorkers->at(id)->isSpawning()) { QThread::msleep(10); } + //mTotalRunningThreads++; + cout << "JackTrip HUB SERVER: Total Running Threads: " << mTotalRunningThreads << endl; + cout << "===============================================================" << endl; + QThread::msleep(100); #ifdef WAIR // WAIR - if (isWAIR()) connectMesh(true); // invoked with -Sw + if (isWAIR()) connectMesh(true); // invoked with -Sw #endif // endwhere -// qDebug() << "mPeerAddress" << mActiveAddress[id].address << mActiveAddress[id].port; + //qDebug() << "mPeerAddress" << mActiveAddress[id].address << mActiveAddress[id].port; - connectPatch(true); - } + connectPatch(true); +} + +void UdpHubListener::stopCheck() +{ + if (mStopped || sSigInt) { + cout << "JackTrip HUB SERVER: Stopped" << endl; + mStopCheckTimer.stop(); + mTcpServer.close(); + stopAllThreads(); + emit signalStopped(); } +} - /* + /* From Old Runloop code // Create objects on the stack QUdpSocket HubUdpSocket; QHostAddress PeerAddress; @@ -310,32 +335,26 @@ void UdpHubListener::run() QThread::msleep(100); } */ -} - //******************************************************************************* // Returns 0 on error -uint16_t UdpHubListener::readClientUdpPort(QTcpSocket* clientConnection) +uint16_t UdpHubListener::readClientUdpPort(QTcpSocket* clientConnection, QString &clientName) { - // Read the size of the package - // ---------------------------- - //tcpClient.waitForReadyRead(); - cout << "JackTrip HUB SERVER: Reading UDP port from Client..." << endl; - while (clientConnection->bytesAvailable() < (qint64)sizeof(uint16_t)) { - if (!clientConnection->waitForReadyRead()) { - std::cerr << "TCP Socket ERROR: " << clientConnection->errorString().toStdString() << endl; - return 0; - } - } - if (gVerboseFlag) cout << "Ready To Read From Client!" << endl; // Read UDP Port Number from Server // -------------------------------- - uint16_t udp_port = 0; + uint16_t udp_port; qint64 size = sizeof(udp_port); char port_buf[size]; clientConnection->read(port_buf, size); std::memcpy(&udp_port, port_buf, size); + + if (clientConnection->bytesAvailable() == gMaxRemoteNameLength) { + char name_buf[gMaxRemoteNameLength]; + clientConnection->read(name_buf, gMaxRemoteNameLength); + clientName = QString::fromUtf8((const char *)name_buf); + } + return udp_port; } @@ -357,7 +376,7 @@ int UdpHubListener::sendUdpPort(QTcpSocket* clientConnection, int udp_port) } } return 1; - cout << "Port sent to Client" << endl; + //cout << "Port sent to Client" << endl; } @@ -489,11 +508,12 @@ void UdpHubListener::enumerateRunningThreadIDs() #include "JMess.h" void UdpHubListener::connectPatch(bool spawn) { - if(m_connectDefaultAudioPorts) { - cout << ((spawn)?"spawning":"releasing") << " jacktripWorker so change patch" << endl; - } else { - cout << ((spawn)?"spawning":"releasing") << " jacktripWorker" << endl; + if ((getHubPatch() == JackTrip::NOAUTO) || + (getHubPatch() == JackTrip::SERVERTOCLIENT && !m_connectDefaultAudioPorts)) { + cout << ((spawn)?"spawning":"releasing") << " jacktripWorker (auto hub patching disabled)" << endl; + return; } + cout << ((spawn)?"spawning":"releasing") << " jacktripWorker so change patch" << endl; JMess tmp; // default is patch 0, which connects server audio to all clients // these are the other cases: @@ -506,5 +526,17 @@ void UdpHubListener::connectPatch(bool spawn) // FIXME: need change to gDefaultNumInChannels if more than stereo } +void UdpHubListener::stopAllThreads() +{ + QVectorIterator<JackTripWorker*> iterator(*mJTWorkers); + while (iterator.hasNext()) { + if (iterator.peekNext() != nullptr) { + iterator.next()->stopThread(); + } else { + iterator.next(); + } + } + mThreadPool.waitForDone(); +} // TODO: // USE bool QAbstractSocket::isValid () const to check if socket is connect. if not, exit loop diff --git a/src/UdpHubListener.h b/src/UdpHubListener.h index 0bc0a8c..763cfea 100644 --- a/src/UdpHubListener.h +++ b/src/UdpHubListener.h @@ -40,6 +40,7 @@ #include <iostream> #include <stdexcept> +#include <fstream> #include <QThread> #include <QThreadPool> @@ -65,17 +66,16 @@ typedef struct { * This creates a server that will listen on the well know port (the server port) and will * spawn JackTrip threads into the Thread pool. Clients request a connection. */ -class UdpHubListener : public QThread +class UdpHubListener : public QObject { Q_OBJECT; public: - UdpHubListener(int server_port = gServerUdpPort, int server_udp_port = NULL); + UdpHubListener(int server_port = gServerUdpPort, int server_udp_port = 0); virtual ~UdpHubListener(); - /// \brief Implements the Thread Loop. To start the thread, call start() - /// ( DO NOT CALL run() ) - void run(); + /// \brief Starts the TCP server + void start(); /// \brief Stops the execution of the Thread void stop() { mStopped = true; } @@ -83,28 +83,33 @@ public: int releaseThread(int id); void setConnectDefaultAudioPorts(bool connectDefaultAudioPorts) { m_connectDefaultAudioPorts = connectDefaultAudioPorts; } - - void setSettings(Settings* s) {m_settings = s;} - Settings* getSettings() const {return m_settings;} + + static void sigIntHandler(__attribute__((unused)) int unused) + { std::cout << std::endl << "Shutting Down..." << std::endl; sSigInt = true; } private slots: void testReceive() { std::cout << "========= TEST RECEIVE SLOT ===========" << std::endl; } + void receivedNewConnection(); + void stopCheck(); signals: void Listening(); void ClientAddressSet(); void signalRemoveThread(int id); - + void signalStopped(); + void signalError(const QString &errorMessage); private: /** \brief Binds a QUdpSocket. It chooses the available (active) interface. * \param udpsocket a QUdpSocket * \param port Port number */ + void receivedClientInfo(QTcpSocket *clientConnection); + static void bindUdpSocket(QUdpSocket& udpsocket, int port); - uint16_t readClientUdpPort(QTcpSocket* clientConnection); + uint16_t readClientUdpPort(QTcpSocket* clientConnection, QString &clientName); int sendUdpPort(QTcpSocket* clientConnection, int udp_port); @@ -124,6 +129,8 @@ private: * is not in the pool yet, returns -1. */ int getPoolID(QString address, uint16_t port); + + void stopAllThreads(); //QUdpSocket mUdpHubSocket; ///< The UDP socket //QHostAddress mPeerAddress; ///< The Peer Address @@ -132,6 +139,7 @@ private: QVector<JackTripWorker*>* mJTWorkers; ///< Vector of JackTripWorker s QThreadPool mThreadPool; ///< The Thread Pool + QTcpServer mTcpServer; int mServerPort; //< Server known port number int mServerUdpPort; //< Server udp base port number int mBasePort; @@ -140,14 +148,26 @@ private: /// Boolean stop the execution of the thread volatile bool mStopped; + static bool sSigInt; + QTimer mStopCheckTimer; int mTotalRunningThreads; ///< Number of Threads running in the pool QMutex mMutex; JackTrip::underrunModeT mUnderRunMode; int mBufferQueueLength; - + + QStringList mHubPatchDescriptions; bool m_connectDefaultAudioPorts; - Settings* m_settings; + int mIOStatTimeout; + QSharedPointer<std::ofstream> mIOStatStream; + + int mBufferStrategy; + int mBroadcastQueue; + double mSimulatedLossRate; + double mSimulatedJitterRate; + double mSimulatedDelayRel; + bool mUseRtUdpPriority; + #ifdef WAIR // wair bool mWAIR; void connectMesh(bool spawn); @@ -164,6 +184,20 @@ public : void setUnderRunMode(JackTrip::underrunModeT UnderRunMode) { mUnderRunMode = UnderRunMode; } void setBufferQueueLength(int BufferQueueLength) { mBufferQueueLength = BufferQueueLength; } + + void setIOStatTimeout(int timeout) { mIOStatTimeout = timeout; } + void setIOStatStream(QSharedPointer<std::ofstream> statStream) { mIOStatStream = statStream; } + + void setBufferStrategy(int BufferStrategy) { mBufferStrategy = BufferStrategy; } + void setNetIssuesSimulation(double loss, double jitter, double delay_rel) + { + mSimulatedLossRate = loss; + mSimulatedJitterRate = jitter; + mSimulatedDelayRel = delay_rel; + } + void setBroadcast(int broadcast_queue) {mBroadcastQueue = broadcast_queue;} + void setUseRtUdpPriority(bool use) {mUseRtUdpPriority = use;} + }; @@ -37,8 +37,8 @@ if [[ $1 == 'nojack' ]]; then $QCMD -spec $QSPEC -config nojack ../src/jacktrip.pro make release else - $QCMD -spec $QSPEC ../src/jacktrip.pro + $QCMD -spec $QSPEC ../src/jacktrip.pro $@ make clean - $QCMD -spec $QSPEC ../src/jacktrip.pro + $QCMD -spec $QSPEC ../src/jacktrip.pro $@ make release fi diff --git a/src/compressordsp.h b/src/compressordsp.h new file mode 100644 index 0000000..26b37ac --- /dev/null +++ b/src/compressordsp.h @@ -0,0 +1,1816 @@ +/* ------------------------------------------------------------ +author: "Julius Smith" +license: "MIT Style STK-4.2" +name: "compressor" +version: "0.0" +Code generated with Faust 2.28.6 (https://faust.grame.fr) +Compilation options: -lang cpp -inpl -scal -ftz 0 +------------------------------------------------------------ */ + +#ifndef __compressordsp_H__ +#define __compressordsp_H__ + +// NOTE: ANY INCLUDE-GUARD HERE MUST BE DERIVED FROM THE CLASS NAME +// +// faust2header.cpp - FAUST Architecture File +// This is a simple variation of matlabplot.cpp in the Faust distribution +// aimed at creating a simple C++ header file (.h) containing a Faust DSP. +// See the Makefile for how to use it. + +/************************** BEGIN dsp.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __dsp__ +#define __dsp__ + +#include <string> +#include <vector> + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +struct UI; +struct Meta; + +/** + * DSP memory manager. + */ + +struct dsp_memory_manager { + + virtual ~dsp_memory_manager() {} + + virtual void* allocate(size_t size) = 0; + virtual void destroy(void* ptr) = 0; + +}; + +/** +* Signal processor definition. +*/ + +class dsp { + + public: + + dsp() {} + virtual ~dsp() {} + + /* Return instance number of audio inputs */ + virtual int getNumInputs() = 0; + + /* Return instance number of audio outputs */ + virtual int getNumOutputs() = 0; + + /** + * Trigger the ui_interface parameter with instance specific calls + * to 'openTabBox', 'addButton', 'addVerticalSlider'... in order to build the UI. + * + * @param ui_interface - the user interface builder + */ + virtual void buildUserInterface(UI* ui_interface) = 0; + + /* Returns the sample rate currently used by the instance */ + virtual int getSampleRate() = 0; + + /** + * Global init, calls the following methods: + * - static class 'classInit': static tables initialization + * - 'instanceInit': constants and instance state initialization + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void init(int sample_rate) = 0; + + /** + * Init instance state + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void instanceInit(int sample_rate) = 0; + + /** + * Init instance constant state + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void instanceConstants(int sample_rate) = 0; + + /* Init default control parameters values */ + virtual void instanceResetUserInterface() = 0; + + /* Init instance state (delay lines...) */ + virtual void instanceClear() = 0; + + /** + * Return a clone of the instance. + * + * @return a copy of the instance on success, otherwise a null pointer. + */ + virtual dsp* clone() = 0; + + /** + * Trigger the Meta* parameter with instance specific calls to 'declare' (key, value) metadata. + * + * @param m - the Meta* meta user + */ + virtual void metadata(Meta* m) = 0; + + /** + * DSP instance computation, to be called with successive in/out audio buffers. + * + * @param count - the number of frames to compute + * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) + * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) + * + */ + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0; + + /** + * DSP instance computation: alternative method to be used by subclasses. + * + * @param date_usec - the timestamp in microsec given by audio driver. + * @param count - the number of frames to compute + * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) + * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) + * + */ + virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); } + +}; + +/** + * Generic DSP decorator. + */ + +class decorator_dsp : public dsp { + + protected: + + dsp* fDSP; + + public: + + decorator_dsp(dsp* dsp = nullptr):fDSP(dsp) {} + virtual ~decorator_dsp() { delete fDSP; } + + virtual int getNumInputs() { return fDSP->getNumInputs(); } + virtual int getNumOutputs() { return fDSP->getNumOutputs(); } + virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); } + virtual int getSampleRate() { return fDSP->getSampleRate(); } + virtual void init(int sample_rate) { fDSP->init(sample_rate); } + virtual void instanceInit(int sample_rate) { fDSP->instanceInit(sample_rate); } + virtual void instanceConstants(int sample_rate) { fDSP->instanceConstants(sample_rate); } + virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); } + virtual void instanceClear() { fDSP->instanceClear(); } + virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); } + virtual void metadata(Meta* m) { fDSP->metadata(m); } + // Beware: subclasses usually have to overload the two 'compute' methods + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); } + virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); } + +}; + +/** + * DSP factory class. + */ + +class dsp_factory { + + protected: + + // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory); + virtual ~dsp_factory() {} + + public: + + virtual std::string getName() = 0; + virtual std::string getSHAKey() = 0; + virtual std::string getDSPCode() = 0; + virtual std::string getCompileOptions() = 0; + virtual std::vector<std::string> getLibraryList() = 0; + virtual std::vector<std::string> getIncludePathnames() = 0; + + virtual dsp* createDSPInstance() = 0; + + virtual void setMemoryManager(dsp_memory_manager* manager) = 0; + virtual dsp_memory_manager* getMemoryManager() = 0; + +}; + +/** + * On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero) + * flags to avoid costly denormals. + */ + +#ifdef __SSE__ + #include <xmmintrin.h> + #ifdef __SSE2__ + #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040) + #else + #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000) + #endif +#else + #define AVOIDDENORMALS +#endif + +#endif +/************************** END dsp.h **************************/ + +/************************** BEGIN APIUI.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef API_UI_H +#define API_UI_H + +#include <sstream> +#include <string> +#include <vector> +#include <iostream> +#include <map> + +/************************** BEGIN meta.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __meta__ +#define __meta__ + +struct Meta +{ + virtual ~Meta() {}; + virtual void declare(const char* key, const char* value) = 0; + +}; + +#endif +/************************** END meta.h **************************/ +/************************** BEGIN UI.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2020 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __UI_H__ +#define __UI_H__ + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +/******************************************************************************* + * UI : Faust DSP User Interface + * User Interface as expected by the buildUserInterface() method of a DSP. + * This abstract class contains only the method that the Faust compiler can + * generate to describe a DSP user interface. + ******************************************************************************/ + +struct Soundfile; + +template <typename REAL> +struct UIReal +{ + UIReal() {} + virtual ~UIReal() {} + + // -- widget's layouts + + virtual void openTabBox(const char* label) = 0; + virtual void openHorizontalBox(const char* label) = 0; + virtual void openVerticalBox(const char* label) = 0; + virtual void closeBox() = 0; + + // -- active widgets + + virtual void addButton(const char* label, REAL* zone) = 0; + virtual void addCheckButton(const char* label, REAL* zone) = 0; + virtual void addVerticalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + virtual void addHorizontalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + virtual void addNumEntry(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + + // -- passive widgets + + virtual void addHorizontalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0; + virtual void addVerticalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0; + + // -- soundfiles + + virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) = 0; + + // -- metadata declarations + + virtual void declare(REAL* zone, const char* key, const char* val) {} +}; + +struct UI : public UIReal<FAUSTFLOAT> +{ + UI() {} + virtual ~UI() {} +}; + +#endif +/************************** END UI.h **************************/ +/************************** BEGIN PathBuilder.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef FAUST_PATHBUILDER_H +#define FAUST_PATHBUILDER_H + +#include <vector> +#include <string> +#include <algorithm> + +/******************************************************************************* + * PathBuilder : Faust User Interface + * Helper class to build complete hierarchical path for UI items. + ******************************************************************************/ + +class PathBuilder +{ + + protected: + + std::vector<std::string> fControlsLevel; + + public: + + PathBuilder() {} + virtual ~PathBuilder() {} + + std::string buildPath(const std::string& label) + { + std::string res = "/"; + for (size_t i = 0; i < fControlsLevel.size(); i++) { + res += fControlsLevel[i]; + res += "/"; + } + res += label; + std::replace(res.begin(), res.end(), ' ', '_'); + return res; + } + + std::string buildLabel(std::string label) + { + std::replace(label.begin(), label.end(), ' ', '_'); + return label; + } + + void pushLabel(const std::string& label) { fControlsLevel.push_back(label); } + void popLabel() { fControlsLevel.pop_back(); } + +}; + +#endif // FAUST_PATHBUILDER_H +/************************** END PathBuilder.h **************************/ +/************************** BEGIN ValueConverter.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __ValueConverter__ +#define __ValueConverter__ + +/*************************************************************************************** + ValueConverter.h + (GRAME, Copyright 2015-2019) + +Set of conversion objects used to map user interface values (for example a gui slider +delivering values between 0 and 1) to faust values (for example a vslider between +20 and 20000) using a log scale. + +-- Utilities + +Range(lo,hi) : clip a value x between lo and hi +Interpolator(lo,hi,v1,v2) : Maps a value x between lo and hi to a value y between v1 and v2 +Interpolator3pt(lo,mi,hi,v1,vm,v2) : Map values between lo mid hi to values between v1 vm v2 + +-- Value Converters + +ValueConverter::ui2faust(x) +ValueConverter::faust2ui(x) + +-- ValueConverters used for sliders depending of the scale + +LinearValueConverter(umin, umax, fmin, fmax) +LinearValueConverter2(lo, mi, hi, v1, vm, v2) using 2 segments +LogValueConverter(umin, umax, fmin, fmax) +ExpValueConverter(umin, umax, fmin, fmax) + +-- ValueConverters used for accelerometers based on 3 points + +AccUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 0 +AccDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 1 +AccUpDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 2 +AccDownUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 3 + +-- lists of ZoneControl are used to implement accelerometers metadata for each axes + +ZoneControl(zone, valueConverter) : a zone with an accelerometer data converter + +-- ZoneReader are used to implement screencolor metadata + +ZoneReader(zone, valueConverter) : a zone with a data converter + +****************************************************************************************/ + +#include <float.h> +#include <algorithm> // std::max +#include <cmath> +#include <vector> +#include <assert.h> + +//-------------------------------------------------------------------------------------- +// Interpolator(lo,hi,v1,v2) +// Maps a value x between lo and hi to a value y between v1 and v2 +// y = v1 + (x-lo)/(hi-lo)*(v2-v1) +// y = v1 + (x-lo) * coef with coef = (v2-v1)/(hi-lo) +// y = v1 + x*coef - lo*coef +// y = v1 - lo*coef + x*coef +// y = offset + x*coef with offset = v1 - lo*coef +//-------------------------------------------------------------------------------------- +class Interpolator +{ + private: + + //-------------------------------------------------------------------------------------- + // Range(lo,hi) clip a value between lo and hi + //-------------------------------------------------------------------------------------- + struct Range + { + double fLo; + double fHi; + + Range(double x, double y) : fLo(std::min<double>(x,y)), fHi(std::max<double>(x,y)) {} + double operator()(double x) { return (x<fLo) ? fLo : (x>fHi) ? fHi : x; } + }; + + + Range fRange; + double fCoef; + double fOffset; + + public: + + Interpolator(double lo, double hi, double v1, double v2) : fRange(lo,hi) + { + if (hi != lo) { + // regular case + fCoef = (v2-v1)/(hi-lo); + fOffset = v1 - lo*fCoef; + } else { + // degenerate case, avoids division by zero + fCoef = 0; + fOffset = (v1+v2)/2; + } + } + double operator()(double v) + { + double x = fRange(v); + return fOffset + x*fCoef; + } + + void getLowHigh(double& amin, double& amax) + { + amin = fRange.fLo; + amax = fRange.fHi; + } +}; + +//-------------------------------------------------------------------------------------- +// Interpolator3pt(lo,mi,hi,v1,vm,v2) +// Map values between lo mid hi to values between v1 vm v2 +//-------------------------------------------------------------------------------------- +class Interpolator3pt +{ + + private: + + Interpolator fSegment1; + Interpolator fSegment2; + double fMid; + + public: + + Interpolator3pt(double lo, double mi, double hi, double v1, double vm, double v2) : + fSegment1(lo, mi, v1, vm), + fSegment2(mi, hi, vm, v2), + fMid(mi) {} + double operator()(double x) { return (x < fMid) ? fSegment1(x) : fSegment2(x); } + + void getMappingValues(double& amin, double& amid, double& amax) + { + fSegment1.getLowHigh(amin, amid); + fSegment2.getLowHigh(amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Abstract ValueConverter class. Converts values between UI and Faust representations +//-------------------------------------------------------------------------------------- +class ValueConverter +{ + + public: + + virtual ~ValueConverter() {} + virtual double ui2faust(double x) = 0; + virtual double faust2ui(double x) = 0; +}; + +//-------------------------------------------------------------------------------------- +// A converter than can be updated +//-------------------------------------------------------------------------------------- + +class UpdatableValueConverter : public ValueConverter { + + protected: + + bool fActive; + + public: + + UpdatableValueConverter():fActive(true) + {} + virtual ~UpdatableValueConverter() + {} + + virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) = 0; + virtual void getMappingValues(double& amin, double& amid, double& amax) = 0; + + void setActive(bool on_off) { fActive = on_off; } + bool getActive() { return fActive; } + +}; + + +//-------------------------------------------------------------------------------------- +// Linear conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LinearValueConverter : public ValueConverter +{ + + private: + + Interpolator fUI2F; + Interpolator fF2UI; + + public: + + LinearValueConverter(double umin, double umax, double fmin, double fmax) : + fUI2F(umin,umax,fmin,fmax), fF2UI(fmin,fmax,umin,umax) + {} + + LinearValueConverter() : fUI2F(0.,0.,0.,0.), fF2UI(0.,0.,0.,0.) + {} + virtual double ui2faust(double x) { return fUI2F(x); } + virtual double faust2ui(double x) { return fF2UI(x); } + +}; + +//-------------------------------------------------------------------------------------- +// Two segments linear conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LinearValueConverter2 : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fUI2F; + Interpolator3pt fF2UI; + + public: + + LinearValueConverter2(double amin, double amid, double amax, double min, double init, double max) : + fUI2F(amin, amid, amax, min, init, max), fF2UI(min, init, max, amin, amid, amax) + {} + + LinearValueConverter2() : fUI2F(0.,0.,0.,0.,0.,0.), fF2UI(0.,0.,0.,0.,0.,0.) + {} + + virtual double ui2faust(double x) { return fUI2F(x); } + virtual double faust2ui(double x) { return fF2UI(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) + { + fUI2F = Interpolator3pt(amin, amid, amax, min, init, max); + fF2UI = Interpolator3pt(min, init, max, amin, amid, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fUI2F.getMappingValues(amin, amid, amax); + } + +}; + +//-------------------------------------------------------------------------------------- +// Logarithmic conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LogValueConverter : public LinearValueConverter +{ + + public: + + LogValueConverter(double umin, double umax, double fmin, double fmax) : + LinearValueConverter(umin, umax, std::log(std::max<double>(DBL_MIN, fmin)), std::log(std::max<double>(DBL_MIN, fmax))) + {} + + virtual double ui2faust(double x) { return std::exp(LinearValueConverter::ui2faust(x)); } + virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::log(std::max<double>(x, DBL_MIN))); } + +}; + +//-------------------------------------------------------------------------------------- +// Exponential conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class ExpValueConverter : public LinearValueConverter +{ + + public: + + ExpValueConverter(double umin, double umax, double fmin, double fmax) : + LinearValueConverter(umin, umax, std::min<double>(DBL_MAX, std::exp(fmin)), std::min<double>(DBL_MAX, std::exp(fmax))) + {} + + virtual double ui2faust(double x) { return std::log(LinearValueConverter::ui2faust(x)); } + virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::min<double>(DBL_MAX, std::exp(x))); } + +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using an Up curve (curve 0) +//-------------------------------------------------------------------------------------- +class AccUpConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator3pt fF2A; + + public: + + AccUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmin,fmid,fmax), + fF2A(fmin,fmid,fmax,amin,amid,amax) + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmin, fmid, fmax); + fF2A = Interpolator3pt(fmin, fmid, fmax, amin, amid, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } + +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using a Down curve (curve 1) +//-------------------------------------------------------------------------------------- +class AccDownConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator3pt fF2A; + + public: + + AccDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmax,fmid,fmin), + fF2A(fmin,fmid,fmax,amax,amid,amin) + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmax, fmid, fmin); + fF2A = Interpolator3pt(fmin, fmid, fmax, amax, amid, amin); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using an Up-Down curve (curve 2) +//-------------------------------------------------------------------------------------- +class AccUpDownConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator fF2A; + + public: + + AccUpDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmin,fmax,fmin), + fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotonic function + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmin, fmax, fmin); + fF2A = Interpolator(fmin, fmax, amin, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using a Down-Up curve (curve 3) +//-------------------------------------------------------------------------------------- +class AccDownUpConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator fF2A; + + public: + + AccDownUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmax,fmin,fmax), + fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotonic function + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmax, fmin, fmax); + fF2A = Interpolator(fmin, fmax, amin, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Base class for ZoneControl +//-------------------------------------------------------------------------------------- +class ZoneControl +{ + + protected: + + FAUSTFLOAT* fZone; + + public: + + ZoneControl(FAUSTFLOAT* zone) : fZone(zone) {} + virtual ~ZoneControl() {} + + virtual void update(double v) const {} + + virtual void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) {} + virtual void getMappingValues(double& amin, double& amid, double& amax) {} + + FAUSTFLOAT* getZone() { return fZone; } + + virtual void setActive(bool on_off) {} + virtual bool getActive() { return false; } + + virtual int getCurve() { return -1; } + +}; + +//-------------------------------------------------------------------------------------- +// Useful to implement accelerometers metadata as a list of ZoneControl for each axes +//-------------------------------------------------------------------------------------- +class ConverterZoneControl : public ZoneControl +{ + + protected: + + ValueConverter* fValueConverter; + + public: + + ConverterZoneControl(FAUSTFLOAT* zone, ValueConverter* converter) : ZoneControl(zone), fValueConverter(converter) {} + virtual ~ConverterZoneControl() { delete fValueConverter; } // Assuming fValueConverter is not kept elsewhere... + + virtual void update(double v) const { *fZone = fValueConverter->ui2faust(v); } + + ValueConverter* getConverter() { return fValueConverter; } + +}; + +//-------------------------------------------------------------------------------------- +// Association of a zone and a four value converter, each one for each possible curve. +// Useful to implement accelerometers metadata as a list of ZoneControl for each axes +//-------------------------------------------------------------------------------------- +class CurveZoneControl : public ZoneControl +{ + + private: + + std::vector<UpdatableValueConverter*> fValueConverters; + int fCurve; + + public: + + CurveZoneControl(FAUSTFLOAT* zone, int curve, double amin, double amid, double amax, double min, double init, double max) : ZoneControl(zone), fCurve(0) + { + assert(curve >= 0 && curve <= 3); + fValueConverters.push_back(new AccUpConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccDownConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccUpDownConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccDownUpConverter(amin, amid, amax, min, init, max)); + fCurve = curve; + } + virtual ~CurveZoneControl() + { + std::vector<UpdatableValueConverter*>::iterator it; + for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) { + delete(*it); + } + } + void update(double v) const { if (fValueConverters[fCurve]->getActive()) *fZone = fValueConverters[fCurve]->ui2faust(v); } + + void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) + { + fValueConverters[curve]->setMappingValues(amin, amid, amax, min, init, max); + fCurve = curve; + } + + void getMappingValues(double& amin, double& amid, double& amax) + { + fValueConverters[fCurve]->getMappingValues(amin, amid, amax); + } + + void setActive(bool on_off) + { + std::vector<UpdatableValueConverter*>::iterator it; + for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) { + (*it)->setActive(on_off); + } + } + + int getCurve() { return fCurve; } +}; + +class ZoneReader +{ + + private: + + FAUSTFLOAT* fZone; + Interpolator fInterpolator; + + public: + + ZoneReader(FAUSTFLOAT* zone, double lo, double hi) : fZone(zone), fInterpolator(lo, hi, 0, 255) {} + + virtual ~ZoneReader() {} + + int getValue() + { + return (fZone != nullptr) ? int(fInterpolator(*fZone)) : 127; + } + +}; + +#endif +/************************** END ValueConverter.h **************************/ + +class APIUI : public PathBuilder, public Meta, public UI +{ + public: + + enum ItemType { kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph }; + + protected: + + enum { kLin = 0, kLog = 1, kExp = 2 }; + + int fNumParameters; + std::vector<std::string> fPaths; + std::vector<std::string> fLabels; + std::map<std::string, int> fPathMap; + std::map<std::string, int> fLabelMap; + std::vector<ValueConverter*> fConversion; + std::vector<FAUSTFLOAT*> fZone; + std::vector<FAUSTFLOAT> fInit; + std::vector<FAUSTFLOAT> fMin; + std::vector<FAUSTFLOAT> fMax; + std::vector<FAUSTFLOAT> fStep; + std::vector<ItemType> fItemType; + std::vector<std::map<std::string, std::string> > fMetaData; + std::vector<ZoneControl*> fAcc[3]; + std::vector<ZoneControl*> fGyr[3]; + + // Screen color control + // "...[screencolor:red]..." etc. + bool fHasScreenControl; // true if control screen color metadata + ZoneReader* fRedReader; + ZoneReader* fGreenReader; + ZoneReader* fBlueReader; + + // Current values controlled by metadata + std::string fCurrentUnit; + int fCurrentScale; + std::string fCurrentAcc; + std::string fCurrentGyr; + std::string fCurrentColor; + std::string fCurrentTooltip; + std::map<std::string, std::string> fCurrentMetadata; + + // Add a generic parameter + virtual void addParameter(const char* label, + FAUSTFLOAT* zone, + FAUSTFLOAT init, + FAUSTFLOAT min, + FAUSTFLOAT max, + FAUSTFLOAT step, + ItemType type) + { + std::string path = buildPath(label); + fPathMap[path] = fLabelMap[label] = fNumParameters++; + fPaths.push_back(path); + fLabels.push_back(label); + fZone.push_back(zone); + fInit.push_back(init); + fMin.push_back(min); + fMax.push_back(max); + fStep.push_back(step); + fItemType.push_back(type); + + // handle scale metadata + switch (fCurrentScale) { + case kLin: + fConversion.push_back(new LinearValueConverter(0, 1, min, max)); + break; + case kLog: + fConversion.push_back(new LogValueConverter(0, 1, min, max)); + break; + case kExp: fConversion.push_back(new ExpValueConverter(0, 1, min, max)); + break; + } + fCurrentScale = kLin; + + if (fCurrentAcc.size() > 0 && fCurrentGyr.size() > 0) { + std::cerr << "warning : 'acc' and 'gyr' metadata used for the same " << label << " parameter !!\n"; + } + + // handle acc metadata "...[acc : <axe> <curve> <amin> <amid> <amax>]..." + if (fCurrentAcc.size() > 0) { + std::istringstream iss(fCurrentAcc); + int axe, curve; + double amin, amid, amax; + iss >> axe >> curve >> amin >> amid >> amax; + + if ((0 <= axe) && (axe < 3) && + (0 <= curve) && (curve < 4) && + (amin < amax) && (amin <= amid) && (amid <= amax)) + { + fAcc[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); + } else { + std::cerr << "incorrect acc metadata : " << fCurrentAcc << std::endl; + } + fCurrentAcc = ""; + } + + // handle gyr metadata "...[gyr : <axe> <curve> <amin> <amid> <amax>]..." + if (fCurrentGyr.size() > 0) { + std::istringstream iss(fCurrentGyr); + int axe, curve; + double amin, amid, amax; + iss >> axe >> curve >> amin >> amid >> amax; + + if ((0 <= axe) && (axe < 3) && + (0 <= curve) && (curve < 4) && + (amin < amax) && (amin <= amid) && (amid <= amax)) + { + fGyr[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); + } else { + std::cerr << "incorrect gyr metadata : " << fCurrentGyr << std::endl; + } + fCurrentGyr = ""; + } + + // handle screencolor metadata "...[screencolor:red|green|blue|white]..." + if (fCurrentColor.size() > 0) { + if ((fCurrentColor == "red") && (fRedReader == 0)) { + fRedReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "green") && (fGreenReader == 0)) { + fGreenReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "blue") && (fBlueReader == 0)) { + fBlueReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "white") && (fRedReader == 0) && (fGreenReader == 0) && (fBlueReader == 0)) { + fRedReader = new ZoneReader(zone, min, max); + fGreenReader = new ZoneReader(zone, min, max); + fBlueReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else { + std::cerr << "incorrect screencolor metadata : " << fCurrentColor << std::endl; + } + } + fCurrentColor = ""; + + fMetaData.push_back(fCurrentMetadata); + fCurrentMetadata.clear(); + } + + int getZoneIndex(std::vector<ZoneControl*>* table, int p, int val) + { + FAUSTFLOAT* zone = fZone[p]; + for (size_t i = 0; i < table[val].size(); i++) { + if (zone == table[val][i]->getZone()) return int(i); + } + return -1; + } + + void setConverter(std::vector<ZoneControl*>* table, int p, int val, int curve, double amin, double amid, double amax) + { + int id1 = getZoneIndex(table, p, 0); + int id2 = getZoneIndex(table, p, 1); + int id3 = getZoneIndex(table, p, 2); + + // Deactivates everywhere.. + if (id1 != -1) table[0][id1]->setActive(false); + if (id2 != -1) table[1][id2]->setActive(false); + if (id3 != -1) table[2][id3]->setActive(false); + + if (val == -1) { // Means: no more mapping... + // So stay all deactivated... + } else { + int id4 = getZoneIndex(table, p, val); + if (id4 != -1) { + // Reactivate the one we edit... + table[val][id4]->setMappingValues(curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]); + table[val][id4]->setActive(true); + } else { + // Allocate a new CurveZoneControl which is 'active' by default + FAUSTFLOAT* zone = fZone[p]; + table[val].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, fMin[p], fInit[p], fMax[p])); + } + } + } + + void getConverter(std::vector<ZoneControl*>* table, int p, int& val, int& curve, double& amin, double& amid, double& amax) + { + int id1 = getZoneIndex(table, p, 0); + int id2 = getZoneIndex(table, p, 1); + int id3 = getZoneIndex(table, p, 2); + + if (id1 != -1) { + val = 0; + curve = table[val][id1]->getCurve(); + table[val][id1]->getMappingValues(amin, amid, amax); + } else if (id2 != -1) { + val = 1; + curve = table[val][id2]->getCurve(); + table[val][id2]->getMappingValues(amin, amid, amax); + } else if (id3 != -1) { + val = 2; + curve = table[val][id3]->getCurve(); + table[val][id3]->getMappingValues(amin, amid, amax); + } else { + val = -1; // No mapping + curve = 0; + amin = -100.; + amid = 0.; + amax = 100.; + } + } + + public: + + enum Type { kAcc = 0, kGyr = 1, kNoType }; + + APIUI() : fNumParameters(0), fHasScreenControl(false), fRedReader(0), fGreenReader(0), fBlueReader(0), fCurrentScale(kLin) + {} + + virtual ~APIUI() + { + for (auto& it : fConversion) delete it; + for (int i = 0; i < 3; i++) { + for (auto& it : fAcc[i]) delete it; + for (auto& it : fGyr[i]) delete it; + } + delete fRedReader; + delete fGreenReader; + delete fBlueReader; + } + + // -- widget's layouts + + virtual void openTabBox(const char* label) { pushLabel(label); } + virtual void openHorizontalBox(const char* label) { pushLabel(label); } + virtual void openVerticalBox(const char* label) { pushLabel(label); } + virtual void closeBox() { popLabel(); } + + // -- active widgets + + virtual void addButton(const char* label, FAUSTFLOAT* zone) + { + addParameter(label, zone, 0, 0, 1, 1, kButton); + } + + virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) + { + addParameter(label, zone, 0, 0, 1, 1, kCheckButton); + } + + virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kVSlider); + } + + virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kHSlider); + } + + virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kNumEntry); + } + + // -- passive widgets + + virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) + { + addParameter(label, zone, min, min, max, (max-min)/1000.0, kHBargraph); + } + + virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) + { + addParameter(label, zone, min, min, max, (max-min)/1000.0, kVBargraph); + } + + // -- soundfiles + + virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) {} + + // -- metadata declarations + + virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val) + { + // Keep metadata + fCurrentMetadata[key] = val; + + if (strcmp(key, "scale") == 0) { + if (strcmp(val, "log") == 0) { + fCurrentScale = kLog; + } else if (strcmp(val, "exp") == 0) { + fCurrentScale = kExp; + } else { + fCurrentScale = kLin; + } + } else if (strcmp(key, "unit") == 0) { + fCurrentUnit = val; + } else if (strcmp(key, "acc") == 0) { + fCurrentAcc = val; + } else if (strcmp(key, "gyr") == 0) { + fCurrentGyr = val; + } else if (strcmp(key, "screencolor") == 0) { + fCurrentColor = val; // val = "red", "green", "blue" or "white" + } else if (strcmp(key, "tooltip") == 0) { + fCurrentTooltip = val; + } + } + + virtual void declare(const char* key, const char* val) + {} + + //------------------------------------------------------------------------------- + // Simple API part + //------------------------------------------------------------------------------- + int getParamsCount() { return fNumParameters; } + int getParamIndex(const char* path) + { + if (fPathMap.find(path) != fPathMap.end()) { + return fPathMap[path]; + } else if (fLabelMap.find(path) != fLabelMap.end()) { + return fLabelMap[path]; + } else { + return -1; + } + } + const char* getParamAddress(int p) { return fPaths[p].c_str(); } + const char* getParamLabel(int p) { return fLabels[p].c_str(); } + std::map<const char*, const char*> getMetadata(int p) + { + std::map<const char*, const char*> res; + std::map<std::string, std::string> metadata = fMetaData[p]; + for (auto it : metadata) { + res[it.first.c_str()] = it.second.c_str(); + } + return res; + } + + const char* getMetadata(int p, const char* key) + { + return (fMetaData[p].find(key) != fMetaData[p].end()) ? fMetaData[p][key].c_str() : ""; + } + FAUSTFLOAT getParamMin(int p) { return fMin[p]; } + FAUSTFLOAT getParamMax(int p) { return fMax[p]; } + FAUSTFLOAT getParamStep(int p) { return fStep[p]; } + FAUSTFLOAT getParamInit(int p) { return fInit[p]; } + + FAUSTFLOAT* getParamZone(int p) { return fZone[p]; } + FAUSTFLOAT getParamValue(int p) { return *fZone[p]; } + void setParamValue(int p, FAUSTFLOAT v) { *fZone[p] = v; } + + double getParamRatio(int p) { return fConversion[p]->faust2ui(*fZone[p]); } + void setParamRatio(int p, double r) { *fZone[p] = fConversion[p]->ui2faust(r); } + + double value2ratio(int p, double r) { return fConversion[p]->faust2ui(r); } + double ratio2value(int p, double r) { return fConversion[p]->ui2faust(r); } + + /** + * Return the control type (kAcc, kGyr, or -1) for a given parameter + * + * @param p - the UI parameter index + * + * @return the type + */ + Type getParamType(int p) + { + if (p >= 0) { + if (getZoneIndex(fAcc, p, 0) != -1 + || getZoneIndex(fAcc, p, 1) != -1 + || getZoneIndex(fAcc, p, 2) != -1) { + return kAcc; + } else if (getZoneIndex(fGyr, p, 0) != -1 + || getZoneIndex(fGyr, p, 1) != -1 + || getZoneIndex(fGyr, p, 2) != -1) { + return kGyr; + } + } + return kNoType; + } + + /** + * Return the Item type (kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph) for a given parameter + * + * @param p - the UI parameter index + * + * @return the Item type + */ + ItemType getParamItemType(int p) + { + return fItemType[p]; + } + + /** + * Set a new value coming from an accelerometer, propagate it to all relevant FAUSTFLOAT* zones. + * + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer + * @param value - the new value + * + */ + void propagateAcc(int acc, double value) + { + for (size_t i = 0; i < fAcc[acc].size(); i++) { + fAcc[acc][i]->update(value); + } + } + + /** + * Used to edit accelerometer curves and mapping. Set curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer (-1 means "no mapping") + * @param curve - between 0 and 3 + * @param amin - mapping 'min' point + * @param amid - mapping 'middle' point + * @param amax - mapping 'max' point + * + */ + void setAccConverter(int p, int acc, int curve, double amin, double amid, double amax) + { + setConverter(fAcc, p, acc, curve, amin, amid, amax); + } + + /** + * Used to edit gyroscope curves and mapping. Set curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope (-1 means "no mapping") + * @param curve - between 0 and 3 + * @param amin - mapping 'min' point + * @param amid - mapping 'middle' point + * @param amax - mapping 'max' point + * + */ + void setGyrConverter(int p, int gyr, int curve, double amin, double amid, double amax) + { + setConverter(fGyr, p, gyr, curve, amin, amid, amax); + } + + /** + * Used to edit accelerometer curves and mapping. Get curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - the acc value to be retrieved (-1 means "no mapping") + * @param curve - the curve value to be retrieved + * @param amin - the amin value to be retrieved + * @param amid - the amid value to be retrieved + * @param amax - the amax value to be retrieved + * + */ + void getAccConverter(int p, int& acc, int& curve, double& amin, double& amid, double& amax) + { + getConverter(fAcc, p, acc, curve, amin, amid, amax); + } + + /** + * Used to edit gyroscope curves and mapping. Get curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param gyr - the gyr value to be retrieved (-1 means "no mapping") + * @param curve - the curve value to be retrieved + * @param amin - the amin value to be retrieved + * @param amid - the amid value to be retrieved + * @param amax - the amax value to be retrieved + * + */ + void getGyrConverter(int p, int& gyr, int& curve, double& amin, double& amid, double& amax) + { + getConverter(fGyr, p, gyr, curve, amin, amid, amax); + } + + /** + * Set a new value coming from an gyroscope, propagate it to all relevant FAUSTFLOAT* zones. + * + * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope + * @param value - the new value + * + */ + void propagateGyr(int gyr, double value) + { + for (size_t i = 0; i < fGyr[gyr].size(); i++) { + fGyr[gyr][i]->update(value); + } + } + + /** + * Get the number of FAUSTFLOAT* zones controlled with the accelerometer + * + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer + * @return the number of zones + * + */ + int getAccCount(int acc) + { + return (acc >= 0 && acc < 3) ? int(fAcc[acc].size()) : 0; + } + + /** + * Get the number of FAUSTFLOAT* zones controlled with the gyroscope + * + * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope + * @param the number of zones + * + */ + int getGyrCount(int gyr) + { + return (gyr >= 0 && gyr < 3) ? int(fGyr[gyr].size()) : 0; + } + + // getScreenColor() : -1 means no screen color control (no screencolor metadata found) + // otherwise return 0x00RRGGBB a ready to use color + int getScreenColor() + { + if (fHasScreenControl) { + int r = (fRedReader) ? fRedReader->getValue() : 0; + int g = (fGreenReader) ? fGreenReader->getValue() : 0; + int b = (fBlueReader) ? fBlueReader->getValue() : 0; + return (r<<16) | (g<<8) | b; + } else { + return -1; + } + } + +}; + +#endif +/************************** END APIUI.h **************************/ + +// NOTE: "faust -scn name" changes the last line above to +// #include <faust/name/name.h> + +//---------------------------------------------------------------------------- +// FAUST Generated Code +//---------------------------------------------------------------------------- + + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +#include <algorithm> +#include <cmath> +#include <math.h> + + +#ifndef FAUSTCLASS +#define FAUSTCLASS compressordsp +#endif + +#ifdef __APPLE__ +#define exp10f __exp10f +#define exp10 __exp10 +#endif + +class compressordsp : public dsp { + + private: + + FAUSTFLOAT fCheckbox0; + FAUSTFLOAT fHslider0; + int fSampleRate; + float fConst0; + FAUSTFLOAT fHslider1; + FAUSTFLOAT fHslider2; + FAUSTFLOAT fHslider3; + float fRec5[2]; + float fRec4[2]; + FAUSTFLOAT fHslider4; + float fRec3[2]; + float fRec2[2]; + float fRec1[2]; + float fRec0[2]; + FAUSTFLOAT fHbargraph0; + + public: + + void metadata(Meta* m) { + m->declare("analyzers.lib/name", "Faust Analyzer Library"); + m->declare("analyzers.lib/version", "0.1"); + m->declare("author", "Julius Smith"); + m->declare("basics.lib/name", "Faust Basic Element Library"); + m->declare("basics.lib/version", "0.1"); + m->declare("compressors.lib/compression_gain_mono:author", "Julius O. Smith III"); + m->declare("compressors.lib/compression_gain_mono:copyright", "Copyright (C) 2014-2020 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("compressors.lib/compression_gain_mono:license", "MIT-style STK-4.3 license"); + m->declare("compressors.lib/compressor_lad_mono:author", "Julius O. Smith III"); + m->declare("compressors.lib/compressor_lad_mono:copyright", "Copyright (C) 2014-2020 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("compressors.lib/compressor_lad_mono:license", "MIT-style STK-4.3 license"); + m->declare("compressors.lib/compressor_mono:author", "Julius O. Smith III"); + m->declare("compressors.lib/compressor_mono:copyright", "Copyright (C) 2014-2020 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("compressors.lib/compressor_mono:license", "MIT-style STK-4.3 license"); + m->declare("compressors.lib/name", "Faust Compressor Effect Library"); + m->declare("compressors.lib/version", "0.0"); + m->declare("description", "Compressor demo application, adapted from the Faust Library's dm.compressor_demo in demos.lib"); + m->declare("documentation", "https://faustlibraries.grame.fr/libs/compressors/#cocompressor_mono"); + m->declare("filename", "compressordsp.dsp"); + m->declare("license", "MIT Style STK-4.2"); + m->declare("maths.lib/author", "GRAME"); + m->declare("maths.lib/copyright", "GRAME"); + m->declare("maths.lib/license", "LGPL with exception"); + m->declare("maths.lib/name", "Faust Math Library"); + m->declare("maths.lib/version", "2.3"); + m->declare("name", "compressor"); + m->declare("platform.lib/name", "Generic Platform Library"); + m->declare("platform.lib/version", "0.1"); + m->declare("signals.lib/name", "Faust Signal Routing Library"); + m->declare("signals.lib/version", "0.0"); + m->declare("version", "0.0"); + } + + virtual int getNumInputs() { + return 1; + } + virtual int getNumOutputs() { + return 1; + } + virtual int getInputRate(int channel) { + int rate; + switch ((channel)) { + case 0: { + rate = 1; + break; + } + default: { + rate = -1; + break; + } + } + return rate; + } + virtual int getOutputRate(int channel) { + int rate; + switch ((channel)) { + case 0: { + rate = 1; + break; + } + default: { + rate = -1; + break; + } + } + return rate; + } + + static void classInit(int sample_rate) { + } + + virtual void instanceConstants(int sample_rate) { + fSampleRate = sample_rate; + fConst0 = (1.0f / std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate)))); + } + + virtual void instanceResetUserInterface() { + fCheckbox0 = FAUSTFLOAT(0.0f); + fHslider0 = FAUSTFLOAT(2.0f); + fHslider1 = FAUSTFLOAT(15.0f); + fHslider2 = FAUSTFLOAT(2.0f); + fHslider3 = FAUSTFLOAT(40.0f); + fHslider4 = FAUSTFLOAT(-24.0f); + } + + virtual void instanceClear() { + for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { + fRec5[l0] = 0.0f; + } + for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { + fRec4[l1] = 0.0f; + } + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec3[l2] = 0.0f; + } + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fRec2[l3] = 0.0f; + } + for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { + fRec1[l4] = 0.0f; + } + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fRec0[l5] = 0.0f; + } + } + + virtual void init(int sample_rate) { + classInit(sample_rate); + instanceInit(sample_rate); + } + virtual void instanceInit(int sample_rate) { + instanceConstants(sample_rate); + instanceResetUserInterface(); + instanceClear(); + } + + virtual compressordsp* clone() { + return new compressordsp(); + } + + virtual int getSampleRate() { + return fSampleRate; + } + + virtual void buildUserInterface(UI* ui_interface) { + ui_interface->declare(0, "tooltip", "References: https://faustlibraries.grame.fr/libs/compressors/ http://en.wikipedia.org/wiki/Dynamic_range_compression"); + ui_interface->openVerticalBox("COMPRESSOR"); + ui_interface->declare(0, "0", ""); + ui_interface->openHorizontalBox("0x00"); + ui_interface->declare(&fCheckbox0, "0", ""); + ui_interface->declare(&fCheckbox0, "tooltip", "When this is checked, the compressor has no effect"); + ui_interface->addCheckButton("Bypass", &fCheckbox0); + ui_interface->declare(&fHbargraph0, "1", ""); + ui_interface->declare(&fHbargraph0, "tooltip", "Compressor gain in dB"); + ui_interface->declare(&fHbargraph0, "unit", "dB"); + ui_interface->addHorizontalBargraph("Compressor Gain", &fHbargraph0, -50.0f, 10.0f); + ui_interface->closeBox(); + ui_interface->declare(0, "1", ""); + ui_interface->openHorizontalBox("0x00"); + ui_interface->declare(0, "3", ""); + ui_interface->openHorizontalBox("Compression Control"); + ui_interface->declare(&fHslider2, "0", ""); + ui_interface->declare(&fHslider2, "style", "knob"); + ui_interface->declare(&fHslider2, "tooltip", "A compression Ratio of N means that for each N dB increase in input signal level above Threshold, the output level goes up 1 dB"); + ui_interface->addHorizontalSlider("Ratio", &fHslider2, 2.0f, 1.0f, 20.0f, 0.100000001f); + ui_interface->declare(&fHslider4, "1", ""); + ui_interface->declare(&fHslider4, "style", "knob"); + ui_interface->declare(&fHslider4, "tooltip", "When the signal level exceeds the Threshold (in dB), its level is compressed according to the Ratio"); + ui_interface->declare(&fHslider4, "unit", "dB"); + ui_interface->addHorizontalSlider("Threshold", &fHslider4, -24.0f, -100.0f, 10.0f, 0.100000001f); + ui_interface->closeBox(); + ui_interface->declare(0, "4", ""); + ui_interface->openHorizontalBox("Compression Response"); + ui_interface->declare(&fHslider1, "1", ""); + ui_interface->declare(&fHslider1, "scale", "log"); + ui_interface->declare(&fHslider1, "style", "knob"); + ui_interface->declare(&fHslider1, "tooltip", "Time constant in ms (1/e smoothing time) for the compression gain to approach (exponentially) a new lower target level (the compression `kicking in')"); + ui_interface->declare(&fHslider1, "unit", "ms"); + ui_interface->addHorizontalSlider("Attack", &fHslider1, 15.0f, 1.0f, 1000.0f, 0.100000001f); + ui_interface->declare(&fHslider3, "2", ""); + ui_interface->declare(&fHslider3, "scale", "log"); + ui_interface->declare(&fHslider3, "style", "knob"); + ui_interface->declare(&fHslider3, "tooltip", "Time constant in ms (1/e smoothing time) for the compression gain to approach (exponentially) a new higher target level (the compression 'releasing')"); + ui_interface->declare(&fHslider3, "unit", "ms"); + ui_interface->addHorizontalSlider("Release", &fHslider3, 40.0f, 1.0f, 1000.0f, 0.100000001f); + ui_interface->closeBox(); + ui_interface->closeBox(); + ui_interface->declare(&fHslider0, "5", ""); + ui_interface->declare(&fHslider0, "tooltip", "The compressed-signal output level is increased by this amount (in dB) to make up for the level lost due to compression"); + ui_interface->declare(&fHslider0, "unit", "dB"); + ui_interface->addHorizontalSlider("MakeUpGain", &fHslider0, 2.0f, -96.0f, 96.0f, 0.100000001f); + ui_interface->closeBox(); + } + + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { + FAUSTFLOAT* input0 = inputs[0]; + FAUSTFLOAT* output0 = outputs[0]; + int iSlow0 = int(float(fCheckbox0)); + float fSlow1 = std::pow(10.0f, (0.0500000007f * float(fHslider0))); + float fSlow2 = std::max<float>(fConst0, (0.00100000005f * float(fHslider1))); + float fSlow3 = (0.5f * fSlow2); + int iSlow4 = (std::fabs(fSlow3) < 1.1920929e-07f); + float fSlow5 = (iSlow4 ? 0.0f : std::exp((0.0f - (fConst0 / (iSlow4 ? 1.0f : fSlow3))))); + float fSlow6 = ((1.0f / std::max<float>(1.00000001e-07f, float(fHslider2))) + -1.0f); + int iSlow7 = (std::fabs(fSlow2) < 1.1920929e-07f); + float fSlow8 = (iSlow7 ? 0.0f : std::exp((0.0f - (fConst0 / (iSlow7 ? 1.0f : fSlow2))))); + float fSlow9 = std::max<float>(fConst0, (0.00100000005f * float(fHslider3))); + int iSlow10 = (std::fabs(fSlow9) < 1.1920929e-07f); + float fSlow11 = (iSlow10 ? 0.0f : std::exp((0.0f - (fConst0 / (iSlow10 ? 1.0f : fSlow9))))); + float fSlow12 = float(fHslider4); + float fSlow13 = (1.0f - fSlow5); + for (int i = 0; (i < count); i = (i + 1)) { + float fTemp0 = float(input0[i]); + float fTemp1 = (iSlow0 ? 0.0f : fTemp0); + float fTemp2 = std::fabs(fTemp1); + float fTemp3 = ((fRec4[1] > fTemp2) ? fSlow11 : fSlow8); + fRec5[0] = ((fRec5[1] * fTemp3) + (fTemp2 * (1.0f - fTemp3))); + fRec4[0] = fRec5[0]; + fRec3[0] = ((fRec3[1] * fSlow5) + (fSlow6 * (std::max<float>(((20.0f * std::log10(fRec4[0])) - fSlow12), 0.0f) * fSlow13))); + float fTemp4 = (fTemp1 * std::pow(10.0f, (0.0500000007f * fRec3[0]))); + float fTemp5 = std::fabs(fTemp4); + float fTemp6 = ((fRec1[1] > fTemp5) ? fSlow11 : fSlow8); + fRec2[0] = ((fRec2[1] * fTemp6) + (fTemp5 * (1.0f - fTemp6))); + fRec1[0] = fRec2[0]; + fRec0[0] = ((fSlow5 * fRec0[1]) + (fSlow6 * (std::max<float>(((20.0f * std::log10(fRec1[0])) - fSlow12), 0.0f) * fSlow13))); + fHbargraph0 = FAUSTFLOAT((20.0f * std::log10(std::pow(10.0f, (0.0500000007f * fRec0[0]))))); + output0[i] = FAUSTFLOAT((iSlow0 ? fTemp0 : (fSlow1 * fTemp4))); + fRec5[1] = fRec5[0]; + fRec4[1] = fRec4[0]; + fRec3[1] = fRec3[0]; + fRec2[1] = fRec2[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + } + } + +}; + +#endif diff --git a/src/freeverbdsp.h b/src/freeverbdsp.h new file mode 100644 index 0000000..4ccaa5e --- /dev/null +++ b/src/freeverbdsp.h @@ -0,0 +1,2168 @@ +/* ------------------------------------------------------------ +author: "Romain Michon" +license: "LGPL" +name: "freeverb" +version: "0.0" +Code generated with Faust 2.28.6 (https://faust.grame.fr) +Compilation options: -lang cpp -inpl -scal -ftz 0 +------------------------------------------------------------ */ + +#ifndef __freeverbdsp_H__ +#define __freeverbdsp_H__ + +// NOTE: ANY INCLUDE-GUARD HERE MUST BE DERIVED FROM THE CLASS NAME +// +// faust2header.cpp - FAUST Architecture File +// This is a simple variation of matlabplot.cpp in the Faust distribution +// aimed at creating a simple C++ header file (.h) containing a Faust DSP. +// See the Makefile for how to use it. + +/************************** BEGIN dsp.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __dsp__ +#define __dsp__ + +#include <string> +#include <vector> + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +struct UI; +struct Meta; + +/** + * DSP memory manager. + */ + +struct dsp_memory_manager { + + virtual ~dsp_memory_manager() {} + + virtual void* allocate(size_t size) = 0; + virtual void destroy(void* ptr) = 0; + +}; + +/** +* Signal processor definition. +*/ + +class dsp { + + public: + + dsp() {} + virtual ~dsp() {} + + /* Return instance number of audio inputs */ + virtual int getNumInputs() = 0; + + /* Return instance number of audio outputs */ + virtual int getNumOutputs() = 0; + + /** + * Trigger the ui_interface parameter with instance specific calls + * to 'openTabBox', 'addButton', 'addVerticalSlider'... in order to build the UI. + * + * @param ui_interface - the user interface builder + */ + virtual void buildUserInterface(UI* ui_interface) = 0; + + /* Returns the sample rate currently used by the instance */ + virtual int getSampleRate() = 0; + + /** + * Global init, calls the following methods: + * - static class 'classInit': static tables initialization + * - 'instanceInit': constants and instance state initialization + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void init(int sample_rate) = 0; + + /** + * Init instance state + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void instanceInit(int sample_rate) = 0; + + /** + * Init instance constant state + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void instanceConstants(int sample_rate) = 0; + + /* Init default control parameters values */ + virtual void instanceResetUserInterface() = 0; + + /* Init instance state (delay lines...) */ + virtual void instanceClear() = 0; + + /** + * Return a clone of the instance. + * + * @return a copy of the instance on success, otherwise a null pointer. + */ + virtual dsp* clone() = 0; + + /** + * Trigger the Meta* parameter with instance specific calls to 'declare' (key, value) metadata. + * + * @param m - the Meta* meta user + */ + virtual void metadata(Meta* m) = 0; + + /** + * DSP instance computation, to be called with successive in/out audio buffers. + * + * @param count - the number of frames to compute + * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) + * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) + * + */ + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0; + + /** + * DSP instance computation: alternative method to be used by subclasses. + * + * @param date_usec - the timestamp in microsec given by audio driver. + * @param count - the number of frames to compute + * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) + * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) + * + */ + virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); } + +}; + +/** + * Generic DSP decorator. + */ + +class decorator_dsp : public dsp { + + protected: + + dsp* fDSP; + + public: + + decorator_dsp(dsp* dsp = nullptr):fDSP(dsp) {} + virtual ~decorator_dsp() { delete fDSP; } + + virtual int getNumInputs() { return fDSP->getNumInputs(); } + virtual int getNumOutputs() { return fDSP->getNumOutputs(); } + virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); } + virtual int getSampleRate() { return fDSP->getSampleRate(); } + virtual void init(int sample_rate) { fDSP->init(sample_rate); } + virtual void instanceInit(int sample_rate) { fDSP->instanceInit(sample_rate); } + virtual void instanceConstants(int sample_rate) { fDSP->instanceConstants(sample_rate); } + virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); } + virtual void instanceClear() { fDSP->instanceClear(); } + virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); } + virtual void metadata(Meta* m) { fDSP->metadata(m); } + // Beware: subclasses usually have to overload the two 'compute' methods + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); } + virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); } + +}; + +/** + * DSP factory class. + */ + +class dsp_factory { + + protected: + + // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory); + virtual ~dsp_factory() {} + + public: + + virtual std::string getName() = 0; + virtual std::string getSHAKey() = 0; + virtual std::string getDSPCode() = 0; + virtual std::string getCompileOptions() = 0; + virtual std::vector<std::string> getLibraryList() = 0; + virtual std::vector<std::string> getIncludePathnames() = 0; + + virtual dsp* createDSPInstance() = 0; + + virtual void setMemoryManager(dsp_memory_manager* manager) = 0; + virtual dsp_memory_manager* getMemoryManager() = 0; + +}; + +/** + * On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero) + * flags to avoid costly denormals. + */ + +#ifdef __SSE__ + #include <xmmintrin.h> + #ifdef __SSE2__ + #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040) + #else + #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000) + #endif +#else + #define AVOIDDENORMALS +#endif + +#endif +/************************** END dsp.h **************************/ + +/************************** BEGIN APIUI.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef API_UI_H +#define API_UI_H + +#include <sstream> +#include <string> +#include <vector> +#include <iostream> +#include <map> + +/************************** BEGIN meta.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __meta__ +#define __meta__ + +struct Meta +{ + virtual ~Meta() {}; + virtual void declare(const char* key, const char* value) = 0; + +}; + +#endif +/************************** END meta.h **************************/ +/************************** BEGIN UI.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2020 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __UI_H__ +#define __UI_H__ + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +/******************************************************************************* + * UI : Faust DSP User Interface + * User Interface as expected by the buildUserInterface() method of a DSP. + * This abstract class contains only the method that the Faust compiler can + * generate to describe a DSP user interface. + ******************************************************************************/ + +struct Soundfile; + +template <typename REAL> +struct UIReal +{ + UIReal() {} + virtual ~UIReal() {} + + // -- widget's layouts + + virtual void openTabBox(const char* label) = 0; + virtual void openHorizontalBox(const char* label) = 0; + virtual void openVerticalBox(const char* label) = 0; + virtual void closeBox() = 0; + + // -- active widgets + + virtual void addButton(const char* label, REAL* zone) = 0; + virtual void addCheckButton(const char* label, REAL* zone) = 0; + virtual void addVerticalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + virtual void addHorizontalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + virtual void addNumEntry(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + + // -- passive widgets + + virtual void addHorizontalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0; + virtual void addVerticalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0; + + // -- soundfiles + + virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) = 0; + + // -- metadata declarations + + virtual void declare(REAL* zone, const char* key, const char* val) {} +}; + +struct UI : public UIReal<FAUSTFLOAT> +{ + UI() {} + virtual ~UI() {} +}; + +#endif +/************************** END UI.h **************************/ +/************************** BEGIN PathBuilder.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef FAUST_PATHBUILDER_H +#define FAUST_PATHBUILDER_H + +#include <vector> +#include <string> +#include <algorithm> + +/******************************************************************************* + * PathBuilder : Faust User Interface + * Helper class to build complete hierarchical path for UI items. + ******************************************************************************/ + +class PathBuilder +{ + + protected: + + std::vector<std::string> fControlsLevel; + + public: + + PathBuilder() {} + virtual ~PathBuilder() {} + + std::string buildPath(const std::string& label) + { + std::string res = "/"; + for (size_t i = 0; i < fControlsLevel.size(); i++) { + res += fControlsLevel[i]; + res += "/"; + } + res += label; + std::replace(res.begin(), res.end(), ' ', '_'); + return res; + } + + std::string buildLabel(std::string label) + { + std::replace(label.begin(), label.end(), ' ', '_'); + return label; + } + + void pushLabel(const std::string& label) { fControlsLevel.push_back(label); } + void popLabel() { fControlsLevel.pop_back(); } + +}; + +#endif // FAUST_PATHBUILDER_H +/************************** END PathBuilder.h **************************/ +/************************** BEGIN ValueConverter.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __ValueConverter__ +#define __ValueConverter__ + +/*************************************************************************************** + ValueConverter.h + (GRAME, Copyright 2015-2019) + +Set of conversion objects used to map user interface values (for example a gui slider +delivering values between 0 and 1) to faust values (for example a vslider between +20 and 20000) using a log scale. + +-- Utilities + +Range(lo,hi) : clip a value x between lo and hi +Interpolator(lo,hi,v1,v2) : Maps a value x between lo and hi to a value y between v1 and v2 +Interpolator3pt(lo,mi,hi,v1,vm,v2) : Map values between lo mid hi to values between v1 vm v2 + +-- Value Converters + +ValueConverter::ui2faust(x) +ValueConverter::faust2ui(x) + +-- ValueConverters used for sliders depending of the scale + +LinearValueConverter(umin, umax, fmin, fmax) +LinearValueConverter2(lo, mi, hi, v1, vm, v2) using 2 segments +LogValueConverter(umin, umax, fmin, fmax) +ExpValueConverter(umin, umax, fmin, fmax) + +-- ValueConverters used for accelerometers based on 3 points + +AccUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 0 +AccDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 1 +AccUpDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 2 +AccDownUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 3 + +-- lists of ZoneControl are used to implement accelerometers metadata for each axes + +ZoneControl(zone, valueConverter) : a zone with an accelerometer data converter + +-- ZoneReader are used to implement screencolor metadata + +ZoneReader(zone, valueConverter) : a zone with a data converter + +****************************************************************************************/ + +#include <float.h> +#include <algorithm> // std::max +#include <cmath> +#include <vector> +#include <assert.h> + +//-------------------------------------------------------------------------------------- +// Interpolator(lo,hi,v1,v2) +// Maps a value x between lo and hi to a value y between v1 and v2 +// y = v1 + (x-lo)/(hi-lo)*(v2-v1) +// y = v1 + (x-lo) * coef with coef = (v2-v1)/(hi-lo) +// y = v1 + x*coef - lo*coef +// y = v1 - lo*coef + x*coef +// y = offset + x*coef with offset = v1 - lo*coef +//-------------------------------------------------------------------------------------- +class Interpolator +{ + private: + + //-------------------------------------------------------------------------------------- + // Range(lo,hi) clip a value between lo and hi + //-------------------------------------------------------------------------------------- + struct Range + { + double fLo; + double fHi; + + Range(double x, double y) : fLo(std::min<double>(x,y)), fHi(std::max<double>(x,y)) {} + double operator()(double x) { return (x<fLo) ? fLo : (x>fHi) ? fHi : x; } + }; + + + Range fRange; + double fCoef; + double fOffset; + + public: + + Interpolator(double lo, double hi, double v1, double v2) : fRange(lo,hi) + { + if (hi != lo) { + // regular case + fCoef = (v2-v1)/(hi-lo); + fOffset = v1 - lo*fCoef; + } else { + // degenerate case, avoids division by zero + fCoef = 0; + fOffset = (v1+v2)/2; + } + } + double operator()(double v) + { + double x = fRange(v); + return fOffset + x*fCoef; + } + + void getLowHigh(double& amin, double& amax) + { + amin = fRange.fLo; + amax = fRange.fHi; + } +}; + +//-------------------------------------------------------------------------------------- +// Interpolator3pt(lo,mi,hi,v1,vm,v2) +// Map values between lo mid hi to values between v1 vm v2 +//-------------------------------------------------------------------------------------- +class Interpolator3pt +{ + + private: + + Interpolator fSegment1; + Interpolator fSegment2; + double fMid; + + public: + + Interpolator3pt(double lo, double mi, double hi, double v1, double vm, double v2) : + fSegment1(lo, mi, v1, vm), + fSegment2(mi, hi, vm, v2), + fMid(mi) {} + double operator()(double x) { return (x < fMid) ? fSegment1(x) : fSegment2(x); } + + void getMappingValues(double& amin, double& amid, double& amax) + { + fSegment1.getLowHigh(amin, amid); + fSegment2.getLowHigh(amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Abstract ValueConverter class. Converts values between UI and Faust representations +//-------------------------------------------------------------------------------------- +class ValueConverter +{ + + public: + + virtual ~ValueConverter() {} + virtual double ui2faust(double x) = 0; + virtual double faust2ui(double x) = 0; +}; + +//-------------------------------------------------------------------------------------- +// A converter than can be updated +//-------------------------------------------------------------------------------------- + +class UpdatableValueConverter : public ValueConverter { + + protected: + + bool fActive; + + public: + + UpdatableValueConverter():fActive(true) + {} + virtual ~UpdatableValueConverter() + {} + + virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) = 0; + virtual void getMappingValues(double& amin, double& amid, double& amax) = 0; + + void setActive(bool on_off) { fActive = on_off; } + bool getActive() { return fActive; } + +}; + + +//-------------------------------------------------------------------------------------- +// Linear conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LinearValueConverter : public ValueConverter +{ + + private: + + Interpolator fUI2F; + Interpolator fF2UI; + + public: + + LinearValueConverter(double umin, double umax, double fmin, double fmax) : + fUI2F(umin,umax,fmin,fmax), fF2UI(fmin,fmax,umin,umax) + {} + + LinearValueConverter() : fUI2F(0.,0.,0.,0.), fF2UI(0.,0.,0.,0.) + {} + virtual double ui2faust(double x) { return fUI2F(x); } + virtual double faust2ui(double x) { return fF2UI(x); } + +}; + +//-------------------------------------------------------------------------------------- +// Two segments linear conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LinearValueConverter2 : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fUI2F; + Interpolator3pt fF2UI; + + public: + + LinearValueConverter2(double amin, double amid, double amax, double min, double init, double max) : + fUI2F(amin, amid, amax, min, init, max), fF2UI(min, init, max, amin, amid, amax) + {} + + LinearValueConverter2() : fUI2F(0.,0.,0.,0.,0.,0.), fF2UI(0.,0.,0.,0.,0.,0.) + {} + + virtual double ui2faust(double x) { return fUI2F(x); } + virtual double faust2ui(double x) { return fF2UI(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) + { + fUI2F = Interpolator3pt(amin, amid, amax, min, init, max); + fF2UI = Interpolator3pt(min, init, max, amin, amid, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fUI2F.getMappingValues(amin, amid, amax); + } + +}; + +//-------------------------------------------------------------------------------------- +// Logarithmic conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LogValueConverter : public LinearValueConverter +{ + + public: + + LogValueConverter(double umin, double umax, double fmin, double fmax) : + LinearValueConverter(umin, umax, std::log(std::max<double>(DBL_MIN, fmin)), std::log(std::max<double>(DBL_MIN, fmax))) + {} + + virtual double ui2faust(double x) { return std::exp(LinearValueConverter::ui2faust(x)); } + virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::log(std::max<double>(x, DBL_MIN))); } + +}; + +//-------------------------------------------------------------------------------------- +// Exponential conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class ExpValueConverter : public LinearValueConverter +{ + + public: + + ExpValueConverter(double umin, double umax, double fmin, double fmax) : + LinearValueConverter(umin, umax, std::min<double>(DBL_MAX, std::exp(fmin)), std::min<double>(DBL_MAX, std::exp(fmax))) + {} + + virtual double ui2faust(double x) { return std::log(LinearValueConverter::ui2faust(x)); } + virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::min<double>(DBL_MAX, std::exp(x))); } + +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using an Up curve (curve 0) +//-------------------------------------------------------------------------------------- +class AccUpConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator3pt fF2A; + + public: + + AccUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmin,fmid,fmax), + fF2A(fmin,fmid,fmax,amin,amid,amax) + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmin, fmid, fmax); + fF2A = Interpolator3pt(fmin, fmid, fmax, amin, amid, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } + +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using a Down curve (curve 1) +//-------------------------------------------------------------------------------------- +class AccDownConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator3pt fF2A; + + public: + + AccDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmax,fmid,fmin), + fF2A(fmin,fmid,fmax,amax,amid,amin) + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmax, fmid, fmin); + fF2A = Interpolator3pt(fmin, fmid, fmax, amax, amid, amin); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using an Up-Down curve (curve 2) +//-------------------------------------------------------------------------------------- +class AccUpDownConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator fF2A; + + public: + + AccUpDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmin,fmax,fmin), + fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotonic function + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmin, fmax, fmin); + fF2A = Interpolator(fmin, fmax, amin, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using a Down-Up curve (curve 3) +//-------------------------------------------------------------------------------------- +class AccDownUpConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator fF2A; + + public: + + AccDownUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmax,fmin,fmax), + fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotonic function + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmax, fmin, fmax); + fF2A = Interpolator(fmin, fmax, amin, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Base class for ZoneControl +//-------------------------------------------------------------------------------------- +class ZoneControl +{ + + protected: + + FAUSTFLOAT* fZone; + + public: + + ZoneControl(FAUSTFLOAT* zone) : fZone(zone) {} + virtual ~ZoneControl() {} + + virtual void update(double v) const {} + + virtual void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) {} + virtual void getMappingValues(double& amin, double& amid, double& amax) {} + + FAUSTFLOAT* getZone() { return fZone; } + + virtual void setActive(bool on_off) {} + virtual bool getActive() { return false; } + + virtual int getCurve() { return -1; } + +}; + +//-------------------------------------------------------------------------------------- +// Useful to implement accelerometers metadata as a list of ZoneControl for each axes +//-------------------------------------------------------------------------------------- +class ConverterZoneControl : public ZoneControl +{ + + protected: + + ValueConverter* fValueConverter; + + public: + + ConverterZoneControl(FAUSTFLOAT* zone, ValueConverter* converter) : ZoneControl(zone), fValueConverter(converter) {} + virtual ~ConverterZoneControl() { delete fValueConverter; } // Assuming fValueConverter is not kept elsewhere... + + virtual void update(double v) const { *fZone = fValueConverter->ui2faust(v); } + + ValueConverter* getConverter() { return fValueConverter; } + +}; + +//-------------------------------------------------------------------------------------- +// Association of a zone and a four value converter, each one for each possible curve. +// Useful to implement accelerometers metadata as a list of ZoneControl for each axes +//-------------------------------------------------------------------------------------- +class CurveZoneControl : public ZoneControl +{ + + private: + + std::vector<UpdatableValueConverter*> fValueConverters; + int fCurve; + + public: + + CurveZoneControl(FAUSTFLOAT* zone, int curve, double amin, double amid, double amax, double min, double init, double max) : ZoneControl(zone), fCurve(0) + { + assert(curve >= 0 && curve <= 3); + fValueConverters.push_back(new AccUpConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccDownConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccUpDownConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccDownUpConverter(amin, amid, amax, min, init, max)); + fCurve = curve; + } + virtual ~CurveZoneControl() + { + std::vector<UpdatableValueConverter*>::iterator it; + for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) { + delete(*it); + } + } + void update(double v) const { if (fValueConverters[fCurve]->getActive()) *fZone = fValueConverters[fCurve]->ui2faust(v); } + + void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) + { + fValueConverters[curve]->setMappingValues(amin, amid, amax, min, init, max); + fCurve = curve; + } + + void getMappingValues(double& amin, double& amid, double& amax) + { + fValueConverters[fCurve]->getMappingValues(amin, amid, amax); + } + + void setActive(bool on_off) + { + std::vector<UpdatableValueConverter*>::iterator it; + for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) { + (*it)->setActive(on_off); + } + } + + int getCurve() { return fCurve; } +}; + +class ZoneReader +{ + + private: + + FAUSTFLOAT* fZone; + Interpolator fInterpolator; + + public: + + ZoneReader(FAUSTFLOAT* zone, double lo, double hi) : fZone(zone), fInterpolator(lo, hi, 0, 255) {} + + virtual ~ZoneReader() {} + + int getValue() + { + return (fZone != nullptr) ? int(fInterpolator(*fZone)) : 127; + } + +}; + +#endif +/************************** END ValueConverter.h **************************/ + +class APIUI : public PathBuilder, public Meta, public UI +{ + public: + + enum ItemType { kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph }; + + protected: + + enum { kLin = 0, kLog = 1, kExp = 2 }; + + int fNumParameters; + std::vector<std::string> fPaths; + std::vector<std::string> fLabels; + std::map<std::string, int> fPathMap; + std::map<std::string, int> fLabelMap; + std::vector<ValueConverter*> fConversion; + std::vector<FAUSTFLOAT*> fZone; + std::vector<FAUSTFLOAT> fInit; + std::vector<FAUSTFLOAT> fMin; + std::vector<FAUSTFLOAT> fMax; + std::vector<FAUSTFLOAT> fStep; + std::vector<ItemType> fItemType; + std::vector<std::map<std::string, std::string> > fMetaData; + std::vector<ZoneControl*> fAcc[3]; + std::vector<ZoneControl*> fGyr[3]; + + // Screen color control + // "...[screencolor:red]..." etc. + bool fHasScreenControl; // true if control screen color metadata + ZoneReader* fRedReader; + ZoneReader* fGreenReader; + ZoneReader* fBlueReader; + + // Current values controlled by metadata + std::string fCurrentUnit; + int fCurrentScale; + std::string fCurrentAcc; + std::string fCurrentGyr; + std::string fCurrentColor; + std::string fCurrentTooltip; + std::map<std::string, std::string> fCurrentMetadata; + + // Add a generic parameter + virtual void addParameter(const char* label, + FAUSTFLOAT* zone, + FAUSTFLOAT init, + FAUSTFLOAT min, + FAUSTFLOAT max, + FAUSTFLOAT step, + ItemType type) + { + std::string path = buildPath(label); + fPathMap[path] = fLabelMap[label] = fNumParameters++; + fPaths.push_back(path); + fLabels.push_back(label); + fZone.push_back(zone); + fInit.push_back(init); + fMin.push_back(min); + fMax.push_back(max); + fStep.push_back(step); + fItemType.push_back(type); + + // handle scale metadata + switch (fCurrentScale) { + case kLin: + fConversion.push_back(new LinearValueConverter(0, 1, min, max)); + break; + case kLog: + fConversion.push_back(new LogValueConverter(0, 1, min, max)); + break; + case kExp: fConversion.push_back(new ExpValueConverter(0, 1, min, max)); + break; + } + fCurrentScale = kLin; + + if (fCurrentAcc.size() > 0 && fCurrentGyr.size() > 0) { + std::cerr << "warning : 'acc' and 'gyr' metadata used for the same " << label << " parameter !!\n"; + } + + // handle acc metadata "...[acc : <axe> <curve> <amin> <amid> <amax>]..." + if (fCurrentAcc.size() > 0) { + std::istringstream iss(fCurrentAcc); + int axe, curve; + double amin, amid, amax; + iss >> axe >> curve >> amin >> amid >> amax; + + if ((0 <= axe) && (axe < 3) && + (0 <= curve) && (curve < 4) && + (amin < amax) && (amin <= amid) && (amid <= amax)) + { + fAcc[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); + } else { + std::cerr << "incorrect acc metadata : " << fCurrentAcc << std::endl; + } + fCurrentAcc = ""; + } + + // handle gyr metadata "...[gyr : <axe> <curve> <amin> <amid> <amax>]..." + if (fCurrentGyr.size() > 0) { + std::istringstream iss(fCurrentGyr); + int axe, curve; + double amin, amid, amax; + iss >> axe >> curve >> amin >> amid >> amax; + + if ((0 <= axe) && (axe < 3) && + (0 <= curve) && (curve < 4) && + (amin < amax) && (amin <= amid) && (amid <= amax)) + { + fGyr[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); + } else { + std::cerr << "incorrect gyr metadata : " << fCurrentGyr << std::endl; + } + fCurrentGyr = ""; + } + + // handle screencolor metadata "...[screencolor:red|green|blue|white]..." + if (fCurrentColor.size() > 0) { + if ((fCurrentColor == "red") && (fRedReader == 0)) { + fRedReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "green") && (fGreenReader == 0)) { + fGreenReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "blue") && (fBlueReader == 0)) { + fBlueReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "white") && (fRedReader == 0) && (fGreenReader == 0) && (fBlueReader == 0)) { + fRedReader = new ZoneReader(zone, min, max); + fGreenReader = new ZoneReader(zone, min, max); + fBlueReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else { + std::cerr << "incorrect screencolor metadata : " << fCurrentColor << std::endl; + } + } + fCurrentColor = ""; + + fMetaData.push_back(fCurrentMetadata); + fCurrentMetadata.clear(); + } + + int getZoneIndex(std::vector<ZoneControl*>* table, int p, int val) + { + FAUSTFLOAT* zone = fZone[p]; + for (size_t i = 0; i < table[val].size(); i++) { + if (zone == table[val][i]->getZone()) return int(i); + } + return -1; + } + + void setConverter(std::vector<ZoneControl*>* table, int p, int val, int curve, double amin, double amid, double amax) + { + int id1 = getZoneIndex(table, p, 0); + int id2 = getZoneIndex(table, p, 1); + int id3 = getZoneIndex(table, p, 2); + + // Deactivates everywhere.. + if (id1 != -1) table[0][id1]->setActive(false); + if (id2 != -1) table[1][id2]->setActive(false); + if (id3 != -1) table[2][id3]->setActive(false); + + if (val == -1) { // Means: no more mapping... + // So stay all deactivated... + } else { + int id4 = getZoneIndex(table, p, val); + if (id4 != -1) { + // Reactivate the one we edit... + table[val][id4]->setMappingValues(curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]); + table[val][id4]->setActive(true); + } else { + // Allocate a new CurveZoneControl which is 'active' by default + FAUSTFLOAT* zone = fZone[p]; + table[val].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, fMin[p], fInit[p], fMax[p])); + } + } + } + + void getConverter(std::vector<ZoneControl*>* table, int p, int& val, int& curve, double& amin, double& amid, double& amax) + { + int id1 = getZoneIndex(table, p, 0); + int id2 = getZoneIndex(table, p, 1); + int id3 = getZoneIndex(table, p, 2); + + if (id1 != -1) { + val = 0; + curve = table[val][id1]->getCurve(); + table[val][id1]->getMappingValues(amin, amid, amax); + } else if (id2 != -1) { + val = 1; + curve = table[val][id2]->getCurve(); + table[val][id2]->getMappingValues(amin, amid, amax); + } else if (id3 != -1) { + val = 2; + curve = table[val][id3]->getCurve(); + table[val][id3]->getMappingValues(amin, amid, amax); + } else { + val = -1; // No mapping + curve = 0; + amin = -100.; + amid = 0.; + amax = 100.; + } + } + + public: + + enum Type { kAcc = 0, kGyr = 1, kNoType }; + + APIUI() : fNumParameters(0), fHasScreenControl(false), fRedReader(0), fGreenReader(0), fBlueReader(0), fCurrentScale(kLin) + {} + + virtual ~APIUI() + { + for (auto& it : fConversion) delete it; + for (int i = 0; i < 3; i++) { + for (auto& it : fAcc[i]) delete it; + for (auto& it : fGyr[i]) delete it; + } + delete fRedReader; + delete fGreenReader; + delete fBlueReader; + } + + // -- widget's layouts + + virtual void openTabBox(const char* label) { pushLabel(label); } + virtual void openHorizontalBox(const char* label) { pushLabel(label); } + virtual void openVerticalBox(const char* label) { pushLabel(label); } + virtual void closeBox() { popLabel(); } + + // -- active widgets + + virtual void addButton(const char* label, FAUSTFLOAT* zone) + { + addParameter(label, zone, 0, 0, 1, 1, kButton); + } + + virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) + { + addParameter(label, zone, 0, 0, 1, 1, kCheckButton); + } + + virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kVSlider); + } + + virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kHSlider); + } + + virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kNumEntry); + } + + // -- passive widgets + + virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) + { + addParameter(label, zone, min, min, max, (max-min)/1000.0, kHBargraph); + } + + virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) + { + addParameter(label, zone, min, min, max, (max-min)/1000.0, kVBargraph); + } + + // -- soundfiles + + virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) {} + + // -- metadata declarations + + virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val) + { + // Keep metadata + fCurrentMetadata[key] = val; + + if (strcmp(key, "scale") == 0) { + if (strcmp(val, "log") == 0) { + fCurrentScale = kLog; + } else if (strcmp(val, "exp") == 0) { + fCurrentScale = kExp; + } else { + fCurrentScale = kLin; + } + } else if (strcmp(key, "unit") == 0) { + fCurrentUnit = val; + } else if (strcmp(key, "acc") == 0) { + fCurrentAcc = val; + } else if (strcmp(key, "gyr") == 0) { + fCurrentGyr = val; + } else if (strcmp(key, "screencolor") == 0) { + fCurrentColor = val; // val = "red", "green", "blue" or "white" + } else if (strcmp(key, "tooltip") == 0) { + fCurrentTooltip = val; + } + } + + virtual void declare(const char* key, const char* val) + {} + + //------------------------------------------------------------------------------- + // Simple API part + //------------------------------------------------------------------------------- + int getParamsCount() { return fNumParameters; } + int getParamIndex(const char* path) + { + if (fPathMap.find(path) != fPathMap.end()) { + return fPathMap[path]; + } else if (fLabelMap.find(path) != fLabelMap.end()) { + return fLabelMap[path]; + } else { + return -1; + } + } + const char* getParamAddress(int p) { return fPaths[p].c_str(); } + const char* getParamLabel(int p) { return fLabels[p].c_str(); } + std::map<const char*, const char*> getMetadata(int p) + { + std::map<const char*, const char*> res; + std::map<std::string, std::string> metadata = fMetaData[p]; + for (auto it : metadata) { + res[it.first.c_str()] = it.second.c_str(); + } + return res; + } + + const char* getMetadata(int p, const char* key) + { + return (fMetaData[p].find(key) != fMetaData[p].end()) ? fMetaData[p][key].c_str() : ""; + } + FAUSTFLOAT getParamMin(int p) { return fMin[p]; } + FAUSTFLOAT getParamMax(int p) { return fMax[p]; } + FAUSTFLOAT getParamStep(int p) { return fStep[p]; } + FAUSTFLOAT getParamInit(int p) { return fInit[p]; } + + FAUSTFLOAT* getParamZone(int p) { return fZone[p]; } + FAUSTFLOAT getParamValue(int p) { return *fZone[p]; } + void setParamValue(int p, FAUSTFLOAT v) { *fZone[p] = v; } + + double getParamRatio(int p) { return fConversion[p]->faust2ui(*fZone[p]); } + void setParamRatio(int p, double r) { *fZone[p] = fConversion[p]->ui2faust(r); } + + double value2ratio(int p, double r) { return fConversion[p]->faust2ui(r); } + double ratio2value(int p, double r) { return fConversion[p]->ui2faust(r); } + + /** + * Return the control type (kAcc, kGyr, or -1) for a given parameter + * + * @param p - the UI parameter index + * + * @return the type + */ + Type getParamType(int p) + { + if (p >= 0) { + if (getZoneIndex(fAcc, p, 0) != -1 + || getZoneIndex(fAcc, p, 1) != -1 + || getZoneIndex(fAcc, p, 2) != -1) { + return kAcc; + } else if (getZoneIndex(fGyr, p, 0) != -1 + || getZoneIndex(fGyr, p, 1) != -1 + || getZoneIndex(fGyr, p, 2) != -1) { + return kGyr; + } + } + return kNoType; + } + + /** + * Return the Item type (kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph) for a given parameter + * + * @param p - the UI parameter index + * + * @return the Item type + */ + ItemType getParamItemType(int p) + { + return fItemType[p]; + } + + /** + * Set a new value coming from an accelerometer, propagate it to all relevant FAUSTFLOAT* zones. + * + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer + * @param value - the new value + * + */ + void propagateAcc(int acc, double value) + { + for (size_t i = 0; i < fAcc[acc].size(); i++) { + fAcc[acc][i]->update(value); + } + } + + /** + * Used to edit accelerometer curves and mapping. Set curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer (-1 means "no mapping") + * @param curve - between 0 and 3 + * @param amin - mapping 'min' point + * @param amid - mapping 'middle' point + * @param amax - mapping 'max' point + * + */ + void setAccConverter(int p, int acc, int curve, double amin, double amid, double amax) + { + setConverter(fAcc, p, acc, curve, amin, amid, amax); + } + + /** + * Used to edit gyroscope curves and mapping. Set curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope (-1 means "no mapping") + * @param curve - between 0 and 3 + * @param amin - mapping 'min' point + * @param amid - mapping 'middle' point + * @param amax - mapping 'max' point + * + */ + void setGyrConverter(int p, int gyr, int curve, double amin, double amid, double amax) + { + setConverter(fGyr, p, gyr, curve, amin, amid, amax); + } + + /** + * Used to edit accelerometer curves and mapping. Get curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - the acc value to be retrieved (-1 means "no mapping") + * @param curve - the curve value to be retrieved + * @param amin - the amin value to be retrieved + * @param amid - the amid value to be retrieved + * @param amax - the amax value to be retrieved + * + */ + void getAccConverter(int p, int& acc, int& curve, double& amin, double& amid, double& amax) + { + getConverter(fAcc, p, acc, curve, amin, amid, amax); + } + + /** + * Used to edit gyroscope curves and mapping. Get curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param gyr - the gyr value to be retrieved (-1 means "no mapping") + * @param curve - the curve value to be retrieved + * @param amin - the amin value to be retrieved + * @param amid - the amid value to be retrieved + * @param amax - the amax value to be retrieved + * + */ + void getGyrConverter(int p, int& gyr, int& curve, double& amin, double& amid, double& amax) + { + getConverter(fGyr, p, gyr, curve, amin, amid, amax); + } + + /** + * Set a new value coming from an gyroscope, propagate it to all relevant FAUSTFLOAT* zones. + * + * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope + * @param value - the new value + * + */ + void propagateGyr(int gyr, double value) + { + for (size_t i = 0; i < fGyr[gyr].size(); i++) { + fGyr[gyr][i]->update(value); + } + } + + /** + * Get the number of FAUSTFLOAT* zones controlled with the accelerometer + * + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer + * @return the number of zones + * + */ + int getAccCount(int acc) + { + return (acc >= 0 && acc < 3) ? int(fAcc[acc].size()) : 0; + } + + /** + * Get the number of FAUSTFLOAT* zones controlled with the gyroscope + * + * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope + * @param the number of zones + * + */ + int getGyrCount(int gyr) + { + return (gyr >= 0 && gyr < 3) ? int(fGyr[gyr].size()) : 0; + } + + // getScreenColor() : -1 means no screen color control (no screencolor metadata found) + // otherwise return 0x00RRGGBB a ready to use color + int getScreenColor() + { + if (fHasScreenControl) { + int r = (fRedReader) ? fRedReader->getValue() : 0; + int g = (fGreenReader) ? fGreenReader->getValue() : 0; + int b = (fBlueReader) ? fBlueReader->getValue() : 0; + return (r<<16) | (g<<8) | b; + } else { + return -1; + } + } + +}; + +#endif +/************************** END APIUI.h **************************/ + +// NOTE: "faust -scn name" changes the last line above to +// #include <faust/name/name.h> + +//---------------------------------------------------------------------------- +// FAUST Generated Code +//---------------------------------------------------------------------------- + + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +#include <algorithm> +#include <cmath> +#include <math.h> + + +#ifndef FAUSTCLASS +#define FAUSTCLASS freeverbdsp +#endif + +#ifdef __APPLE__ +#define exp10f __exp10f +#define exp10 __exp10 +#endif + +class freeverbdsp : public dsp { + + private: + + int fSampleRate; + float fConst0; + float fConst1; + FAUSTFLOAT fVslider0; + float fConst2; + FAUSTFLOAT fVslider1; + float fRec9[2]; + FAUSTFLOAT fVslider2; + int IOTA; + float fVec0[8192]; + int iConst3; + float fRec8[2]; + float fRec11[2]; + float fVec1[8192]; + int iConst4; + float fRec10[2]; + float fRec13[2]; + float fVec2[8192]; + int iConst5; + float fRec12[2]; + float fRec15[2]; + float fVec3[8192]; + int iConst6; + float fRec14[2]; + float fRec17[2]; + float fVec4[8192]; + int iConst7; + float fRec16[2]; + float fRec19[2]; + float fVec5[8192]; + int iConst8; + float fRec18[2]; + float fRec21[2]; + float fVec6[8192]; + int iConst9; + float fRec20[2]; + float fRec23[2]; + float fVec7[8192]; + int iConst10; + float fRec22[2]; + float fVec8[2048]; + int iConst11; + int iConst12; + float fRec6[2]; + float fVec9[2048]; + int iConst13; + int iConst14; + float fRec4[2]; + float fVec10[2048]; + int iConst15; + int iConst16; + float fRec2[2]; + float fVec11[1024]; + int iConst17; + int iConst18; + float fRec0[2]; + float fRec33[2]; + float fVec12[8192]; + float fConst19; + FAUSTFLOAT fVslider3; + float fRec32[2]; + float fRec35[2]; + float fVec13[8192]; + float fRec34[2]; + float fRec37[2]; + float fVec14[8192]; + float fRec36[2]; + float fRec39[2]; + float fVec15[8192]; + float fRec38[2]; + float fRec41[2]; + float fVec16[8192]; + float fRec40[2]; + float fRec43[2]; + float fVec17[8192]; + float fRec42[2]; + float fRec45[2]; + float fVec18[8192]; + float fRec44[2]; + float fRec47[2]; + float fVec19[8192]; + float fRec46[2]; + float fVec20[2048]; + float fRec30[2]; + float fVec21[2048]; + float fRec28[2]; + float fVec22[2048]; + float fRec26[2]; + float fVec23[2048]; + float fRec24[2]; + + public: + + void metadata(Meta* m) { + m->declare("author", "Romain Michon"); + m->declare("delays.lib/name", "Faust Delay Library"); + m->declare("delays.lib/version", "0.1"); + m->declare("description", "Freeverb implementation in Faust, from the Faust Library's dm.freeverb_demo in demos.lib"); + m->declare("filename", "freeverbdsp.dsp"); + m->declare("filters.lib/allpass_comb:author", "Julius O. Smith III"); + m->declare("filters.lib/allpass_comb:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/allpass_comb:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/lowpass0_highpass1", "MIT-style STK-4.3 license"); + m->declare("filters.lib/name", "Faust Filters Library"); + m->declare("license", "LGPL"); + m->declare("maths.lib/author", "GRAME"); + m->declare("maths.lib/copyright", "GRAME"); + m->declare("maths.lib/license", "LGPL with exception"); + m->declare("maths.lib/name", "Faust Math Library"); + m->declare("maths.lib/version", "2.3"); + m->declare("name", "freeverb"); + m->declare("platform.lib/name", "Generic Platform Library"); + m->declare("platform.lib/version", "0.1"); + m->declare("reverbs.lib/name", "Faust Reverb Library"); + m->declare("reverbs.lib/version", "0.0"); + m->declare("version", "0.0"); + } + + virtual int getNumInputs() { + return 2; + } + virtual int getNumOutputs() { + return 2; + } + virtual int getInputRate(int channel) { + int rate; + switch ((channel)) { + case 0: { + rate = 1; + break; + } + case 1: { + rate = 1; + break; + } + default: { + rate = -1; + break; + } + } + return rate; + } + virtual int getOutputRate(int channel) { + int rate; + switch ((channel)) { + case 0: { + rate = 1; + break; + } + case 1: { + rate = 1; + break; + } + default: { + rate = -1; + break; + } + } + return rate; + } + + static void classInit(int sample_rate) { + } + + virtual void instanceConstants(int sample_rate) { + fSampleRate = sample_rate; + fConst0 = std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate))); + fConst1 = (12348.0f / fConst0); + fConst2 = (17640.0f / fConst0); + iConst3 = int((0.0253061224f * fConst0)); + iConst4 = int((0.0269387756f * fConst0)); + iConst5 = int((0.0289569162f * fConst0)); + iConst6 = int((0.0307482984f * fConst0)); + iConst7 = int((0.0322448984f * fConst0)); + iConst8 = int((0.033809524f * fConst0)); + iConst9 = int((0.0353061222f * fConst0)); + iConst10 = int((0.0366666652f * fConst0)); + iConst11 = int((0.0126077095f * fConst0)); + iConst12 = std::min<int>(1024, std::max<int>(0, (iConst11 + -1))); + iConst13 = int((0.00999999978f * fConst0)); + iConst14 = std::min<int>(1024, std::max<int>(0, (iConst13 + -1))); + iConst15 = int((0.00773242628f * fConst0)); + iConst16 = std::min<int>(1024, std::max<int>(0, (iConst15 + -1))); + iConst17 = int((0.00510204071f * fConst0)); + iConst18 = std::min<int>(1024, std::max<int>(0, (iConst17 + -1))); + fConst19 = (0.00104308384f * fConst0); + } + + virtual void instanceResetUserInterface() { + fVslider0 = FAUSTFLOAT(0.10000000000000001f); + fVslider1 = FAUSTFLOAT(0.5f); + fVslider2 = FAUSTFLOAT(0.10000000000000001f); + fVslider3 = FAUSTFLOAT(0.5f); + } + + virtual void instanceClear() { + for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { + fRec9[l0] = 0.0f; + } + IOTA = 0; + for (int l1 = 0; (l1 < 8192); l1 = (l1 + 1)) { + fVec0[l1] = 0.0f; + } + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec8[l2] = 0.0f; + } + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fRec11[l3] = 0.0f; + } + for (int l4 = 0; (l4 < 8192); l4 = (l4 + 1)) { + fVec1[l4] = 0.0f; + } + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fRec10[l5] = 0.0f; + } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fRec13[l6] = 0.0f; + } + for (int l7 = 0; (l7 < 8192); l7 = (l7 + 1)) { + fVec2[l7] = 0.0f; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec12[l8] = 0.0f; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec15[l9] = 0.0f; + } + for (int l10 = 0; (l10 < 8192); l10 = (l10 + 1)) { + fVec3[l10] = 0.0f; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fRec14[l11] = 0.0f; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fRec17[l12] = 0.0f; + } + for (int l13 = 0; (l13 < 8192); l13 = (l13 + 1)) { + fVec4[l13] = 0.0f; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fRec16[l14] = 0.0f; + } + for (int l15 = 0; (l15 < 2); l15 = (l15 + 1)) { + fRec19[l15] = 0.0f; + } + for (int l16 = 0; (l16 < 8192); l16 = (l16 + 1)) { + fVec5[l16] = 0.0f; + } + for (int l17 = 0; (l17 < 2); l17 = (l17 + 1)) { + fRec18[l17] = 0.0f; + } + for (int l18 = 0; (l18 < 2); l18 = (l18 + 1)) { + fRec21[l18] = 0.0f; + } + for (int l19 = 0; (l19 < 8192); l19 = (l19 + 1)) { + fVec6[l19] = 0.0f; + } + for (int l20 = 0; (l20 < 2); l20 = (l20 + 1)) { + fRec20[l20] = 0.0f; + } + for (int l21 = 0; (l21 < 2); l21 = (l21 + 1)) { + fRec23[l21] = 0.0f; + } + for (int l22 = 0; (l22 < 8192); l22 = (l22 + 1)) { + fVec7[l22] = 0.0f; + } + for (int l23 = 0; (l23 < 2); l23 = (l23 + 1)) { + fRec22[l23] = 0.0f; + } + for (int l24 = 0; (l24 < 2048); l24 = (l24 + 1)) { + fVec8[l24] = 0.0f; + } + for (int l25 = 0; (l25 < 2); l25 = (l25 + 1)) { + fRec6[l25] = 0.0f; + } + for (int l26 = 0; (l26 < 2048); l26 = (l26 + 1)) { + fVec9[l26] = 0.0f; + } + for (int l27 = 0; (l27 < 2); l27 = (l27 + 1)) { + fRec4[l27] = 0.0f; + } + for (int l28 = 0; (l28 < 2048); l28 = (l28 + 1)) { + fVec10[l28] = 0.0f; + } + for (int l29 = 0; (l29 < 2); l29 = (l29 + 1)) { + fRec2[l29] = 0.0f; + } + for (int l30 = 0; (l30 < 1024); l30 = (l30 + 1)) { + fVec11[l30] = 0.0f; + } + for (int l31 = 0; (l31 < 2); l31 = (l31 + 1)) { + fRec0[l31] = 0.0f; + } + for (int l32 = 0; (l32 < 2); l32 = (l32 + 1)) { + fRec33[l32] = 0.0f; + } + for (int l33 = 0; (l33 < 8192); l33 = (l33 + 1)) { + fVec12[l33] = 0.0f; + } + for (int l34 = 0; (l34 < 2); l34 = (l34 + 1)) { + fRec32[l34] = 0.0f; + } + for (int l35 = 0; (l35 < 2); l35 = (l35 + 1)) { + fRec35[l35] = 0.0f; + } + for (int l36 = 0; (l36 < 8192); l36 = (l36 + 1)) { + fVec13[l36] = 0.0f; + } + for (int l37 = 0; (l37 < 2); l37 = (l37 + 1)) { + fRec34[l37] = 0.0f; + } + for (int l38 = 0; (l38 < 2); l38 = (l38 + 1)) { + fRec37[l38] = 0.0f; + } + for (int l39 = 0; (l39 < 8192); l39 = (l39 + 1)) { + fVec14[l39] = 0.0f; + } + for (int l40 = 0; (l40 < 2); l40 = (l40 + 1)) { + fRec36[l40] = 0.0f; + } + for (int l41 = 0; (l41 < 2); l41 = (l41 + 1)) { + fRec39[l41] = 0.0f; + } + for (int l42 = 0; (l42 < 8192); l42 = (l42 + 1)) { + fVec15[l42] = 0.0f; + } + for (int l43 = 0; (l43 < 2); l43 = (l43 + 1)) { + fRec38[l43] = 0.0f; + } + for (int l44 = 0; (l44 < 2); l44 = (l44 + 1)) { + fRec41[l44] = 0.0f; + } + for (int l45 = 0; (l45 < 8192); l45 = (l45 + 1)) { + fVec16[l45] = 0.0f; + } + for (int l46 = 0; (l46 < 2); l46 = (l46 + 1)) { + fRec40[l46] = 0.0f; + } + for (int l47 = 0; (l47 < 2); l47 = (l47 + 1)) { + fRec43[l47] = 0.0f; + } + for (int l48 = 0; (l48 < 8192); l48 = (l48 + 1)) { + fVec17[l48] = 0.0f; + } + for (int l49 = 0; (l49 < 2); l49 = (l49 + 1)) { + fRec42[l49] = 0.0f; + } + for (int l50 = 0; (l50 < 2); l50 = (l50 + 1)) { + fRec45[l50] = 0.0f; + } + for (int l51 = 0; (l51 < 8192); l51 = (l51 + 1)) { + fVec18[l51] = 0.0f; + } + for (int l52 = 0; (l52 < 2); l52 = (l52 + 1)) { + fRec44[l52] = 0.0f; + } + for (int l53 = 0; (l53 < 2); l53 = (l53 + 1)) { + fRec47[l53] = 0.0f; + } + for (int l54 = 0; (l54 < 8192); l54 = (l54 + 1)) { + fVec19[l54] = 0.0f; + } + for (int l55 = 0; (l55 < 2); l55 = (l55 + 1)) { + fRec46[l55] = 0.0f; + } + for (int l56 = 0; (l56 < 2048); l56 = (l56 + 1)) { + fVec20[l56] = 0.0f; + } + for (int l57 = 0; (l57 < 2); l57 = (l57 + 1)) { + fRec30[l57] = 0.0f; + } + for (int l58 = 0; (l58 < 2048); l58 = (l58 + 1)) { + fVec21[l58] = 0.0f; + } + for (int l59 = 0; (l59 < 2); l59 = (l59 + 1)) { + fRec28[l59] = 0.0f; + } + for (int l60 = 0; (l60 < 2048); l60 = (l60 + 1)) { + fVec22[l60] = 0.0f; + } + for (int l61 = 0; (l61 < 2); l61 = (l61 + 1)) { + fRec26[l61] = 0.0f; + } + for (int l62 = 0; (l62 < 2048); l62 = (l62 + 1)) { + fVec23[l62] = 0.0f; + } + for (int l63 = 0; (l63 < 2); l63 = (l63 + 1)) { + fRec24[l63] = 0.0f; + } + } + + virtual void init(int sample_rate) { + classInit(sample_rate); + instanceInit(sample_rate); + } + virtual void instanceInit(int sample_rate) { + instanceConstants(sample_rate); + instanceResetUserInterface(); + instanceClear(); + } + + virtual freeverbdsp* clone() { + return new freeverbdsp(); + } + + virtual int getSampleRate() { + return fSampleRate; + } + + virtual void buildUserInterface(UI* ui_interface) { + ui_interface->openHorizontalBox("Freeverb"); + ui_interface->declare(0, "0", ""); + ui_interface->openVerticalBox("0x00"); + ui_interface->declare(&fVslider1, "0", ""); + ui_interface->declare(&fVslider1, "style", "knob"); + ui_interface->declare(&fVslider1, "tooltip", "Somehow control the density of the reverb."); + ui_interface->addVerticalSlider("Damp", &fVslider1, 0.5f, 0.0f, 1.0f, 0.0250000004f); + ui_interface->declare(&fVslider0, "1", ""); + ui_interface->declare(&fVslider0, "style", "knob"); + ui_interface->declare(&fVslider0, "tooltip", "The room size between 0 and 1 with 1 for the largest room."); + ui_interface->addVerticalSlider("RoomSize", &fVslider0, 0.100000001f, 0.0f, 1.0f, 0.0250000004f); + ui_interface->declare(&fVslider3, "2", ""); + ui_interface->declare(&fVslider3, "style", "knob"); + ui_interface->declare(&fVslider3, "tooltip", "Spatial spread between 0 and 1 with 1 for maximum spread."); + ui_interface->addVerticalSlider("Stereo Spread", &fVslider3, 0.5f, 0.0f, 1.0f, 0.00999999978f); + ui_interface->closeBox(); + ui_interface->declare(&fVslider2, "1", ""); + ui_interface->declare(&fVslider2, "tooltip", "The amount of reverb applied to the signal between 0 and 1 with 1 for the maximum amount of reverb."); + ui_interface->addVerticalSlider("Wet", &fVslider2, 0.100000001f, 0.0f, 1.0f, 0.0250000004f); + ui_interface->closeBox(); + } + + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { + FAUSTFLOAT* input0 = inputs[0]; + FAUSTFLOAT* input1 = inputs[1]; + FAUSTFLOAT* output0 = outputs[0]; + FAUSTFLOAT* output1 = outputs[1]; + float fSlow0 = ((fConst1 * float(fVslider0)) + 0.699999988f); + float fSlow1 = (fConst2 * float(fVslider1)); + float fSlow2 = (1.0f - fSlow1); + float fSlow3 = float(fVslider2); + float fSlow4 = (0.100000001f * fSlow3); + float fSlow5 = (1.0f - fSlow3); + int iSlow6 = int((fConst19 * float(fVslider3))); + int iSlow7 = (iConst3 + iSlow6); + int iSlow8 = (iConst4 + iSlow6); + int iSlow9 = (iConst5 + iSlow6); + int iSlow10 = (iConst6 + iSlow6); + int iSlow11 = (iConst7 + iSlow6); + int iSlow12 = (iConst8 + iSlow6); + int iSlow13 = (iConst9 + iSlow6); + int iSlow14 = (iConst10 + iSlow6); + int iSlow15 = (iSlow6 + -1); + int iSlow16 = std::min<int>(1024, std::max<int>(0, (iConst11 + iSlow15))); + int iSlow17 = std::min<int>(1024, std::max<int>(0, (iConst13 + iSlow15))); + int iSlow18 = std::min<int>(1024, std::max<int>(0, (iConst15 + iSlow15))); + int iSlow19 = std::min<int>(1024, std::max<int>(0, (iConst17 + iSlow15))); + for (int i = 0; (i < count); i = (i + 1)) { + float fTemp0 = float(input0[i]); + float fTemp1 = float(input1[i]); + fRec9[0] = ((fSlow1 * fRec9[1]) + (fSlow2 * fRec8[1])); + float fTemp2 = (fSlow4 * (fTemp0 + fTemp1)); + fVec0[(IOTA & 8191)] = ((fSlow0 * fRec9[0]) + fTemp2); + fRec8[0] = fVec0[((IOTA - iConst3) & 8191)]; + fRec11[0] = ((fSlow1 * fRec11[1]) + (fSlow2 * fRec10[1])); + fVec1[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec11[0])); + fRec10[0] = fVec1[((IOTA - iConst4) & 8191)]; + fRec13[0] = ((fSlow1 * fRec13[1]) + (fSlow2 * fRec12[1])); + fVec2[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec13[0])); + fRec12[0] = fVec2[((IOTA - iConst5) & 8191)]; + fRec15[0] = ((fSlow1 * fRec15[1]) + (fSlow2 * fRec14[1])); + fVec3[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec15[0])); + fRec14[0] = fVec3[((IOTA - iConst6) & 8191)]; + fRec17[0] = ((fSlow1 * fRec17[1]) + (fSlow2 * fRec16[1])); + fVec4[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec17[0])); + fRec16[0] = fVec4[((IOTA - iConst7) & 8191)]; + fRec19[0] = ((fSlow1 * fRec19[1]) + (fSlow2 * fRec18[1])); + fVec5[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec19[0])); + fRec18[0] = fVec5[((IOTA - iConst8) & 8191)]; + fRec21[0] = ((fSlow1 * fRec21[1]) + (fSlow2 * fRec20[1])); + fVec6[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec21[0])); + fRec20[0] = fVec6[((IOTA - iConst9) & 8191)]; + fRec23[0] = ((fSlow1 * fRec23[1]) + (fSlow2 * fRec22[1])); + fVec7[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec23[0])); + fRec22[0] = fVec7[((IOTA - iConst10) & 8191)]; + float fTemp3 = ((((((((fRec8[0] + fRec10[0]) + fRec12[0]) + fRec14[0]) + fRec16[0]) + fRec18[0]) + fRec20[0]) + fRec22[0]) + (0.5f * fRec6[1])); + fVec8[(IOTA & 2047)] = fTemp3; + fRec6[0] = fVec8[((IOTA - iConst12) & 2047)]; + float fRec7 = (0.0f - (0.5f * fTemp3)); + float fTemp4 = (fRec6[1] + (fRec7 + (0.5f * fRec4[1]))); + fVec9[(IOTA & 2047)] = fTemp4; + fRec4[0] = fVec9[((IOTA - iConst14) & 2047)]; + float fRec5 = (0.0f - (0.5f * fTemp4)); + float fTemp5 = (fRec4[1] + (fRec5 + (0.5f * fRec2[1]))); + fVec10[(IOTA & 2047)] = fTemp5; + fRec2[0] = fVec10[((IOTA - iConst16) & 2047)]; + float fRec3 = (0.0f - (0.5f * fTemp5)); + float fTemp6 = (fRec2[1] + (fRec3 + (0.5f * fRec0[1]))); + fVec11[(IOTA & 1023)] = fTemp6; + fRec0[0] = fVec11[((IOTA - iConst18) & 1023)]; + float fRec1 = (0.0f - (0.5f * fTemp6)); + output0[i] = FAUSTFLOAT(((fRec1 + fRec0[1]) + (fSlow5 * fTemp0))); + fRec33[0] = ((fSlow1 * fRec33[1]) + (fSlow2 * fRec32[1])); + fVec12[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec33[0])); + fRec32[0] = fVec12[((IOTA - iSlow7) & 8191)]; + fRec35[0] = ((fSlow1 * fRec35[1]) + (fSlow2 * fRec34[1])); + fVec13[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec35[0])); + fRec34[0] = fVec13[((IOTA - iSlow8) & 8191)]; + fRec37[0] = ((fSlow1 * fRec37[1]) + (fSlow2 * fRec36[1])); + fVec14[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec37[0])); + fRec36[0] = fVec14[((IOTA - iSlow9) & 8191)]; + fRec39[0] = ((fSlow1 * fRec39[1]) + (fSlow2 * fRec38[1])); + fVec15[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec39[0])); + fRec38[0] = fVec15[((IOTA - iSlow10) & 8191)]; + fRec41[0] = ((fSlow1 * fRec41[1]) + (fSlow2 * fRec40[1])); + fVec16[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec41[0])); + fRec40[0] = fVec16[((IOTA - iSlow11) & 8191)]; + fRec43[0] = ((fSlow1 * fRec43[1]) + (fSlow2 * fRec42[1])); + fVec17[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec43[0])); + fRec42[0] = fVec17[((IOTA - iSlow12) & 8191)]; + fRec45[0] = ((fSlow1 * fRec45[1]) + (fSlow2 * fRec44[1])); + fVec18[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec45[0])); + fRec44[0] = fVec18[((IOTA - iSlow13) & 8191)]; + fRec47[0] = ((fSlow1 * fRec47[1]) + (fSlow2 * fRec46[1])); + fVec19[(IOTA & 8191)] = (fTemp2 + (fSlow0 * fRec47[0])); + fRec46[0] = fVec19[((IOTA - iSlow14) & 8191)]; + float fTemp7 = ((((((((fRec32[0] + fRec34[0]) + fRec36[0]) + fRec38[0]) + fRec40[0]) + fRec42[0]) + fRec44[0]) + fRec46[0]) + (0.5f * fRec30[1])); + fVec20[(IOTA & 2047)] = fTemp7; + fRec30[0] = fVec20[((IOTA - iSlow16) & 2047)]; + float fRec31 = (0.0f - (0.5f * fTemp7)); + float fTemp8 = (fRec30[1] + (fRec31 + (0.5f * fRec28[1]))); + fVec21[(IOTA & 2047)] = fTemp8; + fRec28[0] = fVec21[((IOTA - iSlow17) & 2047)]; + float fRec29 = (0.0f - (0.5f * fTemp8)); + float fTemp9 = (fRec28[1] + (fRec29 + (0.5f * fRec26[1]))); + fVec22[(IOTA & 2047)] = fTemp9; + fRec26[0] = fVec22[((IOTA - iSlow18) & 2047)]; + float fRec27 = (0.0f - (0.5f * fTemp9)); + float fTemp10 = (fRec26[1] + (fRec27 + (0.5f * fRec24[1]))); + fVec23[(IOTA & 2047)] = fTemp10; + fRec24[0] = fVec23[((IOTA - iSlow19) & 2047)]; + float fRec25 = (0.0f - (0.5f * fTemp10)); + output1[i] = FAUSTFLOAT(((fRec25 + fRec24[1]) + (fSlow5 * fTemp1))); + fRec9[1] = fRec9[0]; + IOTA = (IOTA + 1); + fRec8[1] = fRec8[0]; + fRec11[1] = fRec11[0]; + fRec10[1] = fRec10[0]; + fRec13[1] = fRec13[0]; + fRec12[1] = fRec12[0]; + fRec15[1] = fRec15[0]; + fRec14[1] = fRec14[0]; + fRec17[1] = fRec17[0]; + fRec16[1] = fRec16[0]; + fRec19[1] = fRec19[0]; + fRec18[1] = fRec18[0]; + fRec21[1] = fRec21[0]; + fRec20[1] = fRec20[0]; + fRec23[1] = fRec23[0]; + fRec22[1] = fRec22[0]; + fRec6[1] = fRec6[0]; + fRec4[1] = fRec4[0]; + fRec2[1] = fRec2[0]; + fRec0[1] = fRec0[0]; + fRec33[1] = fRec33[0]; + fRec32[1] = fRec32[0]; + fRec35[1] = fRec35[0]; + fRec34[1] = fRec34[0]; + fRec37[1] = fRec37[0]; + fRec36[1] = fRec36[0]; + fRec39[1] = fRec39[0]; + fRec38[1] = fRec38[0]; + fRec41[1] = fRec41[0]; + fRec40[1] = fRec40[0]; + fRec43[1] = fRec43[0]; + fRec42[1] = fRec42[0]; + fRec45[1] = fRec45[0]; + fRec44[1] = fRec44[0]; + fRec47[1] = fRec47[0]; + fRec46[1] = fRec46[0]; + fRec30[1] = fRec30[0]; + fRec28[1] = fRec28[0]; + fRec26[1] = fRec26[0]; + fRec24[1] = fRec24[0]; + } + } + +}; + +#endif diff --git a/src/freeverbmonodsp.h b/src/freeverbmonodsp.h new file mode 100644 index 0000000..5de618e --- /dev/null +++ b/src/freeverbmonodsp.h @@ -0,0 +1,2154 @@ +/* ------------------------------------------------------------ +name: "freeverbmonodsp" +Code generated with Faust 2.28.6 (https://faust.grame.fr) +Compilation options: -lang cpp -inpl -scal -ftz 0 +------------------------------------------------------------ */ + +#ifndef __freeverbmonodsp_H__ +#define __freeverbmonodsp_H__ + +// NOTE: ANY INCLUDE-GUARD HERE MUST BE DERIVED FROM THE CLASS NAME +// +// faust2header.cpp - FAUST Architecture File +// This is a simple variation of matlabplot.cpp in the Faust distribution +// aimed at creating a simple C++ header file (.h) containing a Faust DSP. +// See the Makefile for how to use it. + +/************************** BEGIN dsp.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __dsp__ +#define __dsp__ + +#include <string> +#include <vector> + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +struct UI; +struct Meta; + +/** + * DSP memory manager. + */ + +struct dsp_memory_manager { + + virtual ~dsp_memory_manager() {} + + virtual void* allocate(size_t size) = 0; + virtual void destroy(void* ptr) = 0; + +}; + +/** +* Signal processor definition. +*/ + +class dsp { + + public: + + dsp() {} + virtual ~dsp() {} + + /* Return instance number of audio inputs */ + virtual int getNumInputs() = 0; + + /* Return instance number of audio outputs */ + virtual int getNumOutputs() = 0; + + /** + * Trigger the ui_interface parameter with instance specific calls + * to 'openTabBox', 'addButton', 'addVerticalSlider'... in order to build the UI. + * + * @param ui_interface - the user interface builder + */ + virtual void buildUserInterface(UI* ui_interface) = 0; + + /* Returns the sample rate currently used by the instance */ + virtual int getSampleRate() = 0; + + /** + * Global init, calls the following methods: + * - static class 'classInit': static tables initialization + * - 'instanceInit': constants and instance state initialization + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void init(int sample_rate) = 0; + + /** + * Init instance state + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void instanceInit(int sample_rate) = 0; + + /** + * Init instance constant state + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void instanceConstants(int sample_rate) = 0; + + /* Init default control parameters values */ + virtual void instanceResetUserInterface() = 0; + + /* Init instance state (delay lines...) */ + virtual void instanceClear() = 0; + + /** + * Return a clone of the instance. + * + * @return a copy of the instance on success, otherwise a null pointer. + */ + virtual dsp* clone() = 0; + + /** + * Trigger the Meta* parameter with instance specific calls to 'declare' (key, value) metadata. + * + * @param m - the Meta* meta user + */ + virtual void metadata(Meta* m) = 0; + + /** + * DSP instance computation, to be called with successive in/out audio buffers. + * + * @param count - the number of frames to compute + * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) + * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) + * + */ + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0; + + /** + * DSP instance computation: alternative method to be used by subclasses. + * + * @param date_usec - the timestamp in microsec given by audio driver. + * @param count - the number of frames to compute + * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) + * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) + * + */ + virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); } + +}; + +/** + * Generic DSP decorator. + */ + +class decorator_dsp : public dsp { + + protected: + + dsp* fDSP; + + public: + + decorator_dsp(dsp* dsp = nullptr):fDSP(dsp) {} + virtual ~decorator_dsp() { delete fDSP; } + + virtual int getNumInputs() { return fDSP->getNumInputs(); } + virtual int getNumOutputs() { return fDSP->getNumOutputs(); } + virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); } + virtual int getSampleRate() { return fDSP->getSampleRate(); } + virtual void init(int sample_rate) { fDSP->init(sample_rate); } + virtual void instanceInit(int sample_rate) { fDSP->instanceInit(sample_rate); } + virtual void instanceConstants(int sample_rate) { fDSP->instanceConstants(sample_rate); } + virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); } + virtual void instanceClear() { fDSP->instanceClear(); } + virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); } + virtual void metadata(Meta* m) { fDSP->metadata(m); } + // Beware: subclasses usually have to overload the two 'compute' methods + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); } + virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); } + +}; + +/** + * DSP factory class. + */ + +class dsp_factory { + + protected: + + // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory); + virtual ~dsp_factory() {} + + public: + + virtual std::string getName() = 0; + virtual std::string getSHAKey() = 0; + virtual std::string getDSPCode() = 0; + virtual std::string getCompileOptions() = 0; + virtual std::vector<std::string> getLibraryList() = 0; + virtual std::vector<std::string> getIncludePathnames() = 0; + + virtual dsp* createDSPInstance() = 0; + + virtual void setMemoryManager(dsp_memory_manager* manager) = 0; + virtual dsp_memory_manager* getMemoryManager() = 0; + +}; + +/** + * On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero) + * flags to avoid costly denormals. + */ + +#ifdef __SSE__ + #include <xmmintrin.h> + #ifdef __SSE2__ + #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040) + #else + #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000) + #endif +#else + #define AVOIDDENORMALS +#endif + +#endif +/************************** END dsp.h **************************/ + +/************************** BEGIN APIUI.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef API_UI_H +#define API_UI_H + +#include <sstream> +#include <string> +#include <vector> +#include <iostream> +#include <map> + +/************************** BEGIN meta.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __meta__ +#define __meta__ + +struct Meta +{ + virtual ~Meta() {}; + virtual void declare(const char* key, const char* value) = 0; + +}; + +#endif +/************************** END meta.h **************************/ +/************************** BEGIN UI.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2020 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __UI_H__ +#define __UI_H__ + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +/******************************************************************************* + * UI : Faust DSP User Interface + * User Interface as expected by the buildUserInterface() method of a DSP. + * This abstract class contains only the method that the Faust compiler can + * generate to describe a DSP user interface. + ******************************************************************************/ + +struct Soundfile; + +template <typename REAL> +struct UIReal +{ + UIReal() {} + virtual ~UIReal() {} + + // -- widget's layouts + + virtual void openTabBox(const char* label) = 0; + virtual void openHorizontalBox(const char* label) = 0; + virtual void openVerticalBox(const char* label) = 0; + virtual void closeBox() = 0; + + // -- active widgets + + virtual void addButton(const char* label, REAL* zone) = 0; + virtual void addCheckButton(const char* label, REAL* zone) = 0; + virtual void addVerticalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + virtual void addHorizontalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + virtual void addNumEntry(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + + // -- passive widgets + + virtual void addHorizontalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0; + virtual void addVerticalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0; + + // -- soundfiles + + virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) = 0; + + // -- metadata declarations + + virtual void declare(REAL* zone, const char* key, const char* val) {} +}; + +struct UI : public UIReal<FAUSTFLOAT> +{ + UI() {} + virtual ~UI() {} +}; + +#endif +/************************** END UI.h **************************/ +/************************** BEGIN PathBuilder.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef FAUST_PATHBUILDER_H +#define FAUST_PATHBUILDER_H + +#include <vector> +#include <string> +#include <algorithm> + +/******************************************************************************* + * PathBuilder : Faust User Interface + * Helper class to build complete hierarchical path for UI items. + ******************************************************************************/ + +class PathBuilder +{ + + protected: + + std::vector<std::string> fControlsLevel; + + public: + + PathBuilder() {} + virtual ~PathBuilder() {} + + std::string buildPath(const std::string& label) + { + std::string res = "/"; + for (size_t i = 0; i < fControlsLevel.size(); i++) { + res += fControlsLevel[i]; + res += "/"; + } + res += label; + std::replace(res.begin(), res.end(), ' ', '_'); + return res; + } + + std::string buildLabel(std::string label) + { + std::replace(label.begin(), label.end(), ' ', '_'); + return label; + } + + void pushLabel(const std::string& label) { fControlsLevel.push_back(label); } + void popLabel() { fControlsLevel.pop_back(); } + +}; + +#endif // FAUST_PATHBUILDER_H +/************************** END PathBuilder.h **************************/ +/************************** BEGIN ValueConverter.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __ValueConverter__ +#define __ValueConverter__ + +/*************************************************************************************** + ValueConverter.h + (GRAME, Copyright 2015-2019) + +Set of conversion objects used to map user interface values (for example a gui slider +delivering values between 0 and 1) to faust values (for example a vslider between +20 and 20000) using a log scale. + +-- Utilities + +Range(lo,hi) : clip a value x between lo and hi +Interpolator(lo,hi,v1,v2) : Maps a value x between lo and hi to a value y between v1 and v2 +Interpolator3pt(lo,mi,hi,v1,vm,v2) : Map values between lo mid hi to values between v1 vm v2 + +-- Value Converters + +ValueConverter::ui2faust(x) +ValueConverter::faust2ui(x) + +-- ValueConverters used for sliders depending of the scale + +LinearValueConverter(umin, umax, fmin, fmax) +LinearValueConverter2(lo, mi, hi, v1, vm, v2) using 2 segments +LogValueConverter(umin, umax, fmin, fmax) +ExpValueConverter(umin, umax, fmin, fmax) + +-- ValueConverters used for accelerometers based on 3 points + +AccUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 0 +AccDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 1 +AccUpDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 2 +AccDownUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 3 + +-- lists of ZoneControl are used to implement accelerometers metadata for each axes + +ZoneControl(zone, valueConverter) : a zone with an accelerometer data converter + +-- ZoneReader are used to implement screencolor metadata + +ZoneReader(zone, valueConverter) : a zone with a data converter + +****************************************************************************************/ + +#include <float.h> +#include <algorithm> // std::max +#include <cmath> +#include <vector> +#include <assert.h> + +//-------------------------------------------------------------------------------------- +// Interpolator(lo,hi,v1,v2) +// Maps a value x between lo and hi to a value y between v1 and v2 +// y = v1 + (x-lo)/(hi-lo)*(v2-v1) +// y = v1 + (x-lo) * coef with coef = (v2-v1)/(hi-lo) +// y = v1 + x*coef - lo*coef +// y = v1 - lo*coef + x*coef +// y = offset + x*coef with offset = v1 - lo*coef +//-------------------------------------------------------------------------------------- +class Interpolator +{ + private: + + //-------------------------------------------------------------------------------------- + // Range(lo,hi) clip a value between lo and hi + //-------------------------------------------------------------------------------------- + struct Range + { + double fLo; + double fHi; + + Range(double x, double y) : fLo(std::min<double>(x,y)), fHi(std::max<double>(x,y)) {} + double operator()(double x) { return (x<fLo) ? fLo : (x>fHi) ? fHi : x; } + }; + + + Range fRange; + double fCoef; + double fOffset; + + public: + + Interpolator(double lo, double hi, double v1, double v2) : fRange(lo,hi) + { + if (hi != lo) { + // regular case + fCoef = (v2-v1)/(hi-lo); + fOffset = v1 - lo*fCoef; + } else { + // degenerate case, avoids division by zero + fCoef = 0; + fOffset = (v1+v2)/2; + } + } + double operator()(double v) + { + double x = fRange(v); + return fOffset + x*fCoef; + } + + void getLowHigh(double& amin, double& amax) + { + amin = fRange.fLo; + amax = fRange.fHi; + } +}; + +//-------------------------------------------------------------------------------------- +// Interpolator3pt(lo,mi,hi,v1,vm,v2) +// Map values between lo mid hi to values between v1 vm v2 +//-------------------------------------------------------------------------------------- +class Interpolator3pt +{ + + private: + + Interpolator fSegment1; + Interpolator fSegment2; + double fMid; + + public: + + Interpolator3pt(double lo, double mi, double hi, double v1, double vm, double v2) : + fSegment1(lo, mi, v1, vm), + fSegment2(mi, hi, vm, v2), + fMid(mi) {} + double operator()(double x) { return (x < fMid) ? fSegment1(x) : fSegment2(x); } + + void getMappingValues(double& amin, double& amid, double& amax) + { + fSegment1.getLowHigh(amin, amid); + fSegment2.getLowHigh(amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Abstract ValueConverter class. Converts values between UI and Faust representations +//-------------------------------------------------------------------------------------- +class ValueConverter +{ + + public: + + virtual ~ValueConverter() {} + virtual double ui2faust(double x) = 0; + virtual double faust2ui(double x) = 0; +}; + +//-------------------------------------------------------------------------------------- +// A converter than can be updated +//-------------------------------------------------------------------------------------- + +class UpdatableValueConverter : public ValueConverter { + + protected: + + bool fActive; + + public: + + UpdatableValueConverter():fActive(true) + {} + virtual ~UpdatableValueConverter() + {} + + virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) = 0; + virtual void getMappingValues(double& amin, double& amid, double& amax) = 0; + + void setActive(bool on_off) { fActive = on_off; } + bool getActive() { return fActive; } + +}; + + +//-------------------------------------------------------------------------------------- +// Linear conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LinearValueConverter : public ValueConverter +{ + + private: + + Interpolator fUI2F; + Interpolator fF2UI; + + public: + + LinearValueConverter(double umin, double umax, double fmin, double fmax) : + fUI2F(umin,umax,fmin,fmax), fF2UI(fmin,fmax,umin,umax) + {} + + LinearValueConverter() : fUI2F(0.,0.,0.,0.), fF2UI(0.,0.,0.,0.) + {} + virtual double ui2faust(double x) { return fUI2F(x); } + virtual double faust2ui(double x) { return fF2UI(x); } + +}; + +//-------------------------------------------------------------------------------------- +// Two segments linear conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LinearValueConverter2 : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fUI2F; + Interpolator3pt fF2UI; + + public: + + LinearValueConverter2(double amin, double amid, double amax, double min, double init, double max) : + fUI2F(amin, amid, amax, min, init, max), fF2UI(min, init, max, amin, amid, amax) + {} + + LinearValueConverter2() : fUI2F(0.,0.,0.,0.,0.,0.), fF2UI(0.,0.,0.,0.,0.,0.) + {} + + virtual double ui2faust(double x) { return fUI2F(x); } + virtual double faust2ui(double x) { return fF2UI(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) + { + fUI2F = Interpolator3pt(amin, amid, amax, min, init, max); + fF2UI = Interpolator3pt(min, init, max, amin, amid, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fUI2F.getMappingValues(amin, amid, amax); + } + +}; + +//-------------------------------------------------------------------------------------- +// Logarithmic conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LogValueConverter : public LinearValueConverter +{ + + public: + + LogValueConverter(double umin, double umax, double fmin, double fmax) : + LinearValueConverter(umin, umax, std::log(std::max<double>(DBL_MIN, fmin)), std::log(std::max<double>(DBL_MIN, fmax))) + {} + + virtual double ui2faust(double x) { return std::exp(LinearValueConverter::ui2faust(x)); } + virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::log(std::max<double>(x, DBL_MIN))); } + +}; + +//-------------------------------------------------------------------------------------- +// Exponential conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class ExpValueConverter : public LinearValueConverter +{ + + public: + + ExpValueConverter(double umin, double umax, double fmin, double fmax) : + LinearValueConverter(umin, umax, std::min<double>(DBL_MAX, std::exp(fmin)), std::min<double>(DBL_MAX, std::exp(fmax))) + {} + + virtual double ui2faust(double x) { return std::log(LinearValueConverter::ui2faust(x)); } + virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::min<double>(DBL_MAX, std::exp(x))); } + +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using an Up curve (curve 0) +//-------------------------------------------------------------------------------------- +class AccUpConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator3pt fF2A; + + public: + + AccUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmin,fmid,fmax), + fF2A(fmin,fmid,fmax,amin,amid,amax) + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmin, fmid, fmax); + fF2A = Interpolator3pt(fmin, fmid, fmax, amin, amid, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } + +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using a Down curve (curve 1) +//-------------------------------------------------------------------------------------- +class AccDownConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator3pt fF2A; + + public: + + AccDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmax,fmid,fmin), + fF2A(fmin,fmid,fmax,amax,amid,amin) + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmax, fmid, fmin); + fF2A = Interpolator3pt(fmin, fmid, fmax, amax, amid, amin); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using an Up-Down curve (curve 2) +//-------------------------------------------------------------------------------------- +class AccUpDownConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator fF2A; + + public: + + AccUpDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmin,fmax,fmin), + fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotonic function + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmin, fmax, fmin); + fF2A = Interpolator(fmin, fmax, amin, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using a Down-Up curve (curve 3) +//-------------------------------------------------------------------------------------- +class AccDownUpConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator fF2A; + + public: + + AccDownUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmax,fmin,fmax), + fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotonic function + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmax, fmin, fmax); + fF2A = Interpolator(fmin, fmax, amin, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Base class for ZoneControl +//-------------------------------------------------------------------------------------- +class ZoneControl +{ + + protected: + + FAUSTFLOAT* fZone; + + public: + + ZoneControl(FAUSTFLOAT* zone) : fZone(zone) {} + virtual ~ZoneControl() {} + + virtual void update(double v) const {} + + virtual void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) {} + virtual void getMappingValues(double& amin, double& amid, double& amax) {} + + FAUSTFLOAT* getZone() { return fZone; } + + virtual void setActive(bool on_off) {} + virtual bool getActive() { return false; } + + virtual int getCurve() { return -1; } + +}; + +//-------------------------------------------------------------------------------------- +// Useful to implement accelerometers metadata as a list of ZoneControl for each axes +//-------------------------------------------------------------------------------------- +class ConverterZoneControl : public ZoneControl +{ + + protected: + + ValueConverter* fValueConverter; + + public: + + ConverterZoneControl(FAUSTFLOAT* zone, ValueConverter* converter) : ZoneControl(zone), fValueConverter(converter) {} + virtual ~ConverterZoneControl() { delete fValueConverter; } // Assuming fValueConverter is not kept elsewhere... + + virtual void update(double v) const { *fZone = fValueConverter->ui2faust(v); } + + ValueConverter* getConverter() { return fValueConverter; } + +}; + +//-------------------------------------------------------------------------------------- +// Association of a zone and a four value converter, each one for each possible curve. +// Useful to implement accelerometers metadata as a list of ZoneControl for each axes +//-------------------------------------------------------------------------------------- +class CurveZoneControl : public ZoneControl +{ + + private: + + std::vector<UpdatableValueConverter*> fValueConverters; + int fCurve; + + public: + + CurveZoneControl(FAUSTFLOAT* zone, int curve, double amin, double amid, double amax, double min, double init, double max) : ZoneControl(zone), fCurve(0) + { + assert(curve >= 0 && curve <= 3); + fValueConverters.push_back(new AccUpConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccDownConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccUpDownConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccDownUpConverter(amin, amid, amax, min, init, max)); + fCurve = curve; + } + virtual ~CurveZoneControl() + { + std::vector<UpdatableValueConverter*>::iterator it; + for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) { + delete(*it); + } + } + void update(double v) const { if (fValueConverters[fCurve]->getActive()) *fZone = fValueConverters[fCurve]->ui2faust(v); } + + void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) + { + fValueConverters[curve]->setMappingValues(amin, amid, amax, min, init, max); + fCurve = curve; + } + + void getMappingValues(double& amin, double& amid, double& amax) + { + fValueConverters[fCurve]->getMappingValues(amin, amid, amax); + } + + void setActive(bool on_off) + { + std::vector<UpdatableValueConverter*>::iterator it; + for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) { + (*it)->setActive(on_off); + } + } + + int getCurve() { return fCurve; } +}; + +class ZoneReader +{ + + private: + + FAUSTFLOAT* fZone; + Interpolator fInterpolator; + + public: + + ZoneReader(FAUSTFLOAT* zone, double lo, double hi) : fZone(zone), fInterpolator(lo, hi, 0, 255) {} + + virtual ~ZoneReader() {} + + int getValue() + { + return (fZone != nullptr) ? int(fInterpolator(*fZone)) : 127; + } + +}; + +#endif +/************************** END ValueConverter.h **************************/ + +class APIUI : public PathBuilder, public Meta, public UI +{ + public: + + enum ItemType { kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph }; + + protected: + + enum { kLin = 0, kLog = 1, kExp = 2 }; + + int fNumParameters; + std::vector<std::string> fPaths; + std::vector<std::string> fLabels; + std::map<std::string, int> fPathMap; + std::map<std::string, int> fLabelMap; + std::vector<ValueConverter*> fConversion; + std::vector<FAUSTFLOAT*> fZone; + std::vector<FAUSTFLOAT> fInit; + std::vector<FAUSTFLOAT> fMin; + std::vector<FAUSTFLOAT> fMax; + std::vector<FAUSTFLOAT> fStep; + std::vector<ItemType> fItemType; + std::vector<std::map<std::string, std::string> > fMetaData; + std::vector<ZoneControl*> fAcc[3]; + std::vector<ZoneControl*> fGyr[3]; + + // Screen color control + // "...[screencolor:red]..." etc. + bool fHasScreenControl; // true if control screen color metadata + ZoneReader* fRedReader; + ZoneReader* fGreenReader; + ZoneReader* fBlueReader; + + // Current values controlled by metadata + std::string fCurrentUnit; + int fCurrentScale; + std::string fCurrentAcc; + std::string fCurrentGyr; + std::string fCurrentColor; + std::string fCurrentTooltip; + std::map<std::string, std::string> fCurrentMetadata; + + // Add a generic parameter + virtual void addParameter(const char* label, + FAUSTFLOAT* zone, + FAUSTFLOAT init, + FAUSTFLOAT min, + FAUSTFLOAT max, + FAUSTFLOAT step, + ItemType type) + { + std::string path = buildPath(label); + fPathMap[path] = fLabelMap[label] = fNumParameters++; + fPaths.push_back(path); + fLabels.push_back(label); + fZone.push_back(zone); + fInit.push_back(init); + fMin.push_back(min); + fMax.push_back(max); + fStep.push_back(step); + fItemType.push_back(type); + + // handle scale metadata + switch (fCurrentScale) { + case kLin: + fConversion.push_back(new LinearValueConverter(0, 1, min, max)); + break; + case kLog: + fConversion.push_back(new LogValueConverter(0, 1, min, max)); + break; + case kExp: fConversion.push_back(new ExpValueConverter(0, 1, min, max)); + break; + } + fCurrentScale = kLin; + + if (fCurrentAcc.size() > 0 && fCurrentGyr.size() > 0) { + std::cerr << "warning : 'acc' and 'gyr' metadata used for the same " << label << " parameter !!\n"; + } + + // handle acc metadata "...[acc : <axe> <curve> <amin> <amid> <amax>]..." + if (fCurrentAcc.size() > 0) { + std::istringstream iss(fCurrentAcc); + int axe, curve; + double amin, amid, amax; + iss >> axe >> curve >> amin >> amid >> amax; + + if ((0 <= axe) && (axe < 3) && + (0 <= curve) && (curve < 4) && + (amin < amax) && (amin <= amid) && (amid <= amax)) + { + fAcc[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); + } else { + std::cerr << "incorrect acc metadata : " << fCurrentAcc << std::endl; + } + fCurrentAcc = ""; + } + + // handle gyr metadata "...[gyr : <axe> <curve> <amin> <amid> <amax>]..." + if (fCurrentGyr.size() > 0) { + std::istringstream iss(fCurrentGyr); + int axe, curve; + double amin, amid, amax; + iss >> axe >> curve >> amin >> amid >> amax; + + if ((0 <= axe) && (axe < 3) && + (0 <= curve) && (curve < 4) && + (amin < amax) && (amin <= amid) && (amid <= amax)) + { + fGyr[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); + } else { + std::cerr << "incorrect gyr metadata : " << fCurrentGyr << std::endl; + } + fCurrentGyr = ""; + } + + // handle screencolor metadata "...[screencolor:red|green|blue|white]..." + if (fCurrentColor.size() > 0) { + if ((fCurrentColor == "red") && (fRedReader == 0)) { + fRedReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "green") && (fGreenReader == 0)) { + fGreenReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "blue") && (fBlueReader == 0)) { + fBlueReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "white") && (fRedReader == 0) && (fGreenReader == 0) && (fBlueReader == 0)) { + fRedReader = new ZoneReader(zone, min, max); + fGreenReader = new ZoneReader(zone, min, max); + fBlueReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else { + std::cerr << "incorrect screencolor metadata : " << fCurrentColor << std::endl; + } + } + fCurrentColor = ""; + + fMetaData.push_back(fCurrentMetadata); + fCurrentMetadata.clear(); + } + + int getZoneIndex(std::vector<ZoneControl*>* table, int p, int val) + { + FAUSTFLOAT* zone = fZone[p]; + for (size_t i = 0; i < table[val].size(); i++) { + if (zone == table[val][i]->getZone()) return int(i); + } + return -1; + } + + void setConverter(std::vector<ZoneControl*>* table, int p, int val, int curve, double amin, double amid, double amax) + { + int id1 = getZoneIndex(table, p, 0); + int id2 = getZoneIndex(table, p, 1); + int id3 = getZoneIndex(table, p, 2); + + // Deactivates everywhere.. + if (id1 != -1) table[0][id1]->setActive(false); + if (id2 != -1) table[1][id2]->setActive(false); + if (id3 != -1) table[2][id3]->setActive(false); + + if (val == -1) { // Means: no more mapping... + // So stay all deactivated... + } else { + int id4 = getZoneIndex(table, p, val); + if (id4 != -1) { + // Reactivate the one we edit... + table[val][id4]->setMappingValues(curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]); + table[val][id4]->setActive(true); + } else { + // Allocate a new CurveZoneControl which is 'active' by default + FAUSTFLOAT* zone = fZone[p]; + table[val].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, fMin[p], fInit[p], fMax[p])); + } + } + } + + void getConverter(std::vector<ZoneControl*>* table, int p, int& val, int& curve, double& amin, double& amid, double& amax) + { + int id1 = getZoneIndex(table, p, 0); + int id2 = getZoneIndex(table, p, 1); + int id3 = getZoneIndex(table, p, 2); + + if (id1 != -1) { + val = 0; + curve = table[val][id1]->getCurve(); + table[val][id1]->getMappingValues(amin, amid, amax); + } else if (id2 != -1) { + val = 1; + curve = table[val][id2]->getCurve(); + table[val][id2]->getMappingValues(amin, amid, amax); + } else if (id3 != -1) { + val = 2; + curve = table[val][id3]->getCurve(); + table[val][id3]->getMappingValues(amin, amid, amax); + } else { + val = -1; // No mapping + curve = 0; + amin = -100.; + amid = 0.; + amax = 100.; + } + } + + public: + + enum Type { kAcc = 0, kGyr = 1, kNoType }; + + APIUI() : fNumParameters(0), fHasScreenControl(false), fRedReader(0), fGreenReader(0), fBlueReader(0), fCurrentScale(kLin) + {} + + virtual ~APIUI() + { + for (auto& it : fConversion) delete it; + for (int i = 0; i < 3; i++) { + for (auto& it : fAcc[i]) delete it; + for (auto& it : fGyr[i]) delete it; + } + delete fRedReader; + delete fGreenReader; + delete fBlueReader; + } + + // -- widget's layouts + + virtual void openTabBox(const char* label) { pushLabel(label); } + virtual void openHorizontalBox(const char* label) { pushLabel(label); } + virtual void openVerticalBox(const char* label) { pushLabel(label); } + virtual void closeBox() { popLabel(); } + + // -- active widgets + + virtual void addButton(const char* label, FAUSTFLOAT* zone) + { + addParameter(label, zone, 0, 0, 1, 1, kButton); + } + + virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) + { + addParameter(label, zone, 0, 0, 1, 1, kCheckButton); + } + + virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kVSlider); + } + + virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kHSlider); + } + + virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kNumEntry); + } + + // -- passive widgets + + virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) + { + addParameter(label, zone, min, min, max, (max-min)/1000.0, kHBargraph); + } + + virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) + { + addParameter(label, zone, min, min, max, (max-min)/1000.0, kVBargraph); + } + + // -- soundfiles + + virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) {} + + // -- metadata declarations + + virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val) + { + // Keep metadata + fCurrentMetadata[key] = val; + + if (strcmp(key, "scale") == 0) { + if (strcmp(val, "log") == 0) { + fCurrentScale = kLog; + } else if (strcmp(val, "exp") == 0) { + fCurrentScale = kExp; + } else { + fCurrentScale = kLin; + } + } else if (strcmp(key, "unit") == 0) { + fCurrentUnit = val; + } else if (strcmp(key, "acc") == 0) { + fCurrentAcc = val; + } else if (strcmp(key, "gyr") == 0) { + fCurrentGyr = val; + } else if (strcmp(key, "screencolor") == 0) { + fCurrentColor = val; // val = "red", "green", "blue" or "white" + } else if (strcmp(key, "tooltip") == 0) { + fCurrentTooltip = val; + } + } + + virtual void declare(const char* key, const char* val) + {} + + //------------------------------------------------------------------------------- + // Simple API part + //------------------------------------------------------------------------------- + int getParamsCount() { return fNumParameters; } + int getParamIndex(const char* path) + { + if (fPathMap.find(path) != fPathMap.end()) { + return fPathMap[path]; + } else if (fLabelMap.find(path) != fLabelMap.end()) { + return fLabelMap[path]; + } else { + return -1; + } + } + const char* getParamAddress(int p) { return fPaths[p].c_str(); } + const char* getParamLabel(int p) { return fLabels[p].c_str(); } + std::map<const char*, const char*> getMetadata(int p) + { + std::map<const char*, const char*> res; + std::map<std::string, std::string> metadata = fMetaData[p]; + for (auto it : metadata) { + res[it.first.c_str()] = it.second.c_str(); + } + return res; + } + + const char* getMetadata(int p, const char* key) + { + return (fMetaData[p].find(key) != fMetaData[p].end()) ? fMetaData[p][key].c_str() : ""; + } + FAUSTFLOAT getParamMin(int p) { return fMin[p]; } + FAUSTFLOAT getParamMax(int p) { return fMax[p]; } + FAUSTFLOAT getParamStep(int p) { return fStep[p]; } + FAUSTFLOAT getParamInit(int p) { return fInit[p]; } + + FAUSTFLOAT* getParamZone(int p) { return fZone[p]; } + FAUSTFLOAT getParamValue(int p) { return *fZone[p]; } + void setParamValue(int p, FAUSTFLOAT v) { *fZone[p] = v; } + + double getParamRatio(int p) { return fConversion[p]->faust2ui(*fZone[p]); } + void setParamRatio(int p, double r) { *fZone[p] = fConversion[p]->ui2faust(r); } + + double value2ratio(int p, double r) { return fConversion[p]->faust2ui(r); } + double ratio2value(int p, double r) { return fConversion[p]->ui2faust(r); } + + /** + * Return the control type (kAcc, kGyr, or -1) for a given parameter + * + * @param p - the UI parameter index + * + * @return the type + */ + Type getParamType(int p) + { + if (p >= 0) { + if (getZoneIndex(fAcc, p, 0) != -1 + || getZoneIndex(fAcc, p, 1) != -1 + || getZoneIndex(fAcc, p, 2) != -1) { + return kAcc; + } else if (getZoneIndex(fGyr, p, 0) != -1 + || getZoneIndex(fGyr, p, 1) != -1 + || getZoneIndex(fGyr, p, 2) != -1) { + return kGyr; + } + } + return kNoType; + } + + /** + * Return the Item type (kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph) for a given parameter + * + * @param p - the UI parameter index + * + * @return the Item type + */ + ItemType getParamItemType(int p) + { + return fItemType[p]; + } + + /** + * Set a new value coming from an accelerometer, propagate it to all relevant FAUSTFLOAT* zones. + * + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer + * @param value - the new value + * + */ + void propagateAcc(int acc, double value) + { + for (size_t i = 0; i < fAcc[acc].size(); i++) { + fAcc[acc][i]->update(value); + } + } + + /** + * Used to edit accelerometer curves and mapping. Set curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer (-1 means "no mapping") + * @param curve - between 0 and 3 + * @param amin - mapping 'min' point + * @param amid - mapping 'middle' point + * @param amax - mapping 'max' point + * + */ + void setAccConverter(int p, int acc, int curve, double amin, double amid, double amax) + { + setConverter(fAcc, p, acc, curve, amin, amid, amax); + } + + /** + * Used to edit gyroscope curves and mapping. Set curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope (-1 means "no mapping") + * @param curve - between 0 and 3 + * @param amin - mapping 'min' point + * @param amid - mapping 'middle' point + * @param amax - mapping 'max' point + * + */ + void setGyrConverter(int p, int gyr, int curve, double amin, double amid, double amax) + { + setConverter(fGyr, p, gyr, curve, amin, amid, amax); + } + + /** + * Used to edit accelerometer curves and mapping. Get curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - the acc value to be retrieved (-1 means "no mapping") + * @param curve - the curve value to be retrieved + * @param amin - the amin value to be retrieved + * @param amid - the amid value to be retrieved + * @param amax - the amax value to be retrieved + * + */ + void getAccConverter(int p, int& acc, int& curve, double& amin, double& amid, double& amax) + { + getConverter(fAcc, p, acc, curve, amin, amid, amax); + } + + /** + * Used to edit gyroscope curves and mapping. Get curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param gyr - the gyr value to be retrieved (-1 means "no mapping") + * @param curve - the curve value to be retrieved + * @param amin - the amin value to be retrieved + * @param amid - the amid value to be retrieved + * @param amax - the amax value to be retrieved + * + */ + void getGyrConverter(int p, int& gyr, int& curve, double& amin, double& amid, double& amax) + { + getConverter(fGyr, p, gyr, curve, amin, amid, amax); + } + + /** + * Set a new value coming from an gyroscope, propagate it to all relevant FAUSTFLOAT* zones. + * + * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope + * @param value - the new value + * + */ + void propagateGyr(int gyr, double value) + { + for (size_t i = 0; i < fGyr[gyr].size(); i++) { + fGyr[gyr][i]->update(value); + } + } + + /** + * Get the number of FAUSTFLOAT* zones controlled with the accelerometer + * + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer + * @return the number of zones + * + */ + int getAccCount(int acc) + { + return (acc >= 0 && acc < 3) ? int(fAcc[acc].size()) : 0; + } + + /** + * Get the number of FAUSTFLOAT* zones controlled with the gyroscope + * + * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope + * @param the number of zones + * + */ + int getGyrCount(int gyr) + { + return (gyr >= 0 && gyr < 3) ? int(fGyr[gyr].size()) : 0; + } + + // getScreenColor() : -1 means no screen color control (no screencolor metadata found) + // otherwise return 0x00RRGGBB a ready to use color + int getScreenColor() + { + if (fHasScreenControl) { + int r = (fRedReader) ? fRedReader->getValue() : 0; + int g = (fGreenReader) ? fGreenReader->getValue() : 0; + int b = (fBlueReader) ? fBlueReader->getValue() : 0; + return (r<<16) | (g<<8) | b; + } else { + return -1; + } + } + +}; + +#endif +/************************** END APIUI.h **************************/ + +// NOTE: "faust -scn name" changes the last line above to +// #include <faust/name/name.h> + +//---------------------------------------------------------------------------- +// FAUST Generated Code +//---------------------------------------------------------------------------- + + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +#include <algorithm> +#include <cmath> +#include <math.h> + + +#ifndef FAUSTCLASS +#define FAUSTCLASS freeverbmonodsp +#endif + +#ifdef __APPLE__ +#define exp10f __exp10f +#define exp10 __exp10 +#endif + +class freeverbmonodsp : public dsp { + + private: + + FAUSTFLOAT fVslider0; + int fSampleRate; + float fConst0; + float fConst1; + FAUSTFLOAT fVslider1; + float fConst2; + FAUSTFLOAT fVslider2; + float fRec9[2]; + int IOTA; + float fVec0[8192]; + int iConst3; + float fConst4; + FAUSTFLOAT fVslider3; + float fRec8[2]; + float fRec11[2]; + float fVec1[8192]; + int iConst5; + float fRec10[2]; + float fRec13[2]; + float fVec2[8192]; + int iConst6; + float fRec12[2]; + float fRec15[2]; + float fVec3[8192]; + int iConst7; + float fRec14[2]; + float fRec17[2]; + float fVec4[8192]; + int iConst8; + float fRec16[2]; + float fRec19[2]; + float fVec5[8192]; + int iConst9; + float fRec18[2]; + float fRec21[2]; + float fVec6[8192]; + int iConst10; + float fRec20[2]; + float fRec23[2]; + float fVec7[8192]; + int iConst11; + float fRec22[2]; + float fVec8[2048]; + int iConst12; + float fRec6[2]; + float fVec9[2048]; + int iConst13; + float fRec4[2]; + float fVec10[2048]; + int iConst14; + float fRec2[2]; + float fVec11[2048]; + int iConst15; + float fRec0[2]; + float fRec33[2]; + float fVec12[8192]; + float fRec32[2]; + float fRec35[2]; + float fVec13[8192]; + float fRec34[2]; + float fRec37[2]; + float fVec14[8192]; + float fRec36[2]; + float fRec39[2]; + float fVec15[8192]; + float fRec38[2]; + float fRec41[2]; + float fVec16[8192]; + float fRec40[2]; + float fRec43[2]; + float fVec17[8192]; + float fRec42[2]; + float fRec45[2]; + float fVec18[8192]; + float fRec44[2]; + float fRec47[2]; + float fVec19[8192]; + float fRec46[2]; + float fVec20[2048]; + int iConst16; + float fRec30[2]; + float fVec21[2048]; + int iConst17; + float fRec28[2]; + float fVec22[2048]; + int iConst18; + float fRec26[2]; + float fVec23[1024]; + int iConst19; + float fRec24[2]; + + public: + + void metadata(Meta* m) { + m->declare("delays.lib/name", "Faust Delay Library"); + m->declare("delays.lib/version", "0.1"); + m->declare("filename", "freeverbmonodsp.dsp"); + m->declare("filters.lib/allpass_comb:author", "Julius O. Smith III"); + m->declare("filters.lib/allpass_comb:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/allpass_comb:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/lowpass0_highpass1", "MIT-style STK-4.3 license"); + m->declare("filters.lib/name", "Faust Filters Library"); + m->declare("freeverbdsp.dsp/author", "Romain Michon"); + m->declare("freeverbdsp.dsp/description", "Freeverb implementation in Faust, from the Faust Library's dm.freeverb_demo in demos.lib"); + m->declare("freeverbdsp.dsp/license", "LGPL"); + m->declare("freeverbdsp.dsp/name", "freeverb"); + m->declare("freeverbdsp.dsp/version", "0.0"); + m->declare("maths.lib/author", "GRAME"); + m->declare("maths.lib/copyright", "GRAME"); + m->declare("maths.lib/license", "LGPL with exception"); + m->declare("maths.lib/name", "Faust Math Library"); + m->declare("maths.lib/version", "2.3"); + m->declare("name", "freeverbmonodsp"); + m->declare("platform.lib/name", "Generic Platform Library"); + m->declare("platform.lib/version", "0.1"); + m->declare("reverbs.lib/name", "Faust Reverb Library"); + m->declare("reverbs.lib/version", "0.0"); + } + + virtual int getNumInputs() { + return 1; + } + virtual int getNumOutputs() { + return 1; + } + virtual int getInputRate(int channel) { + int rate; + switch ((channel)) { + case 0: { + rate = 1; + break; + } + default: { + rate = -1; + break; + } + } + return rate; + } + virtual int getOutputRate(int channel) { + int rate; + switch ((channel)) { + case 0: { + rate = 1; + break; + } + default: { + rate = -1; + break; + } + } + return rate; + } + + static void classInit(int sample_rate) { + } + + virtual void instanceConstants(int sample_rate) { + fSampleRate = sample_rate; + fConst0 = std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate))); + fConst1 = (12348.0f / fConst0); + fConst2 = (17640.0f / fConst0); + iConst3 = int((0.0253061224f * fConst0)); + fConst4 = (0.00104308384f * fConst0); + iConst5 = int((0.0269387756f * fConst0)); + iConst6 = int((0.0289569162f * fConst0)); + iConst7 = int((0.0307482984f * fConst0)); + iConst8 = int((0.0322448984f * fConst0)); + iConst9 = int((0.033809524f * fConst0)); + iConst10 = int((0.0353061222f * fConst0)); + iConst11 = int((0.0366666652f * fConst0)); + iConst12 = int((0.0126077095f * fConst0)); + iConst13 = int((0.00999999978f * fConst0)); + iConst14 = int((0.00773242628f * fConst0)); + iConst15 = int((0.00510204071f * fConst0)); + iConst16 = std::min<int>(1024, std::max<int>(0, (iConst12 + -1))); + iConst17 = std::min<int>(1024, std::max<int>(0, (iConst13 + -1))); + iConst18 = std::min<int>(1024, std::max<int>(0, (iConst14 + -1))); + iConst19 = std::min<int>(1024, std::max<int>(0, (iConst15 + -1))); + } + + virtual void instanceResetUserInterface() { + fVslider0 = FAUSTFLOAT(0.10000000000000001f); + fVslider1 = FAUSTFLOAT(0.10000000000000001f); + fVslider2 = FAUSTFLOAT(0.5f); + fVslider3 = FAUSTFLOAT(0.5f); + } + + virtual void instanceClear() { + for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { + fRec9[l0] = 0.0f; + } + IOTA = 0; + for (int l1 = 0; (l1 < 8192); l1 = (l1 + 1)) { + fVec0[l1] = 0.0f; + } + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec8[l2] = 0.0f; + } + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fRec11[l3] = 0.0f; + } + for (int l4 = 0; (l4 < 8192); l4 = (l4 + 1)) { + fVec1[l4] = 0.0f; + } + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fRec10[l5] = 0.0f; + } + for (int l6 = 0; (l6 < 2); l6 = (l6 + 1)) { + fRec13[l6] = 0.0f; + } + for (int l7 = 0; (l7 < 8192); l7 = (l7 + 1)) { + fVec2[l7] = 0.0f; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec12[l8] = 0.0f; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec15[l9] = 0.0f; + } + for (int l10 = 0; (l10 < 8192); l10 = (l10 + 1)) { + fVec3[l10] = 0.0f; + } + for (int l11 = 0; (l11 < 2); l11 = (l11 + 1)) { + fRec14[l11] = 0.0f; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fRec17[l12] = 0.0f; + } + for (int l13 = 0; (l13 < 8192); l13 = (l13 + 1)) { + fVec4[l13] = 0.0f; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fRec16[l14] = 0.0f; + } + for (int l15 = 0; (l15 < 2); l15 = (l15 + 1)) { + fRec19[l15] = 0.0f; + } + for (int l16 = 0; (l16 < 8192); l16 = (l16 + 1)) { + fVec5[l16] = 0.0f; + } + for (int l17 = 0; (l17 < 2); l17 = (l17 + 1)) { + fRec18[l17] = 0.0f; + } + for (int l18 = 0; (l18 < 2); l18 = (l18 + 1)) { + fRec21[l18] = 0.0f; + } + for (int l19 = 0; (l19 < 8192); l19 = (l19 + 1)) { + fVec6[l19] = 0.0f; + } + for (int l20 = 0; (l20 < 2); l20 = (l20 + 1)) { + fRec20[l20] = 0.0f; + } + for (int l21 = 0; (l21 < 2); l21 = (l21 + 1)) { + fRec23[l21] = 0.0f; + } + for (int l22 = 0; (l22 < 8192); l22 = (l22 + 1)) { + fVec7[l22] = 0.0f; + } + for (int l23 = 0; (l23 < 2); l23 = (l23 + 1)) { + fRec22[l23] = 0.0f; + } + for (int l24 = 0; (l24 < 2048); l24 = (l24 + 1)) { + fVec8[l24] = 0.0f; + } + for (int l25 = 0; (l25 < 2); l25 = (l25 + 1)) { + fRec6[l25] = 0.0f; + } + for (int l26 = 0; (l26 < 2048); l26 = (l26 + 1)) { + fVec9[l26] = 0.0f; + } + for (int l27 = 0; (l27 < 2); l27 = (l27 + 1)) { + fRec4[l27] = 0.0f; + } + for (int l28 = 0; (l28 < 2048); l28 = (l28 + 1)) { + fVec10[l28] = 0.0f; + } + for (int l29 = 0; (l29 < 2); l29 = (l29 + 1)) { + fRec2[l29] = 0.0f; + } + for (int l30 = 0; (l30 < 2048); l30 = (l30 + 1)) { + fVec11[l30] = 0.0f; + } + for (int l31 = 0; (l31 < 2); l31 = (l31 + 1)) { + fRec0[l31] = 0.0f; + } + for (int l32 = 0; (l32 < 2); l32 = (l32 + 1)) { + fRec33[l32] = 0.0f; + } + for (int l33 = 0; (l33 < 8192); l33 = (l33 + 1)) { + fVec12[l33] = 0.0f; + } + for (int l34 = 0; (l34 < 2); l34 = (l34 + 1)) { + fRec32[l34] = 0.0f; + } + for (int l35 = 0; (l35 < 2); l35 = (l35 + 1)) { + fRec35[l35] = 0.0f; + } + for (int l36 = 0; (l36 < 8192); l36 = (l36 + 1)) { + fVec13[l36] = 0.0f; + } + for (int l37 = 0; (l37 < 2); l37 = (l37 + 1)) { + fRec34[l37] = 0.0f; + } + for (int l38 = 0; (l38 < 2); l38 = (l38 + 1)) { + fRec37[l38] = 0.0f; + } + for (int l39 = 0; (l39 < 8192); l39 = (l39 + 1)) { + fVec14[l39] = 0.0f; + } + for (int l40 = 0; (l40 < 2); l40 = (l40 + 1)) { + fRec36[l40] = 0.0f; + } + for (int l41 = 0; (l41 < 2); l41 = (l41 + 1)) { + fRec39[l41] = 0.0f; + } + for (int l42 = 0; (l42 < 8192); l42 = (l42 + 1)) { + fVec15[l42] = 0.0f; + } + for (int l43 = 0; (l43 < 2); l43 = (l43 + 1)) { + fRec38[l43] = 0.0f; + } + for (int l44 = 0; (l44 < 2); l44 = (l44 + 1)) { + fRec41[l44] = 0.0f; + } + for (int l45 = 0; (l45 < 8192); l45 = (l45 + 1)) { + fVec16[l45] = 0.0f; + } + for (int l46 = 0; (l46 < 2); l46 = (l46 + 1)) { + fRec40[l46] = 0.0f; + } + for (int l47 = 0; (l47 < 2); l47 = (l47 + 1)) { + fRec43[l47] = 0.0f; + } + for (int l48 = 0; (l48 < 8192); l48 = (l48 + 1)) { + fVec17[l48] = 0.0f; + } + for (int l49 = 0; (l49 < 2); l49 = (l49 + 1)) { + fRec42[l49] = 0.0f; + } + for (int l50 = 0; (l50 < 2); l50 = (l50 + 1)) { + fRec45[l50] = 0.0f; + } + for (int l51 = 0; (l51 < 8192); l51 = (l51 + 1)) { + fVec18[l51] = 0.0f; + } + for (int l52 = 0; (l52 < 2); l52 = (l52 + 1)) { + fRec44[l52] = 0.0f; + } + for (int l53 = 0; (l53 < 2); l53 = (l53 + 1)) { + fRec47[l53] = 0.0f; + } + for (int l54 = 0; (l54 < 8192); l54 = (l54 + 1)) { + fVec19[l54] = 0.0f; + } + for (int l55 = 0; (l55 < 2); l55 = (l55 + 1)) { + fRec46[l55] = 0.0f; + } + for (int l56 = 0; (l56 < 2048); l56 = (l56 + 1)) { + fVec20[l56] = 0.0f; + } + for (int l57 = 0; (l57 < 2); l57 = (l57 + 1)) { + fRec30[l57] = 0.0f; + } + for (int l58 = 0; (l58 < 2048); l58 = (l58 + 1)) { + fVec21[l58] = 0.0f; + } + for (int l59 = 0; (l59 < 2); l59 = (l59 + 1)) { + fRec28[l59] = 0.0f; + } + for (int l60 = 0; (l60 < 2048); l60 = (l60 + 1)) { + fVec22[l60] = 0.0f; + } + for (int l61 = 0; (l61 < 2); l61 = (l61 + 1)) { + fRec26[l61] = 0.0f; + } + for (int l62 = 0; (l62 < 1024); l62 = (l62 + 1)) { + fVec23[l62] = 0.0f; + } + for (int l63 = 0; (l63 < 2); l63 = (l63 + 1)) { + fRec24[l63] = 0.0f; + } + } + + virtual void init(int sample_rate) { + classInit(sample_rate); + instanceInit(sample_rate); + } + virtual void instanceInit(int sample_rate) { + instanceConstants(sample_rate); + instanceResetUserInterface(); + instanceClear(); + } + + virtual freeverbmonodsp* clone() { + return new freeverbmonodsp(); + } + + virtual int getSampleRate() { + return fSampleRate; + } + + virtual void buildUserInterface(UI* ui_interface) { + ui_interface->openHorizontalBox("Freeverb"); + ui_interface->declare(0, "0", ""); + ui_interface->openVerticalBox("0x00"); + ui_interface->declare(&fVslider2, "0", ""); + ui_interface->declare(&fVslider2, "style", "knob"); + ui_interface->declare(&fVslider2, "tooltip", "Somehow control the density of the reverb."); + ui_interface->addVerticalSlider("Damp", &fVslider2, 0.5f, 0.0f, 1.0f, 0.0250000004f); + ui_interface->declare(&fVslider1, "1", ""); + ui_interface->declare(&fVslider1, "style", "knob"); + ui_interface->declare(&fVslider1, "tooltip", "The room size between 0 and 1 with 1 for the largest room."); + ui_interface->addVerticalSlider("RoomSize", &fVslider1, 0.100000001f, 0.0f, 1.0f, 0.0250000004f); + ui_interface->declare(&fVslider3, "2", ""); + ui_interface->declare(&fVslider3, "style", "knob"); + ui_interface->declare(&fVslider3, "tooltip", "Spatial spread between 0 and 1 with 1 for maximum spread."); + ui_interface->addVerticalSlider("Stereo Spread", &fVslider3, 0.5f, 0.0f, 1.0f, 0.00999999978f); + ui_interface->closeBox(); + ui_interface->declare(&fVslider0, "1", ""); + ui_interface->declare(&fVslider0, "tooltip", "The amount of reverb applied to the signal between 0 and 1 with 1 for the maximum amount of reverb."); + ui_interface->addVerticalSlider("Wet", &fVslider0, 0.100000001f, 0.0f, 1.0f, 0.0250000004f); + ui_interface->closeBox(); + } + + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { + FAUSTFLOAT* input0 = inputs[0]; + FAUSTFLOAT* output0 = outputs[0]; + float fSlow0 = float(fVslider0); + float fSlow1 = (0.200000003f * fSlow0); + float fSlow2 = ((fConst1 * float(fVslider1)) + 0.699999988f); + float fSlow3 = (fConst2 * float(fVslider2)); + float fSlow4 = (1.0f - fSlow3); + int iSlow5 = int((fConst4 * float(fVslider3))); + int iSlow6 = (iConst3 + iSlow5); + int iSlow7 = (iConst5 + iSlow5); + int iSlow8 = (iConst6 + iSlow5); + int iSlow9 = (iConst7 + iSlow5); + int iSlow10 = (iConst8 + iSlow5); + int iSlow11 = (iConst9 + iSlow5); + int iSlow12 = (iConst10 + iSlow5); + int iSlow13 = (iConst11 + iSlow5); + int iSlow14 = (iSlow5 + -1); + int iSlow15 = std::min<int>(1024, std::max<int>(0, (iConst12 + iSlow14))); + int iSlow16 = std::min<int>(1024, std::max<int>(0, (iConst13 + iSlow14))); + int iSlow17 = std::min<int>(1024, std::max<int>(0, (iConst14 + iSlow14))); + int iSlow18 = std::min<int>(1024, std::max<int>(0, (iConst15 + iSlow14))); + float fSlow19 = (2.0f * (1.0f - fSlow0)); + for (int i = 0; (i < count); i = (i + 1)) { + float fTemp0 = float(input0[i]); + float fTemp1 = (fSlow1 * fTemp0); + fRec9[0] = ((fSlow3 * fRec9[1]) + (fSlow4 * fRec8[1])); + fVec0[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec9[0])); + fRec8[0] = fVec0[((IOTA - iSlow6) & 8191)]; + fRec11[0] = ((fSlow3 * fRec11[1]) + (fSlow4 * fRec10[1])); + fVec1[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec11[0])); + fRec10[0] = fVec1[((IOTA - iSlow7) & 8191)]; + fRec13[0] = ((fSlow3 * fRec13[1]) + (fSlow4 * fRec12[1])); + fVec2[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec13[0])); + fRec12[0] = fVec2[((IOTA - iSlow8) & 8191)]; + fRec15[0] = ((fSlow3 * fRec15[1]) + (fSlow4 * fRec14[1])); + fVec3[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec15[0])); + fRec14[0] = fVec3[((IOTA - iSlow9) & 8191)]; + fRec17[0] = ((fSlow3 * fRec17[1]) + (fSlow4 * fRec16[1])); + fVec4[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec17[0])); + fRec16[0] = fVec4[((IOTA - iSlow10) & 8191)]; + fRec19[0] = ((fSlow3 * fRec19[1]) + (fSlow4 * fRec18[1])); + fVec5[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec19[0])); + fRec18[0] = fVec5[((IOTA - iSlow11) & 8191)]; + fRec21[0] = ((fSlow3 * fRec21[1]) + (fSlow4 * fRec20[1])); + fVec6[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec21[0])); + fRec20[0] = fVec6[((IOTA - iSlow12) & 8191)]; + fRec23[0] = ((fSlow3 * fRec23[1]) + (fSlow4 * fRec22[1])); + fVec7[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec23[0])); + fRec22[0] = fVec7[((IOTA - iSlow13) & 8191)]; + float fTemp2 = ((((((((fRec8[0] + fRec10[0]) + fRec12[0]) + fRec14[0]) + fRec16[0]) + fRec18[0]) + fRec20[0]) + fRec22[0]) + (0.5f * fRec6[1])); + fVec8[(IOTA & 2047)] = fTemp2; + fRec6[0] = fVec8[((IOTA - iSlow15) & 2047)]; + float fRec7 = (0.0f - (0.5f * fTemp2)); + float fTemp3 = (fRec6[1] + (fRec7 + (0.5f * fRec4[1]))); + fVec9[(IOTA & 2047)] = fTemp3; + fRec4[0] = fVec9[((IOTA - iSlow16) & 2047)]; + float fRec5 = (0.0f - (0.5f * fTemp3)); + float fTemp4 = (fRec4[1] + (fRec5 + (0.5f * fRec2[1]))); + fVec10[(IOTA & 2047)] = fTemp4; + fRec2[0] = fVec10[((IOTA - iSlow17) & 2047)]; + float fRec3 = (0.0f - (0.5f * fTemp4)); + float fTemp5 = (fRec2[1] + (fRec3 + (0.5f * fRec0[1]))); + fVec11[(IOTA & 2047)] = fTemp5; + fRec0[0] = fVec11[((IOTA - iSlow18) & 2047)]; + float fRec1 = (0.0f - (0.5f * fTemp5)); + fRec33[0] = ((fSlow3 * fRec33[1]) + (fSlow4 * fRec32[1])); + fVec12[(IOTA & 8191)] = ((fSlow2 * fRec33[0]) + fTemp1); + fRec32[0] = fVec12[((IOTA - iConst3) & 8191)]; + fRec35[0] = ((fSlow3 * fRec35[1]) + (fSlow4 * fRec34[1])); + fVec13[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec35[0])); + fRec34[0] = fVec13[((IOTA - iConst5) & 8191)]; + fRec37[0] = ((fSlow3 * fRec37[1]) + (fSlow4 * fRec36[1])); + fVec14[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec37[0])); + fRec36[0] = fVec14[((IOTA - iConst6) & 8191)]; + fRec39[0] = ((fSlow3 * fRec39[1]) + (fSlow4 * fRec38[1])); + fVec15[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec39[0])); + fRec38[0] = fVec15[((IOTA - iConst7) & 8191)]; + fRec41[0] = ((fSlow3 * fRec41[1]) + (fSlow4 * fRec40[1])); + fVec16[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec41[0])); + fRec40[0] = fVec16[((IOTA - iConst8) & 8191)]; + fRec43[0] = ((fSlow3 * fRec43[1]) + (fSlow4 * fRec42[1])); + fVec17[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec43[0])); + fRec42[0] = fVec17[((IOTA - iConst9) & 8191)]; + fRec45[0] = ((fSlow3 * fRec45[1]) + (fSlow4 * fRec44[1])); + fVec18[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec45[0])); + fRec44[0] = fVec18[((IOTA - iConst10) & 8191)]; + fRec47[0] = ((fSlow3 * fRec47[1]) + (fSlow4 * fRec46[1])); + fVec19[(IOTA & 8191)] = (fTemp1 + (fSlow2 * fRec47[0])); + fRec46[0] = fVec19[((IOTA - iConst11) & 8191)]; + float fTemp6 = ((((((((fRec32[0] + fRec34[0]) + fRec36[0]) + fRec38[0]) + fRec40[0]) + fRec42[0]) + fRec44[0]) + fRec46[0]) + (0.5f * fRec30[1])); + fVec20[(IOTA & 2047)] = fTemp6; + fRec30[0] = fVec20[((IOTA - iConst16) & 2047)]; + float fRec31 = (0.0f - (0.5f * fTemp6)); + float fTemp7 = (fRec30[1] + (fRec31 + (0.5f * fRec28[1]))); + fVec21[(IOTA & 2047)] = fTemp7; + fRec28[0] = fVec21[((IOTA - iConst17) & 2047)]; + float fRec29 = (0.0f - (0.5f * fTemp7)); + float fTemp8 = (fRec28[1] + (fRec29 + (0.5f * fRec26[1]))); + fVec22[(IOTA & 2047)] = fTemp8; + fRec26[0] = fVec22[((IOTA - iConst18) & 2047)]; + float fRec27 = (0.0f - (0.5f * fTemp8)); + float fTemp9 = (fRec26[1] + (fRec27 + (0.5f * fRec24[1]))); + fVec23[(IOTA & 1023)] = fTemp9; + fRec24[0] = fVec23[((IOTA - iConst19) & 1023)]; + float fRec25 = (0.0f - (0.5f * fTemp9)); + output0[i] = FAUSTFLOAT((fRec0[1] + ((fRec24[1] + (fRec25 + fRec1)) + (fSlow19 * fTemp0)))); + fRec9[1] = fRec9[0]; + IOTA = (IOTA + 1); + fRec8[1] = fRec8[0]; + fRec11[1] = fRec11[0]; + fRec10[1] = fRec10[0]; + fRec13[1] = fRec13[0]; + fRec12[1] = fRec12[0]; + fRec15[1] = fRec15[0]; + fRec14[1] = fRec14[0]; + fRec17[1] = fRec17[0]; + fRec16[1] = fRec16[0]; + fRec19[1] = fRec19[0]; + fRec18[1] = fRec18[0]; + fRec21[1] = fRec21[0]; + fRec20[1] = fRec20[0]; + fRec23[1] = fRec23[0]; + fRec22[1] = fRec22[0]; + fRec6[1] = fRec6[0]; + fRec4[1] = fRec4[0]; + fRec2[1] = fRec2[0]; + fRec0[1] = fRec0[0]; + fRec33[1] = fRec33[0]; + fRec32[1] = fRec32[0]; + fRec35[1] = fRec35[0]; + fRec34[1] = fRec34[0]; + fRec37[1] = fRec37[0]; + fRec36[1] = fRec36[0]; + fRec39[1] = fRec39[0]; + fRec38[1] = fRec38[0]; + fRec41[1] = fRec41[0]; + fRec40[1] = fRec40[0]; + fRec43[1] = fRec43[0]; + fRec42[1] = fRec42[0]; + fRec45[1] = fRec45[0]; + fRec44[1] = fRec44[0]; + fRec47[1] = fRec47[0]; + fRec46[1] = fRec46[0]; + fRec30[1] = fRec30[0]; + fRec28[1] = fRec28[0]; + fRec26[1] = fRec26[0]; + fRec24[1] = fRec24[0]; + } + } + +}; + +#endif diff --git a/src/jacktrip.pro b/src/jacktrip.pro index f898c63..0157e90 100644 --- a/src/jacktrip.pro +++ b/src/jacktrip.pro @@ -110,7 +110,13 @@ win32 { DESTDIR = . QMAKE_CLEAN += -r ./jacktrip ./jacktrip_debug ./release ./debug -target.path = /usr/bin + +# isEmpty(PREFIX) will allow path to be changed during the command line +# call to qmake, e.g. qmake PREFIX=/usr +isEmpty(PREFIX) { + PREFIX = /usr/local +} +target.path = $$PREFIX/bin/ INSTALLS += target # for plugins @@ -120,11 +126,18 @@ INCLUDEPATH += ../faust-src-lair HEADERS += DataProtocol.h \ JMess.h \ JackTrip.h \ + Effects.h \ + Compressor.h \ + CompressorPresets.h \ + Limiter.h \ + Reverb.h \ + AudioTester.h \ jacktrip_globals.h \ jacktrip_types.h \ JackTripThread.h \ JackTripWorker.h \ JackTripWorkerMessages.h \ + JitterBuffer.h \ LoopBack.h \ NetKS.h \ PacketHeader.h \ @@ -136,7 +149,10 @@ HEADERS += DataProtocol.h \ ThreadPoolTest.h \ UdpDataProtocol.h \ UdpHubListener.h \ - AudioInterface.h + AudioInterface.h \ + compressordsp.h \ + limiterdsp.h \ + freeverbdsp.h !nojack { HEADERS += JackAudioInterface.h @@ -144,11 +160,16 @@ HEADERS += JackAudioInterface.h SOURCES += DataProtocol.cpp \ JMess.cpp \ JackTrip.cpp \ + Compressor.cpp \ + Limiter.cpp \ + Reverb.cpp \ + AudioTester.cpp \ jacktrip_globals.cpp \ jacktrip_main.cpp \ jacktrip_tests.cpp \ JackTripThread.cpp \ JackTripWorker.cpp \ + JitterBuffer.cpp \ LoopBack.cpp \ PacketHeader.cpp \ ProcessPlugin.cpp \ @@ -162,7 +183,7 @@ SOURCES += DataProtocol.cpp \ SOURCES += JackAudioInterface.cpp } -# RtAduio Input +# RtAudio Input win32 { INCLUDEPATH += ../externals/rtaudio-4.1.1/include DEPENDPATH += ../externals/rtaudio-4.1.1/include diff --git a/src/jacktrip_globals.cpp b/src/jacktrip_globals.cpp index 5bd9a54..7e75994 100644 --- a/src/jacktrip_globals.cpp +++ b/src/jacktrip_globals.cpp @@ -151,6 +151,7 @@ void setRealtimeProcessPriority() #ifdef __UBUNTU__ priority = 95; // anything higher is silently ignored by Ubuntu 18.04 #endif + priority = 3; struct sched_param sp = { .sched_priority = priority }; diff --git a/src/jacktrip_globals.h b/src/jacktrip_globals.h index 4c98f17..ab66d2b 100644 --- a/src/jacktrip_globals.h +++ b/src/jacktrip_globals.h @@ -44,7 +44,7 @@ /// \todo Add this namespace //namespace JackTrip -const char* const gVersion = "1.2.2"; ///< JackTrip version +const char* const gVersion = "1.3.0"; ///< JackTrip version //******************************************************************************* /// \name Default Values @@ -126,6 +126,7 @@ extern int gVerboseFlag; ///< Verbose mode flag declaration //@{ const int gJackBitResolution = 32; ///< Audio Bit Resolution of the Jack Server const QString gJackDefaultClientName = "JackTrip"; +const int gMaxRemoteNameLength = 64; //@} diff --git a/src/jacktrip_main.cpp b/src/jacktrip_main.cpp index 5e172a8..bd4ed14 100644 --- a/src/jacktrip_main.cpp +++ b/src/jacktrip_main.cpp @@ -38,75 +38,115 @@ #include <iostream> #include <QCoreApplication> -#include <QDebug> +#include <QScopedPointer> +#include <iostream> +#include <signal.h> +#include "jacktrip_globals.h" +#include "Settings.h" +#include "UdpHubListener.h" #include <QLoggingCategory> -#include "JackAudioInterface.h" -#include "UdpDataProtocol.h" -#include "RingBuffer.h" -#include "JackTrip.h" -#include "Settings.h" -//#include "TestRingBuffer.h" -#include "LoopBack.h" -#include "PacketHeader.h" -//#include "JackTripThread.h" -#ifdef __RT_AUDIO__ -#include "RtAudioInterface.h" -#endif -#include "jacktrip_tests.cpp" -#include "jacktrip_globals.h" +void qtMessageHandler(__attribute__((unused)) QtMsgType type, __attribute__((unused)) const QMessageLogContext &context, const QString &msg) +{ + std::cerr << msg.toStdString() << std::endl; +} + +#if defined (__LINUX__) || (__MAC_OSX__) +static int setupUnixSignalHandler(void (*handler)(int)) +{ + //Setup our SIGINT handler. + struct sigaction sigInt; + sigInt.sa_handler = handler; + sigemptyset(&sigInt.sa_mask); + sigInt.sa_flags = 0; + sigInt.sa_flags |= SA_RESTART; + int result = 0; + if (sigaction(SIGINT, &sigInt, 0)) { + std::cout << "Unable to register SIGINT handler" << std::endl; + result |= 1; + } + if (sigaction(SIGTERM, &sigInt, 0)) { + std::cout << "Unable to register SIGTERM handler" << std::endl; + result |= 2; + } + return result; +} +#else +bool isHubServer = false; -void qtMessageHandler(QtMsgType /*type*/, const QMessageLogContext& /*context*/, const QString& msg) +BOOL WINAPI windowsCtrlHandler(DWORD fdwCtrlType) { - std::cerr << msg.toStdString() << std::endl; + switch (fdwCtrlType) { + case CTRL_C_EVENT: + if (isHubServer) { + UdpHubListener::sigIntHandler(0); + } else { + JackTrip::sigIntHandler(0); + } + return true; + default: + return false; + } } +#endif -int main(int argc, char** argv) +int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); + QScopedPointer<JackTrip> jackTrip; + QScopedPointer<UdpHubListener> udpHub; + QLoggingCategory::setFilterRules(QStringLiteral("*.debug=true")); qInstallMessageHandler(qtMessageHandler); - bool testing = false; - if ( argc > 1 ) { - if ( !strcmp(argv[1], "test") ) { - testing = true; - } - } - - if ( testing ) { - std::cout << "=========TESTING=========" << std::endl; - //main_tests(argc, argv); // test functions - JackTrip jacktrip; - //RtAudioInterface rtaudio(&jacktrip); - //rtaudio.setup(); - //rtaudio.listAllInterfaces(); - //rtaudio.printDeviceInfo(0); - - //while (true) sleep(9999); - } - else { - // catch all potential exeptions - try - { - // Get Settings from user - // ---------------------- - Settings* settings = new Settings; - settings->parseInput(argc, argv); - settings->startJackTrip(); - } - catch ( const std::exception & e ) - { - std::cerr << "ERROR:" << std::endl; - std::cerr << e.what() << std::endl; - std::cerr << "Exiting JackTrip..." << std::endl; - std::cerr << gPrintSeparator << std::endl; - return -1; + try { + Settings settings; + settings.parseInput(argc, argv); + + //Either start our hub server or our jacktrip process as appropriate. + if (settings.isHubServer()) { + udpHub.reset(settings.getConfiguredHubServer()); + if (gVerboseFlag) std::cout << "Settings:startJackTrip before udphub->start" << std::endl; + QObject::connect(udpHub.data(), &UdpHubListener::signalStopped, &app, + &QCoreApplication::quit, Qt::QueuedConnection); + QObject::connect(udpHub.data(), &UdpHubListener::signalError, &app, + &QCoreApplication::quit, Qt::QueuedConnection); +#if defined (__LINUX__) || (__MAC_OSX__) + setupUnixSignalHandler(UdpHubListener::sigIntHandler); +#else + isHubServer = true; + SetConsoleCtrlHandler(windowsCtrlHandler, true); +#endif + udpHub->start(); + } else { + jackTrip.reset(settings.getConfiguredJackTrip()); + if (gVerboseFlag) std::cout << "Settings:startJackTrip before mJackTrip->startProcess" << std::endl; + QObject::connect(jackTrip.data(), &JackTrip::signalProcessesStopped, &app, + &QCoreApplication::quit, Qt::QueuedConnection); + QObject::connect(jackTrip.data(), &JackTrip::signalError, &app, + &QCoreApplication::quit, Qt::QueuedConnection); +#if defined (__LINUX__) || (__MAC_OSX__) + setupUnixSignalHandler(JackTrip::sigIntHandler); +#else + std::cout << SetConsoleCtrlHandler(windowsCtrlHandler, true) << std::endl; +#endif +#ifdef WAIRTOHUB // WAIR + jackTrip->startProcess(0); // for WAIR compatibility, ID in jack client name +#else + jackTrip->startProcess(); +#endif // endwhere } + + if (gVerboseFlag) std::cout << "step 6" << std::endl; + if (gVerboseFlag) std::cout << "jmain before app->exec()" << std::endl; + } catch (const std::exception &e) { + std::cerr << "ERROR:" << std::endl; + std::cerr << e.what() << std::endl; + std::cerr << "Exiting JackTrip..." << std::endl; + std::cerr << gPrintSeparator << std::endl; + return -1; } - if (gVerboseFlag) std::cout << "step 6" << std::endl; - if (gVerboseFlag) std::cout << "jacktrip_main before app.exec()" << std::endl; - + return app.exec(); } diff --git a/src/jacktrip_types_alt.h b/src/jacktrip_types_alt.h new file mode 100644 index 0000000..2ce854b --- /dev/null +++ b/src/jacktrip_types_alt.h @@ -0,0 +1,7 @@ +typedef float sample_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; diff --git a/src/limiterdsp.h b/src/limiterdsp.h new file mode 100644 index 0000000..21ab621 --- /dev/null +++ b/src/limiterdsp.h @@ -0,0 +1,1751 @@ +/* ------------------------------------------------------------ +name: "limiterdsp" +Code generated with Faust 2.28.6 (https://faust.grame.fr) +Compilation options: -lang cpp -inpl -scal -ftz 0 +------------------------------------------------------------ */ + +#ifndef __limiterdsp_H__ +#define __limiterdsp_H__ + +// NOTE: ANY INCLUDE-GUARD HERE MUST BE DERIVED FROM THE CLASS NAME +// +// faust2header.cpp - FAUST Architecture File +// This is a simple variation of matlabplot.cpp in the Faust distribution +// aimed at creating a simple C++ header file (.h) containing a Faust DSP. +// See the Makefile for how to use it. + +/************************** BEGIN dsp.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __dsp__ +#define __dsp__ + +#include <string> +#include <vector> + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +struct UI; +struct Meta; + +/** + * DSP memory manager. + */ + +struct dsp_memory_manager { + + virtual ~dsp_memory_manager() {} + + virtual void* allocate(size_t size) = 0; + virtual void destroy(void* ptr) = 0; + +}; + +/** +* Signal processor definition. +*/ + +class dsp { + + public: + + dsp() {} + virtual ~dsp() {} + + /* Return instance number of audio inputs */ + virtual int getNumInputs() = 0; + + /* Return instance number of audio outputs */ + virtual int getNumOutputs() = 0; + + /** + * Trigger the ui_interface parameter with instance specific calls + * to 'openTabBox', 'addButton', 'addVerticalSlider'... in order to build the UI. + * + * @param ui_interface - the user interface builder + */ + virtual void buildUserInterface(UI* ui_interface) = 0; + + /* Returns the sample rate currently used by the instance */ + virtual int getSampleRate() = 0; + + /** + * Global init, calls the following methods: + * - static class 'classInit': static tables initialization + * - 'instanceInit': constants and instance state initialization + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void init(int sample_rate) = 0; + + /** + * Init instance state + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void instanceInit(int sample_rate) = 0; + + /** + * Init instance constant state + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void instanceConstants(int sample_rate) = 0; + + /* Init default control parameters values */ + virtual void instanceResetUserInterface() = 0; + + /* Init instance state (delay lines...) */ + virtual void instanceClear() = 0; + + /** + * Return a clone of the instance. + * + * @return a copy of the instance on success, otherwise a null pointer. + */ + virtual dsp* clone() = 0; + + /** + * Trigger the Meta* parameter with instance specific calls to 'declare' (key, value) metadata. + * + * @param m - the Meta* meta user + */ + virtual void metadata(Meta* m) = 0; + + /** + * DSP instance computation, to be called with successive in/out audio buffers. + * + * @param count - the number of frames to compute + * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) + * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) + * + */ + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0; + + /** + * DSP instance computation: alternative method to be used by subclasses. + * + * @param date_usec - the timestamp in microsec given by audio driver. + * @param count - the number of frames to compute + * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) + * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) + * + */ + virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); } + +}; + +/** + * Generic DSP decorator. + */ + +class decorator_dsp : public dsp { + + protected: + + dsp* fDSP; + + public: + + decorator_dsp(dsp* dsp = nullptr):fDSP(dsp) {} + virtual ~decorator_dsp() { delete fDSP; } + + virtual int getNumInputs() { return fDSP->getNumInputs(); } + virtual int getNumOutputs() { return fDSP->getNumOutputs(); } + virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); } + virtual int getSampleRate() { return fDSP->getSampleRate(); } + virtual void init(int sample_rate) { fDSP->init(sample_rate); } + virtual void instanceInit(int sample_rate) { fDSP->instanceInit(sample_rate); } + virtual void instanceConstants(int sample_rate) { fDSP->instanceConstants(sample_rate); } + virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); } + virtual void instanceClear() { fDSP->instanceClear(); } + virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); } + virtual void metadata(Meta* m) { fDSP->metadata(m); } + // Beware: subclasses usually have to overload the two 'compute' methods + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); } + virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); } + +}; + +/** + * DSP factory class. + */ + +class dsp_factory { + + protected: + + // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory); + virtual ~dsp_factory() {} + + public: + + virtual std::string getName() = 0; + virtual std::string getSHAKey() = 0; + virtual std::string getDSPCode() = 0; + virtual std::string getCompileOptions() = 0; + virtual std::vector<std::string> getLibraryList() = 0; + virtual std::vector<std::string> getIncludePathnames() = 0; + + virtual dsp* createDSPInstance() = 0; + + virtual void setMemoryManager(dsp_memory_manager* manager) = 0; + virtual dsp_memory_manager* getMemoryManager() = 0; + +}; + +/** + * On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero) + * flags to avoid costly denormals. + */ + +#ifdef __SSE__ + #include <xmmintrin.h> + #ifdef __SSE2__ + #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040) + #else + #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000) + #endif +#else + #define AVOIDDENORMALS +#endif + +#endif +/************************** END dsp.h **************************/ + +/************************** BEGIN APIUI.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef API_UI_H +#define API_UI_H + +#include <sstream> +#include <string> +#include <vector> +#include <iostream> +#include <map> + +/************************** BEGIN meta.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __meta__ +#define __meta__ + +struct Meta +{ + virtual ~Meta() {}; + virtual void declare(const char* key, const char* value) = 0; + +}; + +#endif +/************************** END meta.h **************************/ +/************************** BEGIN UI.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2020 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __UI_H__ +#define __UI_H__ + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +/******************************************************************************* + * UI : Faust DSP User Interface + * User Interface as expected by the buildUserInterface() method of a DSP. + * This abstract class contains only the method that the Faust compiler can + * generate to describe a DSP user interface. + ******************************************************************************/ + +struct Soundfile; + +template <typename REAL> +struct UIReal +{ + UIReal() {} + virtual ~UIReal() {} + + // -- widget's layouts + + virtual void openTabBox(const char* label) = 0; + virtual void openHorizontalBox(const char* label) = 0; + virtual void openVerticalBox(const char* label) = 0; + virtual void closeBox() = 0; + + // -- active widgets + + virtual void addButton(const char* label, REAL* zone) = 0; + virtual void addCheckButton(const char* label, REAL* zone) = 0; + virtual void addVerticalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + virtual void addHorizontalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + virtual void addNumEntry(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + + // -- passive widgets + + virtual void addHorizontalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0; + virtual void addVerticalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0; + + // -- soundfiles + + virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) = 0; + + // -- metadata declarations + + virtual void declare(REAL* zone, const char* key, const char* val) {} +}; + +struct UI : public UIReal<FAUSTFLOAT> +{ + UI() {} + virtual ~UI() {} +}; + +#endif +/************************** END UI.h **************************/ +/************************** BEGIN PathBuilder.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef FAUST_PATHBUILDER_H +#define FAUST_PATHBUILDER_H + +#include <vector> +#include <string> +#include <algorithm> + +/******************************************************************************* + * PathBuilder : Faust User Interface + * Helper class to build complete hierarchical path for UI items. + ******************************************************************************/ + +class PathBuilder +{ + + protected: + + std::vector<std::string> fControlsLevel; + + public: + + PathBuilder() {} + virtual ~PathBuilder() {} + + std::string buildPath(const std::string& label) + { + std::string res = "/"; + for (size_t i = 0; i < fControlsLevel.size(); i++) { + res += fControlsLevel[i]; + res += "/"; + } + res += label; + std::replace(res.begin(), res.end(), ' ', '_'); + return res; + } + + std::string buildLabel(std::string label) + { + std::replace(label.begin(), label.end(), ' ', '_'); + return label; + } + + void pushLabel(const std::string& label) { fControlsLevel.push_back(label); } + void popLabel() { fControlsLevel.pop_back(); } + +}; + +#endif // FAUST_PATHBUILDER_H +/************************** END PathBuilder.h **************************/ +/************************** BEGIN ValueConverter.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __ValueConverter__ +#define __ValueConverter__ + +/*************************************************************************************** + ValueConverter.h + (GRAME, Copyright 2015-2019) + +Set of conversion objects used to map user interface values (for example a gui slider +delivering values between 0 and 1) to faust values (for example a vslider between +20 and 20000) using a log scale. + +-- Utilities + +Range(lo,hi) : clip a value x between lo and hi +Interpolator(lo,hi,v1,v2) : Maps a value x between lo and hi to a value y between v1 and v2 +Interpolator3pt(lo,mi,hi,v1,vm,v2) : Map values between lo mid hi to values between v1 vm v2 + +-- Value Converters + +ValueConverter::ui2faust(x) +ValueConverter::faust2ui(x) + +-- ValueConverters used for sliders depending of the scale + +LinearValueConverter(umin, umax, fmin, fmax) +LinearValueConverter2(lo, mi, hi, v1, vm, v2) using 2 segments +LogValueConverter(umin, umax, fmin, fmax) +ExpValueConverter(umin, umax, fmin, fmax) + +-- ValueConverters used for accelerometers based on 3 points + +AccUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 0 +AccDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 1 +AccUpDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 2 +AccDownUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 3 + +-- lists of ZoneControl are used to implement accelerometers metadata for each axes + +ZoneControl(zone, valueConverter) : a zone with an accelerometer data converter + +-- ZoneReader are used to implement screencolor metadata + +ZoneReader(zone, valueConverter) : a zone with a data converter + +****************************************************************************************/ + +#include <float.h> +#include <algorithm> // std::max +#include <cmath> +#include <vector> +#include <assert.h> + +//-------------------------------------------------------------------------------------- +// Interpolator(lo,hi,v1,v2) +// Maps a value x between lo and hi to a value y between v1 and v2 +// y = v1 + (x-lo)/(hi-lo)*(v2-v1) +// y = v1 + (x-lo) * coef with coef = (v2-v1)/(hi-lo) +// y = v1 + x*coef - lo*coef +// y = v1 - lo*coef + x*coef +// y = offset + x*coef with offset = v1 - lo*coef +//-------------------------------------------------------------------------------------- +class Interpolator +{ + private: + + //-------------------------------------------------------------------------------------- + // Range(lo,hi) clip a value between lo and hi + //-------------------------------------------------------------------------------------- + struct Range + { + double fLo; + double fHi; + + Range(double x, double y) : fLo(std::min<double>(x,y)), fHi(std::max<double>(x,y)) {} + double operator()(double x) { return (x<fLo) ? fLo : (x>fHi) ? fHi : x; } + }; + + + Range fRange; + double fCoef; + double fOffset; + + public: + + Interpolator(double lo, double hi, double v1, double v2) : fRange(lo,hi) + { + if (hi != lo) { + // regular case + fCoef = (v2-v1)/(hi-lo); + fOffset = v1 - lo*fCoef; + } else { + // degenerate case, avoids division by zero + fCoef = 0; + fOffset = (v1+v2)/2; + } + } + double operator()(double v) + { + double x = fRange(v); + return fOffset + x*fCoef; + } + + void getLowHigh(double& amin, double& amax) + { + amin = fRange.fLo; + amax = fRange.fHi; + } +}; + +//-------------------------------------------------------------------------------------- +// Interpolator3pt(lo,mi,hi,v1,vm,v2) +// Map values between lo mid hi to values between v1 vm v2 +//-------------------------------------------------------------------------------------- +class Interpolator3pt +{ + + private: + + Interpolator fSegment1; + Interpolator fSegment2; + double fMid; + + public: + + Interpolator3pt(double lo, double mi, double hi, double v1, double vm, double v2) : + fSegment1(lo, mi, v1, vm), + fSegment2(mi, hi, vm, v2), + fMid(mi) {} + double operator()(double x) { return (x < fMid) ? fSegment1(x) : fSegment2(x); } + + void getMappingValues(double& amin, double& amid, double& amax) + { + fSegment1.getLowHigh(amin, amid); + fSegment2.getLowHigh(amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Abstract ValueConverter class. Converts values between UI and Faust representations +//-------------------------------------------------------------------------------------- +class ValueConverter +{ + + public: + + virtual ~ValueConverter() {} + virtual double ui2faust(double x) = 0; + virtual double faust2ui(double x) = 0; +}; + +//-------------------------------------------------------------------------------------- +// A converter than can be updated +//-------------------------------------------------------------------------------------- + +class UpdatableValueConverter : public ValueConverter { + + protected: + + bool fActive; + + public: + + UpdatableValueConverter():fActive(true) + {} + virtual ~UpdatableValueConverter() + {} + + virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) = 0; + virtual void getMappingValues(double& amin, double& amid, double& amax) = 0; + + void setActive(bool on_off) { fActive = on_off; } + bool getActive() { return fActive; } + +}; + + +//-------------------------------------------------------------------------------------- +// Linear conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LinearValueConverter : public ValueConverter +{ + + private: + + Interpolator fUI2F; + Interpolator fF2UI; + + public: + + LinearValueConverter(double umin, double umax, double fmin, double fmax) : + fUI2F(umin,umax,fmin,fmax), fF2UI(fmin,fmax,umin,umax) + {} + + LinearValueConverter() : fUI2F(0.,0.,0.,0.), fF2UI(0.,0.,0.,0.) + {} + virtual double ui2faust(double x) { return fUI2F(x); } + virtual double faust2ui(double x) { return fF2UI(x); } + +}; + +//-------------------------------------------------------------------------------------- +// Two segments linear conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LinearValueConverter2 : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fUI2F; + Interpolator3pt fF2UI; + + public: + + LinearValueConverter2(double amin, double amid, double amax, double min, double init, double max) : + fUI2F(amin, amid, amax, min, init, max), fF2UI(min, init, max, amin, amid, amax) + {} + + LinearValueConverter2() : fUI2F(0.,0.,0.,0.,0.,0.), fF2UI(0.,0.,0.,0.,0.,0.) + {} + + virtual double ui2faust(double x) { return fUI2F(x); } + virtual double faust2ui(double x) { return fF2UI(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) + { + fUI2F = Interpolator3pt(amin, amid, amax, min, init, max); + fF2UI = Interpolator3pt(min, init, max, amin, amid, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fUI2F.getMappingValues(amin, amid, amax); + } + +}; + +//-------------------------------------------------------------------------------------- +// Logarithmic conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LogValueConverter : public LinearValueConverter +{ + + public: + + LogValueConverter(double umin, double umax, double fmin, double fmax) : + LinearValueConverter(umin, umax, std::log(std::max<double>(DBL_MIN, fmin)), std::log(std::max<double>(DBL_MIN, fmax))) + {} + + virtual double ui2faust(double x) { return std::exp(LinearValueConverter::ui2faust(x)); } + virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::log(std::max<double>(x, DBL_MIN))); } + +}; + +//-------------------------------------------------------------------------------------- +// Exponential conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class ExpValueConverter : public LinearValueConverter +{ + + public: + + ExpValueConverter(double umin, double umax, double fmin, double fmax) : + LinearValueConverter(umin, umax, std::min<double>(DBL_MAX, std::exp(fmin)), std::min<double>(DBL_MAX, std::exp(fmax))) + {} + + virtual double ui2faust(double x) { return std::log(LinearValueConverter::ui2faust(x)); } + virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::min<double>(DBL_MAX, std::exp(x))); } + +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using an Up curve (curve 0) +//-------------------------------------------------------------------------------------- +class AccUpConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator3pt fF2A; + + public: + + AccUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmin,fmid,fmax), + fF2A(fmin,fmid,fmax,amin,amid,amax) + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmin, fmid, fmax); + fF2A = Interpolator3pt(fmin, fmid, fmax, amin, amid, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } + +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using a Down curve (curve 1) +//-------------------------------------------------------------------------------------- +class AccDownConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator3pt fF2A; + + public: + + AccDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmax,fmid,fmin), + fF2A(fmin,fmid,fmax,amax,amid,amin) + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmax, fmid, fmin); + fF2A = Interpolator3pt(fmin, fmid, fmax, amax, amid, amin); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using an Up-Down curve (curve 2) +//-------------------------------------------------------------------------------------- +class AccUpDownConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator fF2A; + + public: + + AccUpDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmin,fmax,fmin), + fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotonic function + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmin, fmax, fmin); + fF2A = Interpolator(fmin, fmax, amin, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using a Down-Up curve (curve 3) +//-------------------------------------------------------------------------------------- +class AccDownUpConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator fF2A; + + public: + + AccDownUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmax,fmin,fmax), + fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotonic function + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmax, fmin, fmax); + fF2A = Interpolator(fmin, fmax, amin, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Base class for ZoneControl +//-------------------------------------------------------------------------------------- +class ZoneControl +{ + + protected: + + FAUSTFLOAT* fZone; + + public: + + ZoneControl(FAUSTFLOAT* zone) : fZone(zone) {} + virtual ~ZoneControl() {} + + virtual void update(double v) const {} + + virtual void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) {} + virtual void getMappingValues(double& amin, double& amid, double& amax) {} + + FAUSTFLOAT* getZone() { return fZone; } + + virtual void setActive(bool on_off) {} + virtual bool getActive() { return false; } + + virtual int getCurve() { return -1; } + +}; + +//-------------------------------------------------------------------------------------- +// Useful to implement accelerometers metadata as a list of ZoneControl for each axes +//-------------------------------------------------------------------------------------- +class ConverterZoneControl : public ZoneControl +{ + + protected: + + ValueConverter* fValueConverter; + + public: + + ConverterZoneControl(FAUSTFLOAT* zone, ValueConverter* converter) : ZoneControl(zone), fValueConverter(converter) {} + virtual ~ConverterZoneControl() { delete fValueConverter; } // Assuming fValueConverter is not kept elsewhere... + + virtual void update(double v) const { *fZone = fValueConverter->ui2faust(v); } + + ValueConverter* getConverter() { return fValueConverter; } + +}; + +//-------------------------------------------------------------------------------------- +// Association of a zone and a four value converter, each one for each possible curve. +// Useful to implement accelerometers metadata as a list of ZoneControl for each axes +//-------------------------------------------------------------------------------------- +class CurveZoneControl : public ZoneControl +{ + + private: + + std::vector<UpdatableValueConverter*> fValueConverters; + int fCurve; + + public: + + CurveZoneControl(FAUSTFLOAT* zone, int curve, double amin, double amid, double amax, double min, double init, double max) : ZoneControl(zone), fCurve(0) + { + assert(curve >= 0 && curve <= 3); + fValueConverters.push_back(new AccUpConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccDownConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccUpDownConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccDownUpConverter(amin, amid, amax, min, init, max)); + fCurve = curve; + } + virtual ~CurveZoneControl() + { + std::vector<UpdatableValueConverter*>::iterator it; + for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) { + delete(*it); + } + } + void update(double v) const { if (fValueConverters[fCurve]->getActive()) *fZone = fValueConverters[fCurve]->ui2faust(v); } + + void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) + { + fValueConverters[curve]->setMappingValues(amin, amid, amax, min, init, max); + fCurve = curve; + } + + void getMappingValues(double& amin, double& amid, double& amax) + { + fValueConverters[fCurve]->getMappingValues(amin, amid, amax); + } + + void setActive(bool on_off) + { + std::vector<UpdatableValueConverter*>::iterator it; + for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) { + (*it)->setActive(on_off); + } + } + + int getCurve() { return fCurve; } +}; + +class ZoneReader +{ + + private: + + FAUSTFLOAT* fZone; + Interpolator fInterpolator; + + public: + + ZoneReader(FAUSTFLOAT* zone, double lo, double hi) : fZone(zone), fInterpolator(lo, hi, 0, 255) {} + + virtual ~ZoneReader() {} + + int getValue() + { + return (fZone != nullptr) ? int(fInterpolator(*fZone)) : 127; + } + +}; + +#endif +/************************** END ValueConverter.h **************************/ + +class APIUI : public PathBuilder, public Meta, public UI +{ + public: + + enum ItemType { kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph }; + + protected: + + enum { kLin = 0, kLog = 1, kExp = 2 }; + + int fNumParameters; + std::vector<std::string> fPaths; + std::vector<std::string> fLabels; + std::map<std::string, int> fPathMap; + std::map<std::string, int> fLabelMap; + std::vector<ValueConverter*> fConversion; + std::vector<FAUSTFLOAT*> fZone; + std::vector<FAUSTFLOAT> fInit; + std::vector<FAUSTFLOAT> fMin; + std::vector<FAUSTFLOAT> fMax; + std::vector<FAUSTFLOAT> fStep; + std::vector<ItemType> fItemType; + std::vector<std::map<std::string, std::string> > fMetaData; + std::vector<ZoneControl*> fAcc[3]; + std::vector<ZoneControl*> fGyr[3]; + + // Screen color control + // "...[screencolor:red]..." etc. + bool fHasScreenControl; // true if control screen color metadata + ZoneReader* fRedReader; + ZoneReader* fGreenReader; + ZoneReader* fBlueReader; + + // Current values controlled by metadata + std::string fCurrentUnit; + int fCurrentScale; + std::string fCurrentAcc; + std::string fCurrentGyr; + std::string fCurrentColor; + std::string fCurrentTooltip; + std::map<std::string, std::string> fCurrentMetadata; + + // Add a generic parameter + virtual void addParameter(const char* label, + FAUSTFLOAT* zone, + FAUSTFLOAT init, + FAUSTFLOAT min, + FAUSTFLOAT max, + FAUSTFLOAT step, + ItemType type) + { + std::string path = buildPath(label); + fPathMap[path] = fLabelMap[label] = fNumParameters++; + fPaths.push_back(path); + fLabels.push_back(label); + fZone.push_back(zone); + fInit.push_back(init); + fMin.push_back(min); + fMax.push_back(max); + fStep.push_back(step); + fItemType.push_back(type); + + // handle scale metadata + switch (fCurrentScale) { + case kLin: + fConversion.push_back(new LinearValueConverter(0, 1, min, max)); + break; + case kLog: + fConversion.push_back(new LogValueConverter(0, 1, min, max)); + break; + case kExp: fConversion.push_back(new ExpValueConverter(0, 1, min, max)); + break; + } + fCurrentScale = kLin; + + if (fCurrentAcc.size() > 0 && fCurrentGyr.size() > 0) { + std::cerr << "warning : 'acc' and 'gyr' metadata used for the same " << label << " parameter !!\n"; + } + + // handle acc metadata "...[acc : <axe> <curve> <amin> <amid> <amax>]..." + if (fCurrentAcc.size() > 0) { + std::istringstream iss(fCurrentAcc); + int axe, curve; + double amin, amid, amax; + iss >> axe >> curve >> amin >> amid >> amax; + + if ((0 <= axe) && (axe < 3) && + (0 <= curve) && (curve < 4) && + (amin < amax) && (amin <= amid) && (amid <= amax)) + { + fAcc[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); + } else { + std::cerr << "incorrect acc metadata : " << fCurrentAcc << std::endl; + } + fCurrentAcc = ""; + } + + // handle gyr metadata "...[gyr : <axe> <curve> <amin> <amid> <amax>]..." + if (fCurrentGyr.size() > 0) { + std::istringstream iss(fCurrentGyr); + int axe, curve; + double amin, amid, amax; + iss >> axe >> curve >> amin >> amid >> amax; + + if ((0 <= axe) && (axe < 3) && + (0 <= curve) && (curve < 4) && + (amin < amax) && (amin <= amid) && (amid <= amax)) + { + fGyr[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); + } else { + std::cerr << "incorrect gyr metadata : " << fCurrentGyr << std::endl; + } + fCurrentGyr = ""; + } + + // handle screencolor metadata "...[screencolor:red|green|blue|white]..." + if (fCurrentColor.size() > 0) { + if ((fCurrentColor == "red") && (fRedReader == 0)) { + fRedReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "green") && (fGreenReader == 0)) { + fGreenReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "blue") && (fBlueReader == 0)) { + fBlueReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "white") && (fRedReader == 0) && (fGreenReader == 0) && (fBlueReader == 0)) { + fRedReader = new ZoneReader(zone, min, max); + fGreenReader = new ZoneReader(zone, min, max); + fBlueReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else { + std::cerr << "incorrect screencolor metadata : " << fCurrentColor << std::endl; + } + } + fCurrentColor = ""; + + fMetaData.push_back(fCurrentMetadata); + fCurrentMetadata.clear(); + } + + int getZoneIndex(std::vector<ZoneControl*>* table, int p, int val) + { + FAUSTFLOAT* zone = fZone[p]; + for (size_t i = 0; i < table[val].size(); i++) { + if (zone == table[val][i]->getZone()) return int(i); + } + return -1; + } + + void setConverter(std::vector<ZoneControl*>* table, int p, int val, int curve, double amin, double amid, double amax) + { + int id1 = getZoneIndex(table, p, 0); + int id2 = getZoneIndex(table, p, 1); + int id3 = getZoneIndex(table, p, 2); + + // Deactivates everywhere.. + if (id1 != -1) table[0][id1]->setActive(false); + if (id2 != -1) table[1][id2]->setActive(false); + if (id3 != -1) table[2][id3]->setActive(false); + + if (val == -1) { // Means: no more mapping... + // So stay all deactivated... + } else { + int id4 = getZoneIndex(table, p, val); + if (id4 != -1) { + // Reactivate the one we edit... + table[val][id4]->setMappingValues(curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]); + table[val][id4]->setActive(true); + } else { + // Allocate a new CurveZoneControl which is 'active' by default + FAUSTFLOAT* zone = fZone[p]; + table[val].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, fMin[p], fInit[p], fMax[p])); + } + } + } + + void getConverter(std::vector<ZoneControl*>* table, int p, int& val, int& curve, double& amin, double& amid, double& amax) + { + int id1 = getZoneIndex(table, p, 0); + int id2 = getZoneIndex(table, p, 1); + int id3 = getZoneIndex(table, p, 2); + + if (id1 != -1) { + val = 0; + curve = table[val][id1]->getCurve(); + table[val][id1]->getMappingValues(amin, amid, amax); + } else if (id2 != -1) { + val = 1; + curve = table[val][id2]->getCurve(); + table[val][id2]->getMappingValues(amin, amid, amax); + } else if (id3 != -1) { + val = 2; + curve = table[val][id3]->getCurve(); + table[val][id3]->getMappingValues(amin, amid, amax); + } else { + val = -1; // No mapping + curve = 0; + amin = -100.; + amid = 0.; + amax = 100.; + } + } + + public: + + enum Type { kAcc = 0, kGyr = 1, kNoType }; + + APIUI() : fNumParameters(0), fHasScreenControl(false), fRedReader(0), fGreenReader(0), fBlueReader(0), fCurrentScale(kLin) + {} + + virtual ~APIUI() + { + for (auto& it : fConversion) delete it; + for (int i = 0; i < 3; i++) { + for (auto& it : fAcc[i]) delete it; + for (auto& it : fGyr[i]) delete it; + } + delete fRedReader; + delete fGreenReader; + delete fBlueReader; + } + + // -- widget's layouts + + virtual void openTabBox(const char* label) { pushLabel(label); } + virtual void openHorizontalBox(const char* label) { pushLabel(label); } + virtual void openVerticalBox(const char* label) { pushLabel(label); } + virtual void closeBox() { popLabel(); } + + // -- active widgets + + virtual void addButton(const char* label, FAUSTFLOAT* zone) + { + addParameter(label, zone, 0, 0, 1, 1, kButton); + } + + virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) + { + addParameter(label, zone, 0, 0, 1, 1, kCheckButton); + } + + virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kVSlider); + } + + virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kHSlider); + } + + virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kNumEntry); + } + + // -- passive widgets + + virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) + { + addParameter(label, zone, min, min, max, (max-min)/1000.0, kHBargraph); + } + + virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) + { + addParameter(label, zone, min, min, max, (max-min)/1000.0, kVBargraph); + } + + // -- soundfiles + + virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) {} + + // -- metadata declarations + + virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val) + { + // Keep metadata + fCurrentMetadata[key] = val; + + if (strcmp(key, "scale") == 0) { + if (strcmp(val, "log") == 0) { + fCurrentScale = kLog; + } else if (strcmp(val, "exp") == 0) { + fCurrentScale = kExp; + } else { + fCurrentScale = kLin; + } + } else if (strcmp(key, "unit") == 0) { + fCurrentUnit = val; + } else if (strcmp(key, "acc") == 0) { + fCurrentAcc = val; + } else if (strcmp(key, "gyr") == 0) { + fCurrentGyr = val; + } else if (strcmp(key, "screencolor") == 0) { + fCurrentColor = val; // val = "red", "green", "blue" or "white" + } else if (strcmp(key, "tooltip") == 0) { + fCurrentTooltip = val; + } + } + + virtual void declare(const char* key, const char* val) + {} + + //------------------------------------------------------------------------------- + // Simple API part + //------------------------------------------------------------------------------- + int getParamsCount() { return fNumParameters; } + int getParamIndex(const char* path) + { + if (fPathMap.find(path) != fPathMap.end()) { + return fPathMap[path]; + } else if (fLabelMap.find(path) != fLabelMap.end()) { + return fLabelMap[path]; + } else { + return -1; + } + } + const char* getParamAddress(int p) { return fPaths[p].c_str(); } + const char* getParamLabel(int p) { return fLabels[p].c_str(); } + std::map<const char*, const char*> getMetadata(int p) + { + std::map<const char*, const char*> res; + std::map<std::string, std::string> metadata = fMetaData[p]; + for (auto it : metadata) { + res[it.first.c_str()] = it.second.c_str(); + } + return res; + } + + const char* getMetadata(int p, const char* key) + { + return (fMetaData[p].find(key) != fMetaData[p].end()) ? fMetaData[p][key].c_str() : ""; + } + FAUSTFLOAT getParamMin(int p) { return fMin[p]; } + FAUSTFLOAT getParamMax(int p) { return fMax[p]; } + FAUSTFLOAT getParamStep(int p) { return fStep[p]; } + FAUSTFLOAT getParamInit(int p) { return fInit[p]; } + + FAUSTFLOAT* getParamZone(int p) { return fZone[p]; } + FAUSTFLOAT getParamValue(int p) { return *fZone[p]; } + void setParamValue(int p, FAUSTFLOAT v) { *fZone[p] = v; } + + double getParamRatio(int p) { return fConversion[p]->faust2ui(*fZone[p]); } + void setParamRatio(int p, double r) { *fZone[p] = fConversion[p]->ui2faust(r); } + + double value2ratio(int p, double r) { return fConversion[p]->faust2ui(r); } + double ratio2value(int p, double r) { return fConversion[p]->ui2faust(r); } + + /** + * Return the control type (kAcc, kGyr, or -1) for a given parameter + * + * @param p - the UI parameter index + * + * @return the type + */ + Type getParamType(int p) + { + if (p >= 0) { + if (getZoneIndex(fAcc, p, 0) != -1 + || getZoneIndex(fAcc, p, 1) != -1 + || getZoneIndex(fAcc, p, 2) != -1) { + return kAcc; + } else if (getZoneIndex(fGyr, p, 0) != -1 + || getZoneIndex(fGyr, p, 1) != -1 + || getZoneIndex(fGyr, p, 2) != -1) { + return kGyr; + } + } + return kNoType; + } + + /** + * Return the Item type (kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph) for a given parameter + * + * @param p - the UI parameter index + * + * @return the Item type + */ + ItemType getParamItemType(int p) + { + return fItemType[p]; + } + + /** + * Set a new value coming from an accelerometer, propagate it to all relevant FAUSTFLOAT* zones. + * + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer + * @param value - the new value + * + */ + void propagateAcc(int acc, double value) + { + for (size_t i = 0; i < fAcc[acc].size(); i++) { + fAcc[acc][i]->update(value); + } + } + + /** + * Used to edit accelerometer curves and mapping. Set curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer (-1 means "no mapping") + * @param curve - between 0 and 3 + * @param amin - mapping 'min' point + * @param amid - mapping 'middle' point + * @param amax - mapping 'max' point + * + */ + void setAccConverter(int p, int acc, int curve, double amin, double amid, double amax) + { + setConverter(fAcc, p, acc, curve, amin, amid, amax); + } + + /** + * Used to edit gyroscope curves and mapping. Set curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope (-1 means "no mapping") + * @param curve - between 0 and 3 + * @param amin - mapping 'min' point + * @param amid - mapping 'middle' point + * @param amax - mapping 'max' point + * + */ + void setGyrConverter(int p, int gyr, int curve, double amin, double amid, double amax) + { + setConverter(fGyr, p, gyr, curve, amin, amid, amax); + } + + /** + * Used to edit accelerometer curves and mapping. Get curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - the acc value to be retrieved (-1 means "no mapping") + * @param curve - the curve value to be retrieved + * @param amin - the amin value to be retrieved + * @param amid - the amid value to be retrieved + * @param amax - the amax value to be retrieved + * + */ + void getAccConverter(int p, int& acc, int& curve, double& amin, double& amid, double& amax) + { + getConverter(fAcc, p, acc, curve, amin, amid, amax); + } + + /** + * Used to edit gyroscope curves and mapping. Get curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param gyr - the gyr value to be retrieved (-1 means "no mapping") + * @param curve - the curve value to be retrieved + * @param amin - the amin value to be retrieved + * @param amid - the amid value to be retrieved + * @param amax - the amax value to be retrieved + * + */ + void getGyrConverter(int p, int& gyr, int& curve, double& amin, double& amid, double& amax) + { + getConverter(fGyr, p, gyr, curve, amin, amid, amax); + } + + /** + * Set a new value coming from an gyroscope, propagate it to all relevant FAUSTFLOAT* zones. + * + * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope + * @param value - the new value + * + */ + void propagateGyr(int gyr, double value) + { + for (size_t i = 0; i < fGyr[gyr].size(); i++) { + fGyr[gyr][i]->update(value); + } + } + + /** + * Get the number of FAUSTFLOAT* zones controlled with the accelerometer + * + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer + * @return the number of zones + * + */ + int getAccCount(int acc) + { + return (acc >= 0 && acc < 3) ? int(fAcc[acc].size()) : 0; + } + + /** + * Get the number of FAUSTFLOAT* zones controlled with the gyroscope + * + * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope + * @param the number of zones + * + */ + int getGyrCount(int gyr) + { + return (gyr >= 0 && gyr < 3) ? int(fGyr[gyr].size()) : 0; + } + + // getScreenColor() : -1 means no screen color control (no screencolor metadata found) + // otherwise return 0x00RRGGBB a ready to use color + int getScreenColor() + { + if (fHasScreenControl) { + int r = (fRedReader) ? fRedReader->getValue() : 0; + int g = (fGreenReader) ? fGreenReader->getValue() : 0; + int b = (fBlueReader) ? fBlueReader->getValue() : 0; + return (r<<16) | (g<<8) | b; + } else { + return -1; + } + } + +}; + +#endif +/************************** END APIUI.h **************************/ + +// NOTE: "faust -scn name" changes the last line above to +// #include <faust/name/name.h> + +//---------------------------------------------------------------------------- +// FAUST Generated Code +//---------------------------------------------------------------------------- + + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +#include <algorithm> +#include <cmath> +#include <math.h> + + +#ifndef FAUSTCLASS +#define FAUSTCLASS limiterdsp +#endif + +#ifdef __APPLE__ +#define exp10f __exp10f +#define exp10 __exp10 +#endif + +class limiterdsp : public dsp { + + private: + + int fSampleRate; + float fConst0; + float fConst1; + float fConst2; + float fConst3; + int iRec5[2]; + FAUSTFLOAT fHslider0; + int IOTA; + float fVec0[32]; + float fRec4[2]; + int iRec2[2]; + float fRec1[2]; + float fConst4; + float fConst5; + float fRec0[2]; + int iConst6; + + public: + + void metadata(Meta* m) { + m->declare("analyzers.lib/name", "Faust Analyzer Library"); + m->declare("analyzers.lib/version", "0.1"); + m->declare("basics.lib/name", "Faust Basic Element Library"); + m->declare("basics.lib/version", "0.1"); + m->declare("compressors.lib/limiter_lad_N:author", "Dario Sanfilippo"); + m->declare("compressors.lib/limiter_lad_N:copyright", "Copyright (C) 2020 Dario Sanfilippo <sanfilippo.dario@gmail.com>"); + m->declare("compressors.lib/limiter_lad_N:license", "GPLv3 license"); + m->declare("compressors.lib/limiter_lad_mono:author", "Dario Sanfilippo"); + m->declare("compressors.lib/limiter_lad_mono:copyright", "Copyright (C) 2020 Dario Sanfilippo <sanfilippo.dario@gmail.com>"); + m->declare("compressors.lib/limiter_lad_mono:license", "GPLv3 license"); + m->declare("compressors.lib/name", "Faust Compressor Effect Library"); + m->declare("compressors.lib/version", "0.0"); + m->declare("filename", "limiterdsp.dsp"); + m->declare("maths.lib/author", "GRAME"); + m->declare("maths.lib/copyright", "GRAME"); + m->declare("maths.lib/license", "LGPL with exception"); + m->declare("maths.lib/name", "Faust Math Library"); + m->declare("maths.lib/version", "2.3"); + m->declare("name", "limiterdsp"); + m->declare("platform.lib/name", "Generic Platform Library"); + m->declare("platform.lib/version", "0.1"); + m->declare("routes.lib/name", "Faust Signal Routing Library"); + m->declare("routes.lib/version", "0.2"); + m->declare("signals.lib/name", "Faust Signal Routing Library"); + m->declare("signals.lib/version", "0.0"); + } + + virtual int getNumInputs() { + return 1; + } + virtual int getNumOutputs() { + return 1; + } + virtual int getInputRate(int channel) { + int rate; + switch ((channel)) { + case 0: { + rate = 1; + break; + } + default: { + rate = -1; + break; + } + } + return rate; + } + virtual int getOutputRate(int channel) { + int rate; + switch ((channel)) { + case 0: { + rate = 1; + break; + } + default: { + rate = -1; + break; + } + } + return rate; + } + + static void classInit(int sample_rate) { + } + + virtual void instanceConstants(int sample_rate) { + fSampleRate = sample_rate; + fConst0 = std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate))); + fConst1 = std::exp((0.0f - (100000.0f / fConst0))); + fConst2 = (1.0f - fConst1); + fConst3 = (0.100000001f * fConst0); + fConst4 = std::exp((0.0f - (4.0f / fConst0))); + fConst5 = (1.0f - fConst4); + iConst6 = int((9.99999975e-05f * fConst0)); + } + + virtual void instanceResetUserInterface() { + fHslider0 = FAUSTFLOAT(2.0f); + } + + virtual void instanceClear() { + for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { + iRec5[l0] = 0; + } + IOTA = 0; + for (int l1 = 0; (l1 < 32); l1 = (l1 + 1)) { + fVec0[l1] = 0.0f; + } + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec4[l2] = 0.0f; + } + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + iRec2[l3] = 0; + } + for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { + fRec1[l4] = 0.0f; + } + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fRec0[l5] = 0.0f; + } + } + + virtual void init(int sample_rate) { + classInit(sample_rate); + instanceInit(sample_rate); + } + virtual void instanceInit(int sample_rate) { + instanceConstants(sample_rate); + instanceResetUserInterface(); + instanceClear(); + } + + virtual limiterdsp* clone() { + return new limiterdsp(); + } + + virtual int getSampleRate() { + return fSampleRate; + } + + virtual void buildUserInterface(UI* ui_interface) { + ui_interface->openVerticalBox("limiterdsp"); + ui_interface->declare(&fHslider0, "0", ""); + ui_interface->addHorizontalSlider("NumClientsAssumed", &fHslider0, 2.0f, 1.0f, 64.0f, 1.0f); + ui_interface->closeBox(); + } + + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { + FAUSTFLOAT* input0 = inputs[0]; + FAUSTFLOAT* output0 = outputs[0]; + float fSlow0 = (1.0f / std::sqrt(float(fHslider0))); + for (int i = 0; (i < count); i = (i + 1)) { + float fTemp0 = float(input0[i]); + iRec5[0] = ((iRec5[1] + 1) % int(std::max<float>(1.0f, (fConst3 * float(iRec2[1]))))); + float fTemp1 = (fSlow0 * fTemp0); + fVec0[(IOTA & 31)] = fTemp1; + float fTemp2 = std::fabs(fTemp1); + fRec4[0] = std::max<float>((float((iRec5[0] > 0)) * fRec4[1]), fTemp2); + iRec2[0] = (fRec4[0] >= fTemp2); + float fRec3 = fRec4[0]; + fRec1[0] = ((fConst1 * fRec1[1]) + (fConst2 * fRec3)); + float fTemp3 = std::fabs(fRec1[0]); + fRec0[0] = std::max<float>(fTemp3, ((fConst4 * fRec0[1]) + (fConst5 * fTemp3))); + output0[i] = FAUSTFLOAT((std::min<float>(1.0f, (0.5f / std::max<float>(fRec0[0], 1.1920929e-07f))) * fVec0[((IOTA - iConst6) & 31)])); + iRec5[1] = iRec5[0]; + IOTA = (IOTA + 1); + fRec4[1] = fRec4[0]; + iRec2[1] = iRec2[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + } + } + +}; + +#endif diff --git a/src/makeXcodeproj.sh b/src/makeXcodeproj.sh new file mode 100755 index 0000000..c1dcf31 --- /dev/null +++ b/src/makeXcodeproj.sh @@ -0,0 +1 @@ +qmake -spec macx-xcode jacktrip.pro diff --git a/src/zitarevdsp.h b/src/zitarevdsp.h new file mode 100644 index 0000000..96067d4 --- /dev/null +++ b/src/zitarevdsp.h @@ -0,0 +1,2375 @@ +/* ------------------------------------------------------------ +name: "zitarevdsp" +Code generated with Faust 2.28.6 (https://faust.grame.fr) +Compilation options: -lang cpp -inpl -scal -ftz 0 +------------------------------------------------------------ */ + +#ifndef __zitarevdsp_H__ +#define __zitarevdsp_H__ + +// NOTE: ANY INCLUDE-GUARD HERE MUST BE DERIVED FROM THE CLASS NAME +// +// faust2header.cpp - FAUST Architecture File +// This is a simple variation of matlabplot.cpp in the Faust distribution +// aimed at creating a simple C++ header file (.h) containing a Faust DSP. +// See the Makefile for how to use it. + +/************************** BEGIN dsp.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __dsp__ +#define __dsp__ + +#include <string> +#include <vector> + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +struct UI; +struct Meta; + +/** + * DSP memory manager. + */ + +struct dsp_memory_manager { + + virtual ~dsp_memory_manager() {} + + virtual void* allocate(size_t size) = 0; + virtual void destroy(void* ptr) = 0; + +}; + +/** +* Signal processor definition. +*/ + +class dsp { + + public: + + dsp() {} + virtual ~dsp() {} + + /* Return instance number of audio inputs */ + virtual int getNumInputs() = 0; + + /* Return instance number of audio outputs */ + virtual int getNumOutputs() = 0; + + /** + * Trigger the ui_interface parameter with instance specific calls + * to 'openTabBox', 'addButton', 'addVerticalSlider'... in order to build the UI. + * + * @param ui_interface - the user interface builder + */ + virtual void buildUserInterface(UI* ui_interface) = 0; + + /* Returns the sample rate currently used by the instance */ + virtual int getSampleRate() = 0; + + /** + * Global init, calls the following methods: + * - static class 'classInit': static tables initialization + * - 'instanceInit': constants and instance state initialization + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void init(int sample_rate) = 0; + + /** + * Init instance state + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void instanceInit(int sample_rate) = 0; + + /** + * Init instance constant state + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void instanceConstants(int sample_rate) = 0; + + /* Init default control parameters values */ + virtual void instanceResetUserInterface() = 0; + + /* Init instance state (delay lines...) */ + virtual void instanceClear() = 0; + + /** + * Return a clone of the instance. + * + * @return a copy of the instance on success, otherwise a null pointer. + */ + virtual dsp* clone() = 0; + + /** + * Trigger the Meta* parameter with instance specific calls to 'declare' (key, value) metadata. + * + * @param m - the Meta* meta user + */ + virtual void metadata(Meta* m) = 0; + + /** + * DSP instance computation, to be called with successive in/out audio buffers. + * + * @param count - the number of frames to compute + * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) + * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) + * + */ + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0; + + /** + * DSP instance computation: alternative method to be used by subclasses. + * + * @param date_usec - the timestamp in microsec given by audio driver. + * @param count - the number of frames to compute + * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) + * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) + * + */ + virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); } + +}; + +/** + * Generic DSP decorator. + */ + +class decorator_dsp : public dsp { + + protected: + + dsp* fDSP; + + public: + + decorator_dsp(dsp* dsp = nullptr):fDSP(dsp) {} + virtual ~decorator_dsp() { delete fDSP; } + + virtual int getNumInputs() { return fDSP->getNumInputs(); } + virtual int getNumOutputs() { return fDSP->getNumOutputs(); } + virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); } + virtual int getSampleRate() { return fDSP->getSampleRate(); } + virtual void init(int sample_rate) { fDSP->init(sample_rate); } + virtual void instanceInit(int sample_rate) { fDSP->instanceInit(sample_rate); } + virtual void instanceConstants(int sample_rate) { fDSP->instanceConstants(sample_rate); } + virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); } + virtual void instanceClear() { fDSP->instanceClear(); } + virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); } + virtual void metadata(Meta* m) { fDSP->metadata(m); } + // Beware: subclasses usually have to overload the two 'compute' methods + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); } + virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); } + +}; + +/** + * DSP factory class. + */ + +class dsp_factory { + + protected: + + // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory); + virtual ~dsp_factory() {} + + public: + + virtual std::string getName() = 0; + virtual std::string getSHAKey() = 0; + virtual std::string getDSPCode() = 0; + virtual std::string getCompileOptions() = 0; + virtual std::vector<std::string> getLibraryList() = 0; + virtual std::vector<std::string> getIncludePathnames() = 0; + + virtual dsp* createDSPInstance() = 0; + + virtual void setMemoryManager(dsp_memory_manager* manager) = 0; + virtual dsp_memory_manager* getMemoryManager() = 0; + +}; + +/** + * On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero) + * flags to avoid costly denormals. + */ + +#ifdef __SSE__ + #include <xmmintrin.h> + #ifdef __SSE2__ + #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040) + #else + #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000) + #endif +#else + #define AVOIDDENORMALS +#endif + +#endif +/************************** END dsp.h **************************/ + +/************************** BEGIN APIUI.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef API_UI_H +#define API_UI_H + +#include <sstream> +#include <string> +#include <vector> +#include <iostream> +#include <map> + +/************************** BEGIN meta.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __meta__ +#define __meta__ + +struct Meta +{ + virtual ~Meta() {}; + virtual void declare(const char* key, const char* value) = 0; + +}; + +#endif +/************************** END meta.h **************************/ +/************************** BEGIN UI.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2020 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __UI_H__ +#define __UI_H__ + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +/******************************************************************************* + * UI : Faust DSP User Interface + * User Interface as expected by the buildUserInterface() method of a DSP. + * This abstract class contains only the method that the Faust compiler can + * generate to describe a DSP user interface. + ******************************************************************************/ + +struct Soundfile; + +template <typename REAL> +struct UIReal +{ + UIReal() {} + virtual ~UIReal() {} + + // -- widget's layouts + + virtual void openTabBox(const char* label) = 0; + virtual void openHorizontalBox(const char* label) = 0; + virtual void openVerticalBox(const char* label) = 0; + virtual void closeBox() = 0; + + // -- active widgets + + virtual void addButton(const char* label, REAL* zone) = 0; + virtual void addCheckButton(const char* label, REAL* zone) = 0; + virtual void addVerticalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + virtual void addHorizontalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + virtual void addNumEntry(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + + // -- passive widgets + + virtual void addHorizontalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0; + virtual void addVerticalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0; + + // -- soundfiles + + virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) = 0; + + // -- metadata declarations + + virtual void declare(REAL* zone, const char* key, const char* val) {} +}; + +struct UI : public UIReal<FAUSTFLOAT> +{ + UI() {} + virtual ~UI() {} +}; + +#endif +/************************** END UI.h **************************/ +/************************** BEGIN PathBuilder.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef FAUST_PATHBUILDER_H +#define FAUST_PATHBUILDER_H + +#include <vector> +#include <string> +#include <algorithm> + +/******************************************************************************* + * PathBuilder : Faust User Interface + * Helper class to build complete hierarchical path for UI items. + ******************************************************************************/ + +class PathBuilder +{ + + protected: + + std::vector<std::string> fControlsLevel; + + public: + + PathBuilder() {} + virtual ~PathBuilder() {} + + std::string buildPath(const std::string& label) + { + std::string res = "/"; + for (size_t i = 0; i < fControlsLevel.size(); i++) { + res += fControlsLevel[i]; + res += "/"; + } + res += label; + std::replace(res.begin(), res.end(), ' ', '_'); + return res; + } + + std::string buildLabel(std::string label) + { + std::replace(label.begin(), label.end(), ' ', '_'); + return label; + } + + void pushLabel(const std::string& label) { fControlsLevel.push_back(label); } + void popLabel() { fControlsLevel.pop_back(); } + +}; + +#endif // FAUST_PATHBUILDER_H +/************************** END PathBuilder.h **************************/ +/************************** BEGIN ValueConverter.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __ValueConverter__ +#define __ValueConverter__ + +/*************************************************************************************** + ValueConverter.h + (GRAME, Copyright 2015-2019) + +Set of conversion objects used to map user interface values (for example a gui slider +delivering values between 0 and 1) to faust values (for example a vslider between +20 and 20000) using a log scale. + +-- Utilities + +Range(lo,hi) : clip a value x between lo and hi +Interpolator(lo,hi,v1,v2) : Maps a value x between lo and hi to a value y between v1 and v2 +Interpolator3pt(lo,mi,hi,v1,vm,v2) : Map values between lo mid hi to values between v1 vm v2 + +-- Value Converters + +ValueConverter::ui2faust(x) +ValueConverter::faust2ui(x) + +-- ValueConverters used for sliders depending of the scale + +LinearValueConverter(umin, umax, fmin, fmax) +LinearValueConverter2(lo, mi, hi, v1, vm, v2) using 2 segments +LogValueConverter(umin, umax, fmin, fmax) +ExpValueConverter(umin, umax, fmin, fmax) + +-- ValueConverters used for accelerometers based on 3 points + +AccUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 0 +AccDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 1 +AccUpDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 2 +AccDownUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 3 + +-- lists of ZoneControl are used to implement accelerometers metadata for each axes + +ZoneControl(zone, valueConverter) : a zone with an accelerometer data converter + +-- ZoneReader are used to implement screencolor metadata + +ZoneReader(zone, valueConverter) : a zone with a data converter + +****************************************************************************************/ + +#include <float.h> +#include <algorithm> // std::max +#include <cmath> +#include <vector> +#include <assert.h> + +//-------------------------------------------------------------------------------------- +// Interpolator(lo,hi,v1,v2) +// Maps a value x between lo and hi to a value y between v1 and v2 +// y = v1 + (x-lo)/(hi-lo)*(v2-v1) +// y = v1 + (x-lo) * coef with coef = (v2-v1)/(hi-lo) +// y = v1 + x*coef - lo*coef +// y = v1 - lo*coef + x*coef +// y = offset + x*coef with offset = v1 - lo*coef +//-------------------------------------------------------------------------------------- +class Interpolator +{ + private: + + //-------------------------------------------------------------------------------------- + // Range(lo,hi) clip a value between lo and hi + //-------------------------------------------------------------------------------------- + struct Range + { + double fLo; + double fHi; + + Range(double x, double y) : fLo(std::min<double>(x,y)), fHi(std::max<double>(x,y)) {} + double operator()(double x) { return (x<fLo) ? fLo : (x>fHi) ? fHi : x; } + }; + + + Range fRange; + double fCoef; + double fOffset; + + public: + + Interpolator(double lo, double hi, double v1, double v2) : fRange(lo,hi) + { + if (hi != lo) { + // regular case + fCoef = (v2-v1)/(hi-lo); + fOffset = v1 - lo*fCoef; + } else { + // degenerate case, avoids division by zero + fCoef = 0; + fOffset = (v1+v2)/2; + } + } + double operator()(double v) + { + double x = fRange(v); + return fOffset + x*fCoef; + } + + void getLowHigh(double& amin, double& amax) + { + amin = fRange.fLo; + amax = fRange.fHi; + } +}; + +//-------------------------------------------------------------------------------------- +// Interpolator3pt(lo,mi,hi,v1,vm,v2) +// Map values between lo mid hi to values between v1 vm v2 +//-------------------------------------------------------------------------------------- +class Interpolator3pt +{ + + private: + + Interpolator fSegment1; + Interpolator fSegment2; + double fMid; + + public: + + Interpolator3pt(double lo, double mi, double hi, double v1, double vm, double v2) : + fSegment1(lo, mi, v1, vm), + fSegment2(mi, hi, vm, v2), + fMid(mi) {} + double operator()(double x) { return (x < fMid) ? fSegment1(x) : fSegment2(x); } + + void getMappingValues(double& amin, double& amid, double& amax) + { + fSegment1.getLowHigh(amin, amid); + fSegment2.getLowHigh(amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Abstract ValueConverter class. Converts values between UI and Faust representations +//-------------------------------------------------------------------------------------- +class ValueConverter +{ + + public: + + virtual ~ValueConverter() {} + virtual double ui2faust(double x) = 0; + virtual double faust2ui(double x) = 0; +}; + +//-------------------------------------------------------------------------------------- +// A converter than can be updated +//-------------------------------------------------------------------------------------- + +class UpdatableValueConverter : public ValueConverter { + + protected: + + bool fActive; + + public: + + UpdatableValueConverter():fActive(true) + {} + virtual ~UpdatableValueConverter() + {} + + virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) = 0; + virtual void getMappingValues(double& amin, double& amid, double& amax) = 0; + + void setActive(bool on_off) { fActive = on_off; } + bool getActive() { return fActive; } + +}; + + +//-------------------------------------------------------------------------------------- +// Linear conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LinearValueConverter : public ValueConverter +{ + + private: + + Interpolator fUI2F; + Interpolator fF2UI; + + public: + + LinearValueConverter(double umin, double umax, double fmin, double fmax) : + fUI2F(umin,umax,fmin,fmax), fF2UI(fmin,fmax,umin,umax) + {} + + LinearValueConverter() : fUI2F(0.,0.,0.,0.), fF2UI(0.,0.,0.,0.) + {} + virtual double ui2faust(double x) { return fUI2F(x); } + virtual double faust2ui(double x) { return fF2UI(x); } + +}; + +//-------------------------------------------------------------------------------------- +// Two segments linear conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LinearValueConverter2 : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fUI2F; + Interpolator3pt fF2UI; + + public: + + LinearValueConverter2(double amin, double amid, double amax, double min, double init, double max) : + fUI2F(amin, amid, amax, min, init, max), fF2UI(min, init, max, amin, amid, amax) + {} + + LinearValueConverter2() : fUI2F(0.,0.,0.,0.,0.,0.), fF2UI(0.,0.,0.,0.,0.,0.) + {} + + virtual double ui2faust(double x) { return fUI2F(x); } + virtual double faust2ui(double x) { return fF2UI(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) + { + fUI2F = Interpolator3pt(amin, amid, amax, min, init, max); + fF2UI = Interpolator3pt(min, init, max, amin, amid, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fUI2F.getMappingValues(amin, amid, amax); + } + +}; + +//-------------------------------------------------------------------------------------- +// Logarithmic conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LogValueConverter : public LinearValueConverter +{ + + public: + + LogValueConverter(double umin, double umax, double fmin, double fmax) : + LinearValueConverter(umin, umax, std::log(std::max<double>(DBL_MIN, fmin)), std::log(std::max<double>(DBL_MIN, fmax))) + {} + + virtual double ui2faust(double x) { return std::exp(LinearValueConverter::ui2faust(x)); } + virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::log(std::max<double>(x, DBL_MIN))); } + +}; + +//-------------------------------------------------------------------------------------- +// Exponential conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class ExpValueConverter : public LinearValueConverter +{ + + public: + + ExpValueConverter(double umin, double umax, double fmin, double fmax) : + LinearValueConverter(umin, umax, std::min<double>(DBL_MAX, std::exp(fmin)), std::min<double>(DBL_MAX, std::exp(fmax))) + {} + + virtual double ui2faust(double x) { return std::log(LinearValueConverter::ui2faust(x)); } + virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::min<double>(DBL_MAX, std::exp(x))); } + +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using an Up curve (curve 0) +//-------------------------------------------------------------------------------------- +class AccUpConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator3pt fF2A; + + public: + + AccUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmin,fmid,fmax), + fF2A(fmin,fmid,fmax,amin,amid,amax) + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmin, fmid, fmax); + fF2A = Interpolator3pt(fmin, fmid, fmax, amin, amid, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } + +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using a Down curve (curve 1) +//-------------------------------------------------------------------------------------- +class AccDownConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator3pt fF2A; + + public: + + AccDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmax,fmid,fmin), + fF2A(fmin,fmid,fmax,amax,amid,amin) + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmax, fmid, fmin); + fF2A = Interpolator3pt(fmin, fmid, fmax, amax, amid, amin); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using an Up-Down curve (curve 2) +//-------------------------------------------------------------------------------------- +class AccUpDownConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator fF2A; + + public: + + AccUpDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmin,fmax,fmin), + fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotonic function + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmin, fmax, fmin); + fF2A = Interpolator(fmin, fmax, amin, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using a Down-Up curve (curve 3) +//-------------------------------------------------------------------------------------- +class AccDownUpConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator fF2A; + + public: + + AccDownUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmax,fmin,fmax), + fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotonic function + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmax, fmin, fmax); + fF2A = Interpolator(fmin, fmax, amin, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Base class for ZoneControl +//-------------------------------------------------------------------------------------- +class ZoneControl +{ + + protected: + + FAUSTFLOAT* fZone; + + public: + + ZoneControl(FAUSTFLOAT* zone) : fZone(zone) {} + virtual ~ZoneControl() {} + + virtual void update(double v) const {} + + virtual void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) {} + virtual void getMappingValues(double& amin, double& amid, double& amax) {} + + FAUSTFLOAT* getZone() { return fZone; } + + virtual void setActive(bool on_off) {} + virtual bool getActive() { return false; } + + virtual int getCurve() { return -1; } + +}; + +//-------------------------------------------------------------------------------------- +// Useful to implement accelerometers metadata as a list of ZoneControl for each axes +//-------------------------------------------------------------------------------------- +class ConverterZoneControl : public ZoneControl +{ + + protected: + + ValueConverter* fValueConverter; + + public: + + ConverterZoneControl(FAUSTFLOAT* zone, ValueConverter* converter) : ZoneControl(zone), fValueConverter(converter) {} + virtual ~ConverterZoneControl() { delete fValueConverter; } // Assuming fValueConverter is not kept elsewhere... + + virtual void update(double v) const { *fZone = fValueConverter->ui2faust(v); } + + ValueConverter* getConverter() { return fValueConverter; } + +}; + +//-------------------------------------------------------------------------------------- +// Association of a zone and a four value converter, each one for each possible curve. +// Useful to implement accelerometers metadata as a list of ZoneControl for each axes +//-------------------------------------------------------------------------------------- +class CurveZoneControl : public ZoneControl +{ + + private: + + std::vector<UpdatableValueConverter*> fValueConverters; + int fCurve; + + public: + + CurveZoneControl(FAUSTFLOAT* zone, int curve, double amin, double amid, double amax, double min, double init, double max) : ZoneControl(zone), fCurve(0) + { + assert(curve >= 0 && curve <= 3); + fValueConverters.push_back(new AccUpConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccDownConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccUpDownConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccDownUpConverter(amin, amid, amax, min, init, max)); + fCurve = curve; + } + virtual ~CurveZoneControl() + { + std::vector<UpdatableValueConverter*>::iterator it; + for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) { + delete(*it); + } + } + void update(double v) const { if (fValueConverters[fCurve]->getActive()) *fZone = fValueConverters[fCurve]->ui2faust(v); } + + void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) + { + fValueConverters[curve]->setMappingValues(amin, amid, amax, min, init, max); + fCurve = curve; + } + + void getMappingValues(double& amin, double& amid, double& amax) + { + fValueConverters[fCurve]->getMappingValues(amin, amid, amax); + } + + void setActive(bool on_off) + { + std::vector<UpdatableValueConverter*>::iterator it; + for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) { + (*it)->setActive(on_off); + } + } + + int getCurve() { return fCurve; } +}; + +class ZoneReader +{ + + private: + + FAUSTFLOAT* fZone; + Interpolator fInterpolator; + + public: + + ZoneReader(FAUSTFLOAT* zone, double lo, double hi) : fZone(zone), fInterpolator(lo, hi, 0, 255) {} + + virtual ~ZoneReader() {} + + int getValue() + { + return (fZone != nullptr) ? int(fInterpolator(*fZone)) : 127; + } + +}; + +#endif +/************************** END ValueConverter.h **************************/ + +class APIUI : public PathBuilder, public Meta, public UI +{ + public: + + enum ItemType { kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph }; + + protected: + + enum { kLin = 0, kLog = 1, kExp = 2 }; + + int fNumParameters; + std::vector<std::string> fPaths; + std::vector<std::string> fLabels; + std::map<std::string, int> fPathMap; + std::map<std::string, int> fLabelMap; + std::vector<ValueConverter*> fConversion; + std::vector<FAUSTFLOAT*> fZone; + std::vector<FAUSTFLOAT> fInit; + std::vector<FAUSTFLOAT> fMin; + std::vector<FAUSTFLOAT> fMax; + std::vector<FAUSTFLOAT> fStep; + std::vector<ItemType> fItemType; + std::vector<std::map<std::string, std::string> > fMetaData; + std::vector<ZoneControl*> fAcc[3]; + std::vector<ZoneControl*> fGyr[3]; + + // Screen color control + // "...[screencolor:red]..." etc. + bool fHasScreenControl; // true if control screen color metadata + ZoneReader* fRedReader; + ZoneReader* fGreenReader; + ZoneReader* fBlueReader; + + // Current values controlled by metadata + std::string fCurrentUnit; + int fCurrentScale; + std::string fCurrentAcc; + std::string fCurrentGyr; + std::string fCurrentColor; + std::string fCurrentTooltip; + std::map<std::string, std::string> fCurrentMetadata; + + // Add a generic parameter + virtual void addParameter(const char* label, + FAUSTFLOAT* zone, + FAUSTFLOAT init, + FAUSTFLOAT min, + FAUSTFLOAT max, + FAUSTFLOAT step, + ItemType type) + { + std::string path = buildPath(label); + fPathMap[path] = fLabelMap[label] = fNumParameters++; + fPaths.push_back(path); + fLabels.push_back(label); + fZone.push_back(zone); + fInit.push_back(init); + fMin.push_back(min); + fMax.push_back(max); + fStep.push_back(step); + fItemType.push_back(type); + + // handle scale metadata + switch (fCurrentScale) { + case kLin: + fConversion.push_back(new LinearValueConverter(0, 1, min, max)); + break; + case kLog: + fConversion.push_back(new LogValueConverter(0, 1, min, max)); + break; + case kExp: fConversion.push_back(new ExpValueConverter(0, 1, min, max)); + break; + } + fCurrentScale = kLin; + + if (fCurrentAcc.size() > 0 && fCurrentGyr.size() > 0) { + std::cerr << "warning : 'acc' and 'gyr' metadata used for the same " << label << " parameter !!\n"; + } + + // handle acc metadata "...[acc : <axe> <curve> <amin> <amid> <amax>]..." + if (fCurrentAcc.size() > 0) { + std::istringstream iss(fCurrentAcc); + int axe, curve; + double amin, amid, amax; + iss >> axe >> curve >> amin >> amid >> amax; + + if ((0 <= axe) && (axe < 3) && + (0 <= curve) && (curve < 4) && + (amin < amax) && (amin <= amid) && (amid <= amax)) + { + fAcc[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); + } else { + std::cerr << "incorrect acc metadata : " << fCurrentAcc << std::endl; + } + fCurrentAcc = ""; + } + + // handle gyr metadata "...[gyr : <axe> <curve> <amin> <amid> <amax>]..." + if (fCurrentGyr.size() > 0) { + std::istringstream iss(fCurrentGyr); + int axe, curve; + double amin, amid, amax; + iss >> axe >> curve >> amin >> amid >> amax; + + if ((0 <= axe) && (axe < 3) && + (0 <= curve) && (curve < 4) && + (amin < amax) && (amin <= amid) && (amid <= amax)) + { + fGyr[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); + } else { + std::cerr << "incorrect gyr metadata : " << fCurrentGyr << std::endl; + } + fCurrentGyr = ""; + } + + // handle screencolor metadata "...[screencolor:red|green|blue|white]..." + if (fCurrentColor.size() > 0) { + if ((fCurrentColor == "red") && (fRedReader == 0)) { + fRedReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "green") && (fGreenReader == 0)) { + fGreenReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "blue") && (fBlueReader == 0)) { + fBlueReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "white") && (fRedReader == 0) && (fGreenReader == 0) && (fBlueReader == 0)) { + fRedReader = new ZoneReader(zone, min, max); + fGreenReader = new ZoneReader(zone, min, max); + fBlueReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else { + std::cerr << "incorrect screencolor metadata : " << fCurrentColor << std::endl; + } + } + fCurrentColor = ""; + + fMetaData.push_back(fCurrentMetadata); + fCurrentMetadata.clear(); + } + + int getZoneIndex(std::vector<ZoneControl*>* table, int p, int val) + { + FAUSTFLOAT* zone = fZone[p]; + for (size_t i = 0; i < table[val].size(); i++) { + if (zone == table[val][i]->getZone()) return int(i); + } + return -1; + } + + void setConverter(std::vector<ZoneControl*>* table, int p, int val, int curve, double amin, double amid, double amax) + { + int id1 = getZoneIndex(table, p, 0); + int id2 = getZoneIndex(table, p, 1); + int id3 = getZoneIndex(table, p, 2); + + // Deactivates everywhere.. + if (id1 != -1) table[0][id1]->setActive(false); + if (id2 != -1) table[1][id2]->setActive(false); + if (id3 != -1) table[2][id3]->setActive(false); + + if (val == -1) { // Means: no more mapping... + // So stay all deactivated... + } else { + int id4 = getZoneIndex(table, p, val); + if (id4 != -1) { + // Reactivate the one we edit... + table[val][id4]->setMappingValues(curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]); + table[val][id4]->setActive(true); + } else { + // Allocate a new CurveZoneControl which is 'active' by default + FAUSTFLOAT* zone = fZone[p]; + table[val].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, fMin[p], fInit[p], fMax[p])); + } + } + } + + void getConverter(std::vector<ZoneControl*>* table, int p, int& val, int& curve, double& amin, double& amid, double& amax) + { + int id1 = getZoneIndex(table, p, 0); + int id2 = getZoneIndex(table, p, 1); + int id3 = getZoneIndex(table, p, 2); + + if (id1 != -1) { + val = 0; + curve = table[val][id1]->getCurve(); + table[val][id1]->getMappingValues(amin, amid, amax); + } else if (id2 != -1) { + val = 1; + curve = table[val][id2]->getCurve(); + table[val][id2]->getMappingValues(amin, amid, amax); + } else if (id3 != -1) { + val = 2; + curve = table[val][id3]->getCurve(); + table[val][id3]->getMappingValues(amin, amid, amax); + } else { + val = -1; // No mapping + curve = 0; + amin = -100.; + amid = 0.; + amax = 100.; + } + } + + public: + + enum Type { kAcc = 0, kGyr = 1, kNoType }; + + APIUI() : fNumParameters(0), fHasScreenControl(false), fRedReader(0), fGreenReader(0), fBlueReader(0), fCurrentScale(kLin) + {} + + virtual ~APIUI() + { + for (auto& it : fConversion) delete it; + for (int i = 0; i < 3; i++) { + for (auto& it : fAcc[i]) delete it; + for (auto& it : fGyr[i]) delete it; + } + delete fRedReader; + delete fGreenReader; + delete fBlueReader; + } + + // -- widget's layouts + + virtual void openTabBox(const char* label) { pushLabel(label); } + virtual void openHorizontalBox(const char* label) { pushLabel(label); } + virtual void openVerticalBox(const char* label) { pushLabel(label); } + virtual void closeBox() { popLabel(); } + + // -- active widgets + + virtual void addButton(const char* label, FAUSTFLOAT* zone) + { + addParameter(label, zone, 0, 0, 1, 1, kButton); + } + + virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) + { + addParameter(label, zone, 0, 0, 1, 1, kCheckButton); + } + + virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kVSlider); + } + + virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kHSlider); + } + + virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kNumEntry); + } + + // -- passive widgets + + virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) + { + addParameter(label, zone, min, min, max, (max-min)/1000.0, kHBargraph); + } + + virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) + { + addParameter(label, zone, min, min, max, (max-min)/1000.0, kVBargraph); + } + + // -- soundfiles + + virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) {} + + // -- metadata declarations + + virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val) + { + // Keep metadata + fCurrentMetadata[key] = val; + + if (strcmp(key, "scale") == 0) { + if (strcmp(val, "log") == 0) { + fCurrentScale = kLog; + } else if (strcmp(val, "exp") == 0) { + fCurrentScale = kExp; + } else { + fCurrentScale = kLin; + } + } else if (strcmp(key, "unit") == 0) { + fCurrentUnit = val; + } else if (strcmp(key, "acc") == 0) { + fCurrentAcc = val; + } else if (strcmp(key, "gyr") == 0) { + fCurrentGyr = val; + } else if (strcmp(key, "screencolor") == 0) { + fCurrentColor = val; // val = "red", "green", "blue" or "white" + } else if (strcmp(key, "tooltip") == 0) { + fCurrentTooltip = val; + } + } + + virtual void declare(const char* key, const char* val) + {} + + //------------------------------------------------------------------------------- + // Simple API part + //------------------------------------------------------------------------------- + int getParamsCount() { return fNumParameters; } + int getParamIndex(const char* path) + { + if (fPathMap.find(path) != fPathMap.end()) { + return fPathMap[path]; + } else if (fLabelMap.find(path) != fLabelMap.end()) { + return fLabelMap[path]; + } else { + return -1; + } + } + const char* getParamAddress(int p) { return fPaths[p].c_str(); } + const char* getParamLabel(int p) { return fLabels[p].c_str(); } + std::map<const char*, const char*> getMetadata(int p) + { + std::map<const char*, const char*> res; + std::map<std::string, std::string> metadata = fMetaData[p]; + for (auto it : metadata) { + res[it.first.c_str()] = it.second.c_str(); + } + return res; + } + + const char* getMetadata(int p, const char* key) + { + return (fMetaData[p].find(key) != fMetaData[p].end()) ? fMetaData[p][key].c_str() : ""; + } + FAUSTFLOAT getParamMin(int p) { return fMin[p]; } + FAUSTFLOAT getParamMax(int p) { return fMax[p]; } + FAUSTFLOAT getParamStep(int p) { return fStep[p]; } + FAUSTFLOAT getParamInit(int p) { return fInit[p]; } + + FAUSTFLOAT* getParamZone(int p) { return fZone[p]; } + FAUSTFLOAT getParamValue(int p) { return *fZone[p]; } + void setParamValue(int p, FAUSTFLOAT v) { *fZone[p] = v; } + + double getParamRatio(int p) { return fConversion[p]->faust2ui(*fZone[p]); } + void setParamRatio(int p, double r) { *fZone[p] = fConversion[p]->ui2faust(r); } + + double value2ratio(int p, double r) { return fConversion[p]->faust2ui(r); } + double ratio2value(int p, double r) { return fConversion[p]->ui2faust(r); } + + /** + * Return the control type (kAcc, kGyr, or -1) for a given parameter + * + * @param p - the UI parameter index + * + * @return the type + */ + Type getParamType(int p) + { + if (p >= 0) { + if (getZoneIndex(fAcc, p, 0) != -1 + || getZoneIndex(fAcc, p, 1) != -1 + || getZoneIndex(fAcc, p, 2) != -1) { + return kAcc; + } else if (getZoneIndex(fGyr, p, 0) != -1 + || getZoneIndex(fGyr, p, 1) != -1 + || getZoneIndex(fGyr, p, 2) != -1) { + return kGyr; + } + } + return kNoType; + } + + /** + * Return the Item type (kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph) for a given parameter + * + * @param p - the UI parameter index + * + * @return the Item type + */ + ItemType getParamItemType(int p) + { + return fItemType[p]; + } + + /** + * Set a new value coming from an accelerometer, propagate it to all relevant FAUSTFLOAT* zones. + * + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer + * @param value - the new value + * + */ + void propagateAcc(int acc, double value) + { + for (size_t i = 0; i < fAcc[acc].size(); i++) { + fAcc[acc][i]->update(value); + } + } + + /** + * Used to edit accelerometer curves and mapping. Set curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer (-1 means "no mapping") + * @param curve - between 0 and 3 + * @param amin - mapping 'min' point + * @param amid - mapping 'middle' point + * @param amax - mapping 'max' point + * + */ + void setAccConverter(int p, int acc, int curve, double amin, double amid, double amax) + { + setConverter(fAcc, p, acc, curve, amin, amid, amax); + } + + /** + * Used to edit gyroscope curves and mapping. Set curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope (-1 means "no mapping") + * @param curve - between 0 and 3 + * @param amin - mapping 'min' point + * @param amid - mapping 'middle' point + * @param amax - mapping 'max' point + * + */ + void setGyrConverter(int p, int gyr, int curve, double amin, double amid, double amax) + { + setConverter(fGyr, p, gyr, curve, amin, amid, amax); + } + + /** + * Used to edit accelerometer curves and mapping. Get curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - the acc value to be retrieved (-1 means "no mapping") + * @param curve - the curve value to be retrieved + * @param amin - the amin value to be retrieved + * @param amid - the amid value to be retrieved + * @param amax - the amax value to be retrieved + * + */ + void getAccConverter(int p, int& acc, int& curve, double& amin, double& amid, double& amax) + { + getConverter(fAcc, p, acc, curve, amin, amid, amax); + } + + /** + * Used to edit gyroscope curves and mapping. Get curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param gyr - the gyr value to be retrieved (-1 means "no mapping") + * @param curve - the curve value to be retrieved + * @param amin - the amin value to be retrieved + * @param amid - the amid value to be retrieved + * @param amax - the amax value to be retrieved + * + */ + void getGyrConverter(int p, int& gyr, int& curve, double& amin, double& amid, double& amax) + { + getConverter(fGyr, p, gyr, curve, amin, amid, amax); + } + + /** + * Set a new value coming from an gyroscope, propagate it to all relevant FAUSTFLOAT* zones. + * + * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope + * @param value - the new value + * + */ + void propagateGyr(int gyr, double value) + { + for (size_t i = 0; i < fGyr[gyr].size(); i++) { + fGyr[gyr][i]->update(value); + } + } + + /** + * Get the number of FAUSTFLOAT* zones controlled with the accelerometer + * + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer + * @return the number of zones + * + */ + int getAccCount(int acc) + { + return (acc >= 0 && acc < 3) ? int(fAcc[acc].size()) : 0; + } + + /** + * Get the number of FAUSTFLOAT* zones controlled with the gyroscope + * + * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope + * @param the number of zones + * + */ + int getGyrCount(int gyr) + { + return (gyr >= 0 && gyr < 3) ? int(fGyr[gyr].size()) : 0; + } + + // getScreenColor() : -1 means no screen color control (no screencolor metadata found) + // otherwise return 0x00RRGGBB a ready to use color + int getScreenColor() + { + if (fHasScreenControl) { + int r = (fRedReader) ? fRedReader->getValue() : 0; + int g = (fGreenReader) ? fGreenReader->getValue() : 0; + int b = (fBlueReader) ? fBlueReader->getValue() : 0; + return (r<<16) | (g<<8) | b; + } else { + return -1; + } + } + +}; + +#endif +/************************** END APIUI.h **************************/ + +// NOTE: "faust -scn name" changes the last line above to +// #include <faust/name/name.h> + +//---------------------------------------------------------------------------- +// FAUST Generated Code +//---------------------------------------------------------------------------- + + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +#include <algorithm> +#include <cmath> +#include <math.h> + +static float zitarevdsp_faustpower2_f(float value) { + return (value * value); +} + +#ifndef FAUSTCLASS +#define FAUSTCLASS zitarevdsp +#endif + +#ifdef __APPLE__ +#define exp10f __exp10f +#define exp10 __exp10 +#endif + +class zitarevdsp : public dsp { + + private: + + int IOTA; + float fVec0[16384]; + float fVec1[16384]; + FAUSTFLOAT fVslider0; + float fRec0[2]; + FAUSTFLOAT fVslider1; + float fRec1[2]; + int fSampleRate; + float fConst0; + float fConst1; + FAUSTFLOAT fVslider2; + FAUSTFLOAT fVslider3; + FAUSTFLOAT fVslider4; + FAUSTFLOAT fVslider5; + float fConst2; + float fConst3; + FAUSTFLOAT fVslider6; + FAUSTFLOAT fVslider7; + FAUSTFLOAT fVslider8; + float fConst4; + FAUSTFLOAT fVslider9; + float fRec15[2]; + float fRec14[2]; + float fVec2[32768]; + float fConst5; + int iConst6; + float fConst7; + FAUSTFLOAT fVslider10; + float fVec3[2048]; + int iConst8; + float fRec12[2]; + float fConst9; + float fConst10; + float fRec19[2]; + float fRec18[2]; + float fVec4[32768]; + float fConst11; + int iConst12; + float fVec5[4096]; + int iConst13; + float fRec16[2]; + float fConst14; + float fConst15; + float fRec23[2]; + float fRec22[2]; + float fVec6[16384]; + float fConst16; + int iConst17; + float fVec7[4096]; + int iConst18; + float fRec20[2]; + float fConst19; + float fConst20; + float fRec27[2]; + float fRec26[2]; + float fVec8[32768]; + float fConst21; + int iConst22; + float fVec9[4096]; + int iConst23; + float fRec24[2]; + float fConst24; + float fConst25; + float fRec31[2]; + float fRec30[2]; + float fVec10[16384]; + float fConst26; + int iConst27; + float fVec11[2048]; + int iConst28; + float fRec28[2]; + float fConst29; + float fConst30; + float fRec35[2]; + float fRec34[2]; + float fVec12[16384]; + float fConst31; + int iConst32; + float fVec13[4096]; + int iConst33; + float fRec32[2]; + float fConst34; + float fConst35; + float fRec39[2]; + float fRec38[2]; + float fVec14[16384]; + float fConst36; + int iConst37; + float fVec15[4096]; + int iConst38; + float fRec36[2]; + float fConst39; + float fConst40; + float fRec43[2]; + float fRec42[2]; + float fVec16[16384]; + float fConst41; + int iConst42; + float fVec17[2048]; + int iConst43; + float fRec40[2]; + float fRec4[3]; + float fRec5[3]; + float fRec6[3]; + float fRec7[3]; + float fRec8[3]; + float fRec9[3]; + float fRec10[3]; + float fRec11[3]; + float fRec3[3]; + float fRec2[3]; + float fRec45[3]; + float fRec44[3]; + + public: + + void metadata(Meta* m) { + m->declare("basics.lib/name", "Faust Basic Element Library"); + m->declare("basics.lib/version", "0.1"); + m->declare("delays.lib/name", "Faust Delay Library"); + m->declare("delays.lib/version", "0.1"); + m->declare("filename", "zitarevdsp.dsp"); + m->declare("filters.lib/allpass_comb:author", "Julius O. Smith III"); + m->declare("filters.lib/allpass_comb:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/allpass_comb:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/fir:author", "Julius O. Smith III"); + m->declare("filters.lib/fir:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/fir:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/iir:author", "Julius O. Smith III"); + m->declare("filters.lib/iir:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/iir:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/lowpass0_highpass1", "MIT-style STK-4.3 license"); + m->declare("filters.lib/lowpass0_highpass1:author", "Julius O. Smith III"); + m->declare("filters.lib/lowpass:author", "Julius O. Smith III"); + m->declare("filters.lib/lowpass:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/lowpass:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/name", "Faust Filters Library"); + m->declare("filters.lib/peak_eq_rm:author", "Julius O. Smith III"); + m->declare("filters.lib/peak_eq_rm:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/peak_eq_rm:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/tf1:author", "Julius O. Smith III"); + m->declare("filters.lib/tf1:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/tf1:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/tf1s:author", "Julius O. Smith III"); + m->declare("filters.lib/tf1s:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/tf1s:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/tf2:author", "Julius O. Smith III"); + m->declare("filters.lib/tf2:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/tf2:license", "MIT-style STK-4.3 license"); + m->declare("maths.lib/author", "GRAME"); + m->declare("maths.lib/copyright", "GRAME"); + m->declare("maths.lib/license", "LGPL with exception"); + m->declare("maths.lib/name", "Faust Math Library"); + m->declare("maths.lib/version", "2.3"); + m->declare("name", "zitarevdsp"); + m->declare("platform.lib/name", "Generic Platform Library"); + m->declare("platform.lib/version", "0.1"); + m->declare("reverbs.lib/name", "Faust Reverb Library"); + m->declare("reverbs.lib/version", "0.0"); + m->declare("routes.lib/name", "Faust Signal Routing Library"); + m->declare("routes.lib/version", "0.2"); + m->declare("signals.lib/name", "Faust Signal Routing Library"); + m->declare("signals.lib/version", "0.0"); + } + + virtual int getNumInputs() { + return 2; + } + virtual int getNumOutputs() { + return 2; + } + virtual int getInputRate(int channel) { + int rate; + switch ((channel)) { + case 0: { + rate = 1; + break; + } + case 1: { + rate = 1; + break; + } + default: { + rate = -1; + break; + } + } + return rate; + } + virtual int getOutputRate(int channel) { + int rate; + switch ((channel)) { + case 0: { + rate = 1; + break; + } + case 1: { + rate = 1; + break; + } + default: { + rate = -1; + break; + } + } + return rate; + } + + static void classInit(int sample_rate) { + } + + virtual void instanceConstants(int sample_rate) { + fSampleRate = sample_rate; + fConst0 = std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate))); + fConst1 = (6.28318548f / fConst0); + fConst2 = std::floor(((0.219990999f * fConst0) + 0.5f)); + fConst3 = ((0.0f - (6.90775537f * fConst2)) / fConst0); + fConst4 = (3.14159274f / fConst0); + fConst5 = std::floor(((0.0191229992f * fConst0) + 0.5f)); + iConst6 = int(std::min<float>(16384.0f, std::max<float>(0.0f, (fConst2 - fConst5)))); + fConst7 = (0.00100000005f * fConst0); + iConst8 = int(std::min<float>(1024.0f, std::max<float>(0.0f, (fConst5 + -1.0f)))); + fConst9 = std::floor(((0.256891012f * fConst0) + 0.5f)); + fConst10 = ((0.0f - (6.90775537f * fConst9)) / fConst0); + fConst11 = std::floor(((0.0273330007f * fConst0) + 0.5f)); + iConst12 = int(std::min<float>(16384.0f, std::max<float>(0.0f, (fConst9 - fConst11)))); + iConst13 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst11 + -1.0f)))); + fConst14 = std::floor(((0.192303002f * fConst0) + 0.5f)); + fConst15 = ((0.0f - (6.90775537f * fConst14)) / fConst0); + fConst16 = std::floor(((0.0292910002f * fConst0) + 0.5f)); + iConst17 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst14 - fConst16)))); + iConst18 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst16 + -1.0f)))); + fConst19 = std::floor(((0.210389003f * fConst0) + 0.5f)); + fConst20 = ((0.0f - (6.90775537f * fConst19)) / fConst0); + fConst21 = std::floor(((0.0244210009f * fConst0) + 0.5f)); + iConst22 = int(std::min<float>(16384.0f, std::max<float>(0.0f, (fConst19 - fConst21)))); + iConst23 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst21 + -1.0f)))); + fConst24 = std::floor(((0.125f * fConst0) + 0.5f)); + fConst25 = ((0.0f - (6.90775537f * fConst24)) / fConst0); + fConst26 = std::floor(((0.0134579996f * fConst0) + 0.5f)); + iConst27 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst24 - fConst26)))); + iConst28 = int(std::min<float>(1024.0f, std::max<float>(0.0f, (fConst26 + -1.0f)))); + fConst29 = std::floor(((0.127837002f * fConst0) + 0.5f)); + fConst30 = ((0.0f - (6.90775537f * fConst29)) / fConst0); + fConst31 = std::floor(((0.0316039994f * fConst0) + 0.5f)); + iConst32 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst29 - fConst31)))); + iConst33 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst31 + -1.0f)))); + fConst34 = std::floor(((0.174713001f * fConst0) + 0.5f)); + fConst35 = ((0.0f - (6.90775537f * fConst34)) / fConst0); + fConst36 = std::floor(((0.0229039993f * fConst0) + 0.5f)); + iConst37 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst34 - fConst36)))); + iConst38 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst36 + -1.0f)))); + fConst39 = std::floor(((0.153128996f * fConst0) + 0.5f)); + fConst40 = ((0.0f - (6.90775537f * fConst39)) / fConst0); + fConst41 = std::floor(((0.0203460008f * fConst0) + 0.5f)); + iConst42 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst39 - fConst41)))); + iConst43 = int(std::min<float>(1024.0f, std::max<float>(0.0f, (fConst41 + -1.0f)))); + } + + virtual void instanceResetUserInterface() { + fVslider0 = FAUSTFLOAT(-3.0f); + fVslider1 = FAUSTFLOAT(0.0f); + fVslider2 = FAUSTFLOAT(1500.0f); + fVslider3 = FAUSTFLOAT(0.0f); + fVslider4 = FAUSTFLOAT(315.0f); + fVslider5 = FAUSTFLOAT(0.0f); + fVslider6 = FAUSTFLOAT(2.0f); + fVslider7 = FAUSTFLOAT(6000.0f); + fVslider8 = FAUSTFLOAT(3.0f); + fVslider9 = FAUSTFLOAT(200.0f); + fVslider10 = FAUSTFLOAT(60.0f); + } + + virtual void instanceClear() { + IOTA = 0; + for (int l0 = 0; (l0 < 16384); l0 = (l0 + 1)) { + fVec0[l0] = 0.0f; + } + for (int l1 = 0; (l1 < 16384); l1 = (l1 + 1)) { + fVec1[l1] = 0.0f; + } + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec0[l2] = 0.0f; + } + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fRec1[l3] = 0.0f; + } + for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { + fRec15[l4] = 0.0f; + } + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fRec14[l5] = 0.0f; + } + for (int l6 = 0; (l6 < 32768); l6 = (l6 + 1)) { + fVec2[l6] = 0.0f; + } + for (int l7 = 0; (l7 < 2048); l7 = (l7 + 1)) { + fVec3[l7] = 0.0f; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec12[l8] = 0.0f; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec19[l9] = 0.0f; + } + for (int l10 = 0; (l10 < 2); l10 = (l10 + 1)) { + fRec18[l10] = 0.0f; + } + for (int l11 = 0; (l11 < 32768); l11 = (l11 + 1)) { + fVec4[l11] = 0.0f; + } + for (int l12 = 0; (l12 < 4096); l12 = (l12 + 1)) { + fVec5[l12] = 0.0f; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec16[l13] = 0.0f; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fRec23[l14] = 0.0f; + } + for (int l15 = 0; (l15 < 2); l15 = (l15 + 1)) { + fRec22[l15] = 0.0f; + } + for (int l16 = 0; (l16 < 16384); l16 = (l16 + 1)) { + fVec6[l16] = 0.0f; + } + for (int l17 = 0; (l17 < 4096); l17 = (l17 + 1)) { + fVec7[l17] = 0.0f; + } + for (int l18 = 0; (l18 < 2); l18 = (l18 + 1)) { + fRec20[l18] = 0.0f; + } + for (int l19 = 0; (l19 < 2); l19 = (l19 + 1)) { + fRec27[l19] = 0.0f; + } + for (int l20 = 0; (l20 < 2); l20 = (l20 + 1)) { + fRec26[l20] = 0.0f; + } + for (int l21 = 0; (l21 < 32768); l21 = (l21 + 1)) { + fVec8[l21] = 0.0f; + } + for (int l22 = 0; (l22 < 4096); l22 = (l22 + 1)) { + fVec9[l22] = 0.0f; + } + for (int l23 = 0; (l23 < 2); l23 = (l23 + 1)) { + fRec24[l23] = 0.0f; + } + for (int l24 = 0; (l24 < 2); l24 = (l24 + 1)) { + fRec31[l24] = 0.0f; + } + for (int l25 = 0; (l25 < 2); l25 = (l25 + 1)) { + fRec30[l25] = 0.0f; + } + for (int l26 = 0; (l26 < 16384); l26 = (l26 + 1)) { + fVec10[l26] = 0.0f; + } + for (int l27 = 0; (l27 < 2048); l27 = (l27 + 1)) { + fVec11[l27] = 0.0f; + } + for (int l28 = 0; (l28 < 2); l28 = (l28 + 1)) { + fRec28[l28] = 0.0f; + } + for (int l29 = 0; (l29 < 2); l29 = (l29 + 1)) { + fRec35[l29] = 0.0f; + } + for (int l30 = 0; (l30 < 2); l30 = (l30 + 1)) { + fRec34[l30] = 0.0f; + } + for (int l31 = 0; (l31 < 16384); l31 = (l31 + 1)) { + fVec12[l31] = 0.0f; + } + for (int l32 = 0; (l32 < 4096); l32 = (l32 + 1)) { + fVec13[l32] = 0.0f; + } + for (int l33 = 0; (l33 < 2); l33 = (l33 + 1)) { + fRec32[l33] = 0.0f; + } + for (int l34 = 0; (l34 < 2); l34 = (l34 + 1)) { + fRec39[l34] = 0.0f; + } + for (int l35 = 0; (l35 < 2); l35 = (l35 + 1)) { + fRec38[l35] = 0.0f; + } + for (int l36 = 0; (l36 < 16384); l36 = (l36 + 1)) { + fVec14[l36] = 0.0f; + } + for (int l37 = 0; (l37 < 4096); l37 = (l37 + 1)) { + fVec15[l37] = 0.0f; + } + for (int l38 = 0; (l38 < 2); l38 = (l38 + 1)) { + fRec36[l38] = 0.0f; + } + for (int l39 = 0; (l39 < 2); l39 = (l39 + 1)) { + fRec43[l39] = 0.0f; + } + for (int l40 = 0; (l40 < 2); l40 = (l40 + 1)) { + fRec42[l40] = 0.0f; + } + for (int l41 = 0; (l41 < 16384); l41 = (l41 + 1)) { + fVec16[l41] = 0.0f; + } + for (int l42 = 0; (l42 < 2048); l42 = (l42 + 1)) { + fVec17[l42] = 0.0f; + } + for (int l43 = 0; (l43 < 2); l43 = (l43 + 1)) { + fRec40[l43] = 0.0f; + } + for (int l44 = 0; (l44 < 3); l44 = (l44 + 1)) { + fRec4[l44] = 0.0f; + } + for (int l45 = 0; (l45 < 3); l45 = (l45 + 1)) { + fRec5[l45] = 0.0f; + } + for (int l46 = 0; (l46 < 3); l46 = (l46 + 1)) { + fRec6[l46] = 0.0f; + } + for (int l47 = 0; (l47 < 3); l47 = (l47 + 1)) { + fRec7[l47] = 0.0f; + } + for (int l48 = 0; (l48 < 3); l48 = (l48 + 1)) { + fRec8[l48] = 0.0f; + } + for (int l49 = 0; (l49 < 3); l49 = (l49 + 1)) { + fRec9[l49] = 0.0f; + } + for (int l50 = 0; (l50 < 3); l50 = (l50 + 1)) { + fRec10[l50] = 0.0f; + } + for (int l51 = 0; (l51 < 3); l51 = (l51 + 1)) { + fRec11[l51] = 0.0f; + } + for (int l52 = 0; (l52 < 3); l52 = (l52 + 1)) { + fRec3[l52] = 0.0f; + } + for (int l53 = 0; (l53 < 3); l53 = (l53 + 1)) { + fRec2[l53] = 0.0f; + } + for (int l54 = 0; (l54 < 3); l54 = (l54 + 1)) { + fRec45[l54] = 0.0f; + } + for (int l55 = 0; (l55 < 3); l55 = (l55 + 1)) { + fRec44[l55] = 0.0f; + } + } + + virtual void init(int sample_rate) { + classInit(sample_rate); + instanceInit(sample_rate); + } + virtual void instanceInit(int sample_rate) { + instanceConstants(sample_rate); + instanceResetUserInterface(); + instanceClear(); + } + + virtual zitarevdsp* clone() { + return new zitarevdsp(); + } + + virtual int getSampleRate() { + return fSampleRate; + } + + virtual void buildUserInterface(UI* ui_interface) { + ui_interface->declare(0, "0", ""); + ui_interface->declare(0, "tooltip", "~ ZITA REV1 FEEDBACK DELAY NETWORK (FDN) & SCHROEDER ALLPASS-COMB REVERBERATOR (8x8). See Faust's reverbs.lib for documentation and references"); + ui_interface->openHorizontalBox("Zita_Rev1"); + ui_interface->declare(0, "1", ""); + ui_interface->openHorizontalBox("Input"); + ui_interface->declare(&fVslider10, "1", ""); + ui_interface->declare(&fVslider10, "style", "knob"); + ui_interface->declare(&fVslider10, "tooltip", "Delay in ms before reverberation begins"); + ui_interface->declare(&fVslider10, "unit", "ms"); + ui_interface->addVerticalSlider("In Delay", &fVslider10, 60.0f, 20.0f, 100.0f, 1.0f); + ui_interface->closeBox(); + ui_interface->declare(0, "2", ""); + ui_interface->openHorizontalBox("Decay Times in Bands (see tooltips)"); + ui_interface->declare(&fVslider9, "1", ""); + ui_interface->declare(&fVslider9, "scale", "log"); + ui_interface->declare(&fVslider9, "style", "knob"); + ui_interface->declare(&fVslider9, "tooltip", "Crossover frequency (Hz) separating low and middle frequencies"); + ui_interface->declare(&fVslider9, "unit", "Hz"); + ui_interface->addVerticalSlider("LF X", &fVslider9, 200.0f, 50.0f, 1000.0f, 1.0f); + ui_interface->declare(&fVslider8, "2", ""); + ui_interface->declare(&fVslider8, "scale", "log"); + ui_interface->declare(&fVslider8, "style", "knob"); + ui_interface->declare(&fVslider8, "tooltip", "T60 = time (in seconds) to decay 60dB in low-frequency band"); + ui_interface->declare(&fVslider8, "unit", "s"); + ui_interface->addVerticalSlider("Low RT60", &fVslider8, 3.0f, 1.0f, 8.0f, 0.100000001f); + ui_interface->declare(&fVslider6, "3", ""); + ui_interface->declare(&fVslider6, "scale", "log"); + ui_interface->declare(&fVslider6, "style", "knob"); + ui_interface->declare(&fVslider6, "tooltip", "T60 = time (in seconds) to decay 60dB in middle band"); + ui_interface->declare(&fVslider6, "unit", "s"); + ui_interface->addVerticalSlider("Mid RT60", &fVslider6, 2.0f, 1.0f, 8.0f, 0.100000001f); + ui_interface->declare(&fVslider7, "4", ""); + ui_interface->declare(&fVslider7, "scale", "log"); + ui_interface->declare(&fVslider7, "style", "knob"); + ui_interface->declare(&fVslider7, "tooltip", "Frequency (Hz) at which the high-frequency T60 is half the middle-band's T60"); + ui_interface->declare(&fVslider7, "unit", "Hz"); + ui_interface->addVerticalSlider("HF Damping", &fVslider7, 6000.0f, 1500.0f, 23520.0f, 1.0f); + ui_interface->closeBox(); + ui_interface->declare(0, "3", ""); + ui_interface->openHorizontalBox("RM Peaking Equalizer 1"); + ui_interface->declare(&fVslider4, "1", ""); + ui_interface->declare(&fVslider4, "scale", "log"); + ui_interface->declare(&fVslider4, "style", "knob"); + ui_interface->declare(&fVslider4, "tooltip", "Center-frequency of second-order Regalia-Mitra peaking equalizer section 1"); + ui_interface->declare(&fVslider4, "unit", "Hz"); + ui_interface->addVerticalSlider("Eq1 Freq", &fVslider4, 315.0f, 40.0f, 2500.0f, 1.0f); + ui_interface->declare(&fVslider5, "2", ""); + ui_interface->declare(&fVslider5, "style", "knob"); + ui_interface->declare(&fVslider5, "tooltip", "Peak level in dB of second-order Regalia-Mitra peaking equalizer section 1"); + ui_interface->declare(&fVslider5, "unit", "dB"); + ui_interface->addVerticalSlider("Eq1 Level", &fVslider5, 0.0f, -15.0f, 15.0f, 0.100000001f); + ui_interface->closeBox(); + ui_interface->declare(0, "4", ""); + ui_interface->openHorizontalBox("RM Peaking Equalizer 2"); + ui_interface->declare(&fVslider2, "1", ""); + ui_interface->declare(&fVslider2, "scale", "log"); + ui_interface->declare(&fVslider2, "style", "knob"); + ui_interface->declare(&fVslider2, "tooltip", "Center-frequency of second-order Regalia-Mitra peaking equalizer section 2"); + ui_interface->declare(&fVslider2, "unit", "Hz"); + ui_interface->addVerticalSlider("Eq2 Freq", &fVslider2, 1500.0f, 160.0f, 10000.0f, 1.0f); + ui_interface->declare(&fVslider3, "2", ""); + ui_interface->declare(&fVslider3, "style", "knob"); + ui_interface->declare(&fVslider3, "tooltip", "Peak level in dB of second-order Regalia-Mitra peaking equalizer section 2"); + ui_interface->declare(&fVslider3, "unit", "dB"); + ui_interface->addVerticalSlider("Eq2 Level", &fVslider3, 0.0f, -15.0f, 15.0f, 0.100000001f); + ui_interface->closeBox(); + ui_interface->declare(0, "5", ""); + ui_interface->openHorizontalBox("Output"); + ui_interface->declare(&fVslider1, "1", ""); + ui_interface->declare(&fVslider1, "style", "knob"); + ui_interface->declare(&fVslider1, "tooltip", "Dry/Wet Mix: 0 = dry, 1 = wet"); + ui_interface->addVerticalSlider("Wet", &fVslider1, 0.0f, 0.0f, 1.0f, 0.00999999978f); + ui_interface->declare(&fVslider0, "2", ""); + ui_interface->declare(&fVslider0, "style", "knob"); + ui_interface->declare(&fVslider0, "tooltip", "Output scale factor"); + ui_interface->declare(&fVslider0, "unit", "dB"); + ui_interface->addVerticalSlider("Level", &fVslider0, -3.0f, -70.0f, 20.0f, 0.100000001f); + ui_interface->closeBox(); + ui_interface->closeBox(); + } + + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { + FAUSTFLOAT* input0 = inputs[0]; + FAUSTFLOAT* input1 = inputs[1]; + FAUSTFLOAT* output0 = outputs[0]; + FAUSTFLOAT* output1 = outputs[1]; + float fSlow0 = (0.00100000005f * std::pow(10.0f, (0.0500000007f * float(fVslider0)))); + float fSlow1 = (0.00100000005f * float(fVslider1)); + float fSlow2 = float(fVslider2); + float fSlow3 = std::pow(10.0f, (0.0500000007f * float(fVslider3))); + float fSlow4 = (fConst1 * (fSlow2 / std::sqrt(std::max<float>(0.0f, fSlow3)))); + float fSlow5 = ((1.0f - fSlow4) / (fSlow4 + 1.0f)); + float fSlow6 = float(fVslider4); + float fSlow7 = std::pow(10.0f, (0.0500000007f * float(fVslider5))); + float fSlow8 = (fConst1 * (fSlow6 / std::sqrt(std::max<float>(0.0f, fSlow7)))); + float fSlow9 = ((1.0f - fSlow8) / (fSlow8 + 1.0f)); + float fSlow10 = float(fVslider6); + float fSlow11 = std::exp((fConst3 / fSlow10)); + float fSlow12 = zitarevdsp_faustpower2_f(fSlow11); + float fSlow13 = std::cos((fConst1 * float(fVslider7))); + float fSlow14 = (1.0f - (fSlow12 * fSlow13)); + float fSlow15 = (1.0f - fSlow12); + float fSlow16 = (fSlow14 / fSlow15); + float fSlow17 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow14) / zitarevdsp_faustpower2_f(fSlow15)) + -1.0f))); + float fSlow18 = (fSlow16 - fSlow17); + float fSlow19 = (fSlow11 * (fSlow17 + (1.0f - fSlow16))); + float fSlow20 = float(fVslider8); + float fSlow21 = ((std::exp((fConst3 / fSlow20)) / fSlow11) + -1.0f); + float fSlow22 = (1.0f / std::tan((fConst4 * float(fVslider9)))); + float fSlow23 = (1.0f / (fSlow22 + 1.0f)); + float fSlow24 = (1.0f - fSlow22); + int iSlow25 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst7 * float(fVslider10))))); + float fSlow26 = std::exp((fConst10 / fSlow10)); + float fSlow27 = zitarevdsp_faustpower2_f(fSlow26); + float fSlow28 = (1.0f - (fSlow27 * fSlow13)); + float fSlow29 = (1.0f - fSlow27); + float fSlow30 = (fSlow28 / fSlow29); + float fSlow31 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow28) / zitarevdsp_faustpower2_f(fSlow29)) + -1.0f))); + float fSlow32 = (fSlow30 - fSlow31); + float fSlow33 = (fSlow26 * (fSlow31 + (1.0f - fSlow30))); + float fSlow34 = ((std::exp((fConst10 / fSlow20)) / fSlow26) + -1.0f); + float fSlow35 = std::exp((fConst15 / fSlow10)); + float fSlow36 = zitarevdsp_faustpower2_f(fSlow35); + float fSlow37 = (1.0f - (fSlow36 * fSlow13)); + float fSlow38 = (1.0f - fSlow36); + float fSlow39 = (fSlow37 / fSlow38); + float fSlow40 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow37) / zitarevdsp_faustpower2_f(fSlow38)) + -1.0f))); + float fSlow41 = (fSlow39 - fSlow40); + float fSlow42 = (fSlow35 * (fSlow40 + (1.0f - fSlow39))); + float fSlow43 = ((std::exp((fConst15 / fSlow20)) / fSlow35) + -1.0f); + float fSlow44 = std::exp((fConst20 / fSlow10)); + float fSlow45 = zitarevdsp_faustpower2_f(fSlow44); + float fSlow46 = (1.0f - (fSlow45 * fSlow13)); + float fSlow47 = (1.0f - fSlow45); + float fSlow48 = (fSlow46 / fSlow47); + float fSlow49 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow46) / zitarevdsp_faustpower2_f(fSlow47)) + -1.0f))); + float fSlow50 = (fSlow48 - fSlow49); + float fSlow51 = (fSlow44 * (fSlow49 + (1.0f - fSlow48))); + float fSlow52 = ((std::exp((fConst20 / fSlow20)) / fSlow44) + -1.0f); + float fSlow53 = std::exp((fConst25 / fSlow10)); + float fSlow54 = zitarevdsp_faustpower2_f(fSlow53); + float fSlow55 = (1.0f - (fSlow54 * fSlow13)); + float fSlow56 = (1.0f - fSlow54); + float fSlow57 = (fSlow55 / fSlow56); + float fSlow58 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow55) / zitarevdsp_faustpower2_f(fSlow56)) + -1.0f))); + float fSlow59 = (fSlow57 - fSlow58); + float fSlow60 = (fSlow53 * (fSlow58 + (1.0f - fSlow57))); + float fSlow61 = ((std::exp((fConst25 / fSlow20)) / fSlow53) + -1.0f); + float fSlow62 = std::exp((fConst30 / fSlow10)); + float fSlow63 = zitarevdsp_faustpower2_f(fSlow62); + float fSlow64 = (1.0f - (fSlow63 * fSlow13)); + float fSlow65 = (1.0f - fSlow63); + float fSlow66 = (fSlow64 / fSlow65); + float fSlow67 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow64) / zitarevdsp_faustpower2_f(fSlow65)) + -1.0f))); + float fSlow68 = (fSlow66 - fSlow67); + float fSlow69 = (fSlow62 * (fSlow67 + (1.0f - fSlow66))); + float fSlow70 = ((std::exp((fConst30 / fSlow20)) / fSlow62) + -1.0f); + float fSlow71 = std::exp((fConst35 / fSlow10)); + float fSlow72 = zitarevdsp_faustpower2_f(fSlow71); + float fSlow73 = (1.0f - (fSlow72 * fSlow13)); + float fSlow74 = (1.0f - fSlow72); + float fSlow75 = (fSlow73 / fSlow74); + float fSlow76 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow73) / zitarevdsp_faustpower2_f(fSlow74)) + -1.0f))); + float fSlow77 = (fSlow75 - fSlow76); + float fSlow78 = (fSlow71 * (fSlow76 + (1.0f - fSlow75))); + float fSlow79 = ((std::exp((fConst35 / fSlow20)) / fSlow71) + -1.0f); + float fSlow80 = std::exp((fConst40 / fSlow10)); + float fSlow81 = zitarevdsp_faustpower2_f(fSlow80); + float fSlow82 = (1.0f - (fSlow81 * fSlow13)); + float fSlow83 = (1.0f - fSlow81); + float fSlow84 = (fSlow82 / fSlow83); + float fSlow85 = std::sqrt(std::max<float>(0.0f, ((zitarevdsp_faustpower2_f(fSlow82) / zitarevdsp_faustpower2_f(fSlow83)) + -1.0f))); + float fSlow86 = (fSlow84 - fSlow85); + float fSlow87 = (fSlow80 * (fSlow85 + (1.0f - fSlow84))); + float fSlow88 = ((std::exp((fConst40 / fSlow20)) / fSlow80) + -1.0f); + float fSlow89 = (0.0f - (std::cos((fConst1 * fSlow6)) * (fSlow9 + 1.0f))); + float fSlow90 = (0.0f - (std::cos((fConst1 * fSlow2)) * (fSlow5 + 1.0f))); + for (int i = 0; (i < count); i = (i + 1)) { + float fTemp0 = float(input0[i]); + fVec0[(IOTA & 16383)] = fTemp0; + float fTemp1 = float(input1[i]); + fVec1[(IOTA & 16383)] = fTemp1; + fRec0[0] = (fSlow0 + (0.999000013f * fRec0[1])); + fRec1[0] = (fSlow1 + (0.999000013f * fRec1[1])); + fRec15[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec15[1]) - (fRec11[1] + fRec11[2])))); + fRec14[0] = ((fSlow18 * fRec14[1]) + (fSlow19 * (fRec11[1] + (fSlow21 * fRec15[0])))); + fVec2[(IOTA & 32767)] = ((0.353553385f * fRec14[0]) + 9.99999968e-21f); + float fTemp2 = (0.300000012f * fVec1[((IOTA - iSlow25) & 16383)]); + float fTemp3 = (((0.600000024f * fRec12[1]) + fVec2[((IOTA - iConst6) & 32767)]) - fTemp2); + fVec3[(IOTA & 2047)] = fTemp3; + fRec12[0] = fVec3[((IOTA - iConst8) & 2047)]; + float fRec13 = (0.0f - (0.600000024f * fTemp3)); + fRec19[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec19[1]) - (fRec7[1] + fRec7[2])))); + fRec18[0] = ((fSlow32 * fRec18[1]) + (fSlow33 * (fRec7[1] + (fSlow34 * fRec19[0])))); + fVec4[(IOTA & 32767)] = ((0.353553385f * fRec18[0]) + 9.99999968e-21f); + float fTemp4 = (((0.600000024f * fRec16[1]) + fVec4[((IOTA - iConst12) & 32767)]) - fTemp2); + fVec5[(IOTA & 4095)] = fTemp4; + fRec16[0] = fVec5[((IOTA - iConst13) & 4095)]; + float fRec17 = (0.0f - (0.600000024f * fTemp4)); + fRec23[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec23[1]) - (fRec9[1] + fRec9[2])))); + fRec22[0] = ((fSlow41 * fRec22[1]) + (fSlow42 * (fRec9[1] + (fSlow43 * fRec23[0])))); + fVec6[(IOTA & 16383)] = ((0.353553385f * fRec22[0]) + 9.99999968e-21f); + float fTemp5 = (fVec6[((IOTA - iConst17) & 16383)] + (fTemp2 + (0.600000024f * fRec20[1]))); + fVec7[(IOTA & 4095)] = fTemp5; + fRec20[0] = fVec7[((IOTA - iConst18) & 4095)]; + float fRec21 = (0.0f - (0.600000024f * fTemp5)); + fRec27[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec27[1]) - (fRec5[1] + fRec5[2])))); + fRec26[0] = ((fSlow50 * fRec26[1]) + (fSlow51 * (fRec5[1] + (fSlow52 * fRec27[0])))); + fVec8[(IOTA & 32767)] = ((0.353553385f * fRec26[0]) + 9.99999968e-21f); + float fTemp6 = (fTemp2 + ((0.600000024f * fRec24[1]) + fVec8[((IOTA - iConst22) & 32767)])); + fVec9[(IOTA & 4095)] = fTemp6; + fRec24[0] = fVec9[((IOTA - iConst23) & 4095)]; + float fRec25 = (0.0f - (0.600000024f * fTemp6)); + fRec31[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec31[1]) - (fRec10[1] + fRec10[2])))); + fRec30[0] = ((fSlow59 * fRec30[1]) + (fSlow60 * (fRec10[1] + (fSlow61 * fRec31[0])))); + fVec10[(IOTA & 16383)] = ((0.353553385f * fRec30[0]) + 9.99999968e-21f); + float fTemp7 = (0.300000012f * fVec0[((IOTA - iSlow25) & 16383)]); + float fTemp8 = (fVec10[((IOTA - iConst27) & 16383)] - (fTemp7 + (0.600000024f * fRec28[1]))); + fVec11[(IOTA & 2047)] = fTemp8; + fRec28[0] = fVec11[((IOTA - iConst28) & 2047)]; + float fRec29 = (0.600000024f * fTemp8); + fRec35[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec35[1]) - (fRec6[1] + fRec6[2])))); + fRec34[0] = ((fSlow68 * fRec34[1]) + (fSlow69 * (fRec6[1] + (fSlow70 * fRec35[0])))); + fVec12[(IOTA & 16383)] = ((0.353553385f * fRec34[0]) + 9.99999968e-21f); + float fTemp9 = (fVec12[((IOTA - iConst32) & 16383)] - (fTemp7 + (0.600000024f * fRec32[1]))); + fVec13[(IOTA & 4095)] = fTemp9; + fRec32[0] = fVec13[((IOTA - iConst33) & 4095)]; + float fRec33 = (0.600000024f * fTemp9); + fRec39[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec39[1]) - (fRec8[1] + fRec8[2])))); + fRec38[0] = ((fSlow77 * fRec38[1]) + (fSlow78 * (fRec8[1] + (fSlow79 * fRec39[0])))); + fVec14[(IOTA & 16383)] = ((0.353553385f * fRec38[0]) + 9.99999968e-21f); + float fTemp10 = ((fTemp7 + fVec14[((IOTA - iConst37) & 16383)]) - (0.600000024f * fRec36[1])); + fVec15[(IOTA & 4095)] = fTemp10; + fRec36[0] = fVec15[((IOTA - iConst38) & 4095)]; + float fRec37 = (0.600000024f * fTemp10); + fRec43[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec43[1]) - (fRec4[1] + fRec4[2])))); + fRec42[0] = ((fSlow86 * fRec42[1]) + (fSlow87 * (fRec4[1] + (fSlow88 * fRec43[0])))); + fVec16[(IOTA & 16383)] = ((0.353553385f * fRec42[0]) + 9.99999968e-21f); + float fTemp11 = ((fVec16[((IOTA - iConst42) & 16383)] + fTemp7) - (0.600000024f * fRec40[1])); + fVec17[(IOTA & 2047)] = fTemp11; + fRec40[0] = fVec17[((IOTA - iConst43) & 2047)]; + float fRec41 = (0.600000024f * fTemp11); + float fTemp12 = (fRec41 + fRec37); + float fTemp13 = (fRec29 + (fRec33 + fTemp12)); + fRec4[0] = (fRec12[1] + (fRec16[1] + (fRec20[1] + (fRec24[1] + (fRec28[1] + (fRec32[1] + (fRec36[1] + (fRec40[1] + (fRec13 + (fRec17 + (fRec21 + (fRec25 + fTemp13)))))))))))); + fRec5[0] = ((fRec28[1] + (fRec32[1] + (fRec36[1] + (fRec40[1] + fTemp13)))) - (fRec12[1] + (fRec16[1] + (fRec20[1] + (fRec24[1] + (fRec13 + (fRec17 + (fRec25 + fRec21)))))))); + float fTemp14 = (fRec33 + fRec29); + fRec6[0] = ((fRec20[1] + (fRec24[1] + (fRec36[1] + (fRec40[1] + (fRec21 + (fRec25 + fTemp12)))))) - (fRec12[1] + (fRec16[1] + (fRec28[1] + (fRec32[1] + (fRec13 + (fRec17 + fTemp14))))))); + fRec7[0] = ((fRec12[1] + (fRec16[1] + (fRec36[1] + (fRec40[1] + (fRec13 + (fRec17 + fTemp12)))))) - (fRec20[1] + (fRec24[1] + (fRec28[1] + (fRec32[1] + (fRec21 + (fRec25 + fTemp14))))))); + float fTemp15 = (fRec41 + fRec33); + float fTemp16 = (fRec37 + fRec29); + fRec8[0] = ((fRec16[1] + (fRec24[1] + (fRec32[1] + (fRec40[1] + (fRec17 + (fRec25 + fTemp15)))))) - (fRec12[1] + (fRec20[1] + (fRec28[1] + (fRec36[1] + (fRec13 + (fRec21 + fTemp16))))))); + fRec9[0] = ((fRec12[1] + (fRec20[1] + (fRec32[1] + (fRec40[1] + (fRec13 + (fRec21 + fTemp15)))))) - (fRec16[1] + (fRec24[1] + (fRec28[1] + (fRec36[1] + (fRec17 + (fRec25 + fTemp16))))))); + float fTemp17 = (fRec41 + fRec29); + float fTemp18 = (fRec37 + fRec33); + fRec10[0] = ((fRec12[1] + (fRec24[1] + (fRec28[1] + (fRec40[1] + (fRec13 + (fRec25 + fTemp17)))))) - (fRec16[1] + (fRec20[1] + (fRec32[1] + (fRec36[1] + (fRec17 + (fRec21 + fTemp18))))))); + fRec11[0] = ((fRec16[1] + (fRec20[1] + (fRec28[1] + (fRec40[1] + (fRec17 + (fRec21 + fTemp17)))))) - (fRec12[1] + (fRec24[1] + (fRec32[1] + (fRec36[1] + (fRec13 + (fRec25 + fTemp18))))))); + float fTemp19 = (0.370000005f * (fRec5[0] + fRec6[0])); + float fTemp20 = (fSlow89 * fRec3[1]); + fRec3[0] = (fTemp19 - (fTemp20 + (fSlow9 * fRec3[2]))); + float fTemp21 = (fSlow9 * fRec3[0]); + float fTemp22 = (0.5f * ((fTemp21 + (fRec3[2] + (fTemp19 + fTemp20))) + (fSlow7 * ((fTemp21 + (fTemp20 + fRec3[2])) - fTemp19)))); + float fTemp23 = (fSlow90 * fRec2[1]); + fRec2[0] = (fTemp22 - (fTemp23 + (fSlow5 * fRec2[2]))); + float fTemp24 = (fSlow5 * fRec2[0]); + float fTemp25 = (1.0f - fRec1[0]); + output0[i] = FAUSTFLOAT((fRec0[0] * ((0.5f * (fRec1[0] * ((fTemp24 + (fRec2[2] + (fTemp22 + fTemp23))) + (fSlow3 * ((fTemp24 + (fTemp23 + fRec2[2])) - fTemp22))))) + (fTemp0 * fTemp25)))); + float fTemp26 = (0.370000005f * (fRec5[0] - fRec6[0])); + float fTemp27 = (fSlow89 * fRec45[1]); + fRec45[0] = (fTemp26 - (fTemp27 + (fSlow9 * fRec45[2]))); + float fTemp28 = (fSlow9 * fRec45[0]); + float fTemp29 = (0.5f * ((fTemp28 + (fRec45[2] + (fTemp26 + fTemp27))) + (fSlow7 * ((fTemp28 + (fTemp27 + fRec45[2])) - fTemp26)))); + float fTemp30 = (fSlow90 * fRec44[1]); + fRec44[0] = (fTemp29 - (fTemp30 + (fSlow5 * fRec44[2]))); + float fTemp31 = (fSlow5 * fRec44[0]); + output1[i] = FAUSTFLOAT((fRec0[0] * ((0.5f * (fRec1[0] * ((fTemp31 + (fRec44[2] + (fTemp29 + fTemp30))) + (fSlow3 * ((fTemp31 + (fTemp30 + fRec44[2])) - fTemp29))))) + (fTemp1 * fTemp25)))); + IOTA = (IOTA + 1); + fRec0[1] = fRec0[0]; + fRec1[1] = fRec1[0]; + fRec15[1] = fRec15[0]; + fRec14[1] = fRec14[0]; + fRec12[1] = fRec12[0]; + fRec19[1] = fRec19[0]; + fRec18[1] = fRec18[0]; + fRec16[1] = fRec16[0]; + fRec23[1] = fRec23[0]; + fRec22[1] = fRec22[0]; + fRec20[1] = fRec20[0]; + fRec27[1] = fRec27[0]; + fRec26[1] = fRec26[0]; + fRec24[1] = fRec24[0]; + fRec31[1] = fRec31[0]; + fRec30[1] = fRec30[0]; + fRec28[1] = fRec28[0]; + fRec35[1] = fRec35[0]; + fRec34[1] = fRec34[0]; + fRec32[1] = fRec32[0]; + fRec39[1] = fRec39[0]; + fRec38[1] = fRec38[0]; + fRec36[1] = fRec36[0]; + fRec43[1] = fRec43[0]; + fRec42[1] = fRec42[0]; + fRec40[1] = fRec40[0]; + fRec4[2] = fRec4[1]; + fRec4[1] = fRec4[0]; + fRec5[2] = fRec5[1]; + fRec5[1] = fRec5[0]; + fRec6[2] = fRec6[1]; + fRec6[1] = fRec6[0]; + fRec7[2] = fRec7[1]; + fRec7[1] = fRec7[0]; + fRec8[2] = fRec8[1]; + fRec8[1] = fRec8[0]; + fRec9[2] = fRec9[1]; + fRec9[1] = fRec9[0]; + fRec10[2] = fRec10[1]; + fRec10[1] = fRec10[0]; + fRec11[2] = fRec11[1]; + fRec11[1] = fRec11[0]; + fRec3[2] = fRec3[1]; + fRec3[1] = fRec3[0]; + fRec2[2] = fRec2[1]; + fRec2[1] = fRec2[0]; + fRec45[2] = fRec45[1]; + fRec45[1] = fRec45[0]; + fRec44[2] = fRec44[1]; + fRec44[1] = fRec44[0]; + } + } + +}; + +#endif diff --git a/src/zitarevmonodsp.h b/src/zitarevmonodsp.h new file mode 100644 index 0000000..190453a --- /dev/null +++ b/src/zitarevmonodsp.h @@ -0,0 +1,2357 @@ +/* ------------------------------------------------------------ +name: "zitarevmonodsp" +Code generated with Faust 2.28.6 (https://faust.grame.fr) +Compilation options: -lang cpp -inpl -scal -ftz 0 +------------------------------------------------------------ */ + +#ifndef __zitarevmonodsp_H__ +#define __zitarevmonodsp_H__ + +// NOTE: ANY INCLUDE-GUARD HERE MUST BE DERIVED FROM THE CLASS NAME +// +// faust2header.cpp - FAUST Architecture File +// This is a simple variation of matlabplot.cpp in the Faust distribution +// aimed at creating a simple C++ header file (.h) containing a Faust DSP. +// See the Makefile for how to use it. + +/************************** BEGIN dsp.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __dsp__ +#define __dsp__ + +#include <string> +#include <vector> + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +struct UI; +struct Meta; + +/** + * DSP memory manager. + */ + +struct dsp_memory_manager { + + virtual ~dsp_memory_manager() {} + + virtual void* allocate(size_t size) = 0; + virtual void destroy(void* ptr) = 0; + +}; + +/** +* Signal processor definition. +*/ + +class dsp { + + public: + + dsp() {} + virtual ~dsp() {} + + /* Return instance number of audio inputs */ + virtual int getNumInputs() = 0; + + /* Return instance number of audio outputs */ + virtual int getNumOutputs() = 0; + + /** + * Trigger the ui_interface parameter with instance specific calls + * to 'openTabBox', 'addButton', 'addVerticalSlider'... in order to build the UI. + * + * @param ui_interface - the user interface builder + */ + virtual void buildUserInterface(UI* ui_interface) = 0; + + /* Returns the sample rate currently used by the instance */ + virtual int getSampleRate() = 0; + + /** + * Global init, calls the following methods: + * - static class 'classInit': static tables initialization + * - 'instanceInit': constants and instance state initialization + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void init(int sample_rate) = 0; + + /** + * Init instance state + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void instanceInit(int sample_rate) = 0; + + /** + * Init instance constant state + * + * @param sample_rate - the sampling rate in Hertz + */ + virtual void instanceConstants(int sample_rate) = 0; + + /* Init default control parameters values */ + virtual void instanceResetUserInterface() = 0; + + /* Init instance state (delay lines...) */ + virtual void instanceClear() = 0; + + /** + * Return a clone of the instance. + * + * @return a copy of the instance on success, otherwise a null pointer. + */ + virtual dsp* clone() = 0; + + /** + * Trigger the Meta* parameter with instance specific calls to 'declare' (key, value) metadata. + * + * @param m - the Meta* meta user + */ + virtual void metadata(Meta* m) = 0; + + /** + * DSP instance computation, to be called with successive in/out audio buffers. + * + * @param count - the number of frames to compute + * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) + * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (eiher float, double or quad) + * + */ + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) = 0; + + /** + * DSP instance computation: alternative method to be used by subclasses. + * + * @param date_usec - the timestamp in microsec given by audio driver. + * @param count - the number of frames to compute + * @param inputs - the input audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) + * @param outputs - the output audio buffers as an array of non-interleaved FAUSTFLOAT samples (either float, double or quad) + * + */ + virtual void compute(double /*date_usec*/, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { compute(count, inputs, outputs); } + +}; + +/** + * Generic DSP decorator. + */ + +class decorator_dsp : public dsp { + + protected: + + dsp* fDSP; + + public: + + decorator_dsp(dsp* dsp = nullptr):fDSP(dsp) {} + virtual ~decorator_dsp() { delete fDSP; } + + virtual int getNumInputs() { return fDSP->getNumInputs(); } + virtual int getNumOutputs() { return fDSP->getNumOutputs(); } + virtual void buildUserInterface(UI* ui_interface) { fDSP->buildUserInterface(ui_interface); } + virtual int getSampleRate() { return fDSP->getSampleRate(); } + virtual void init(int sample_rate) { fDSP->init(sample_rate); } + virtual void instanceInit(int sample_rate) { fDSP->instanceInit(sample_rate); } + virtual void instanceConstants(int sample_rate) { fDSP->instanceConstants(sample_rate); } + virtual void instanceResetUserInterface() { fDSP->instanceResetUserInterface(); } + virtual void instanceClear() { fDSP->instanceClear(); } + virtual decorator_dsp* clone() { return new decorator_dsp(fDSP->clone()); } + virtual void metadata(Meta* m) { fDSP->metadata(m); } + // Beware: subclasses usually have to overload the two 'compute' methods + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(count, inputs, outputs); } + virtual void compute(double date_usec, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { fDSP->compute(date_usec, count, inputs, outputs); } + +}; + +/** + * DSP factory class. + */ + +class dsp_factory { + + protected: + + // So that to force sub-classes to use deleteDSPFactory(dsp_factory* factory); + virtual ~dsp_factory() {} + + public: + + virtual std::string getName() = 0; + virtual std::string getSHAKey() = 0; + virtual std::string getDSPCode() = 0; + virtual std::string getCompileOptions() = 0; + virtual std::vector<std::string> getLibraryList() = 0; + virtual std::vector<std::string> getIncludePathnames() = 0; + + virtual dsp* createDSPInstance() = 0; + + virtual void setMemoryManager(dsp_memory_manager* manager) = 0; + virtual dsp_memory_manager* getMemoryManager() = 0; + +}; + +/** + * On Intel set FZ (Flush to Zero) and DAZ (Denormals Are Zero) + * flags to avoid costly denormals. + */ + +#ifdef __SSE__ + #include <xmmintrin.h> + #ifdef __SSE2__ + #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8040) + #else + #define AVOIDDENORMALS _mm_setcsr(_mm_getcsr() | 0x8000) + #endif +#else + #define AVOIDDENORMALS +#endif + +#endif +/************************** END dsp.h **************************/ + +/************************** BEGIN APIUI.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef API_UI_H +#define API_UI_H + +#include <sstream> +#include <string> +#include <vector> +#include <iostream> +#include <map> + +/************************** BEGIN meta.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __meta__ +#define __meta__ + +struct Meta +{ + virtual ~Meta() {}; + virtual void declare(const char* key, const char* value) = 0; + +}; + +#endif +/************************** END meta.h **************************/ +/************************** BEGIN UI.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2020 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __UI_H__ +#define __UI_H__ + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +/******************************************************************************* + * UI : Faust DSP User Interface + * User Interface as expected by the buildUserInterface() method of a DSP. + * This abstract class contains only the method that the Faust compiler can + * generate to describe a DSP user interface. + ******************************************************************************/ + +struct Soundfile; + +template <typename REAL> +struct UIReal +{ + UIReal() {} + virtual ~UIReal() {} + + // -- widget's layouts + + virtual void openTabBox(const char* label) = 0; + virtual void openHorizontalBox(const char* label) = 0; + virtual void openVerticalBox(const char* label) = 0; + virtual void closeBox() = 0; + + // -- active widgets + + virtual void addButton(const char* label, REAL* zone) = 0; + virtual void addCheckButton(const char* label, REAL* zone) = 0; + virtual void addVerticalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + virtual void addHorizontalSlider(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + virtual void addNumEntry(const char* label, REAL* zone, REAL init, REAL min, REAL max, REAL step) = 0; + + // -- passive widgets + + virtual void addHorizontalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0; + virtual void addVerticalBargraph(const char* label, REAL* zone, REAL min, REAL max) = 0; + + // -- soundfiles + + virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) = 0; + + // -- metadata declarations + + virtual void declare(REAL* zone, const char* key, const char* val) {} +}; + +struct UI : public UIReal<FAUSTFLOAT> +{ + UI() {} + virtual ~UI() {} +}; + +#endif +/************************** END UI.h **************************/ +/************************** BEGIN PathBuilder.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef FAUST_PATHBUILDER_H +#define FAUST_PATHBUILDER_H + +#include <vector> +#include <string> +#include <algorithm> + +/******************************************************************************* + * PathBuilder : Faust User Interface + * Helper class to build complete hierarchical path for UI items. + ******************************************************************************/ + +class PathBuilder +{ + + protected: + + std::vector<std::string> fControlsLevel; + + public: + + PathBuilder() {} + virtual ~PathBuilder() {} + + std::string buildPath(const std::string& label) + { + std::string res = "/"; + for (size_t i = 0; i < fControlsLevel.size(); i++) { + res += fControlsLevel[i]; + res += "/"; + } + res += label; + std::replace(res.begin(), res.end(), ' ', '_'); + return res; + } + + std::string buildLabel(std::string label) + { + std::replace(label.begin(), label.end(), ' ', '_'); + return label; + } + + void pushLabel(const std::string& label) { fControlsLevel.push_back(label); } + void popLabel() { fControlsLevel.pop_back(); } + +}; + +#endif // FAUST_PATHBUILDER_H +/************************** END PathBuilder.h **************************/ +/************************** BEGIN ValueConverter.h **************************/ +/************************************************************************ + FAUST Architecture File + Copyright (C) 2003-2017 GRAME, Centre National de Creation Musicale + --------------------------------------------------------------------- + This Architecture section is free software; you can redistribute it + and/or modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + + EXCEPTION : As a special exception, you may create a larger work + that contains this FAUST architecture section and distribute + that work under terms of your choice, so long as this FAUST + architecture section is not modified. + ************************************************************************/ + +#ifndef __ValueConverter__ +#define __ValueConverter__ + +/*************************************************************************************** + ValueConverter.h + (GRAME, Copyright 2015-2019) + +Set of conversion objects used to map user interface values (for example a gui slider +delivering values between 0 and 1) to faust values (for example a vslider between +20 and 20000) using a log scale. + +-- Utilities + +Range(lo,hi) : clip a value x between lo and hi +Interpolator(lo,hi,v1,v2) : Maps a value x between lo and hi to a value y between v1 and v2 +Interpolator3pt(lo,mi,hi,v1,vm,v2) : Map values between lo mid hi to values between v1 vm v2 + +-- Value Converters + +ValueConverter::ui2faust(x) +ValueConverter::faust2ui(x) + +-- ValueConverters used for sliders depending of the scale + +LinearValueConverter(umin, umax, fmin, fmax) +LinearValueConverter2(lo, mi, hi, v1, vm, v2) using 2 segments +LogValueConverter(umin, umax, fmin, fmax) +ExpValueConverter(umin, umax, fmin, fmax) + +-- ValueConverters used for accelerometers based on 3 points + +AccUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 0 +AccDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 1 +AccUpDownConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 2 +AccDownUpConverter(amin, amid, amax, fmin, fmid, fmax) -- curve 3 + +-- lists of ZoneControl are used to implement accelerometers metadata for each axes + +ZoneControl(zone, valueConverter) : a zone with an accelerometer data converter + +-- ZoneReader are used to implement screencolor metadata + +ZoneReader(zone, valueConverter) : a zone with a data converter + +****************************************************************************************/ + +#include <float.h> +#include <algorithm> // std::max +#include <cmath> +#include <vector> +#include <assert.h> + +//-------------------------------------------------------------------------------------- +// Interpolator(lo,hi,v1,v2) +// Maps a value x between lo and hi to a value y between v1 and v2 +// y = v1 + (x-lo)/(hi-lo)*(v2-v1) +// y = v1 + (x-lo) * coef with coef = (v2-v1)/(hi-lo) +// y = v1 + x*coef - lo*coef +// y = v1 - lo*coef + x*coef +// y = offset + x*coef with offset = v1 - lo*coef +//-------------------------------------------------------------------------------------- +class Interpolator +{ + private: + + //-------------------------------------------------------------------------------------- + // Range(lo,hi) clip a value between lo and hi + //-------------------------------------------------------------------------------------- + struct Range + { + double fLo; + double fHi; + + Range(double x, double y) : fLo(std::min<double>(x,y)), fHi(std::max<double>(x,y)) {} + double operator()(double x) { return (x<fLo) ? fLo : (x>fHi) ? fHi : x; } + }; + + + Range fRange; + double fCoef; + double fOffset; + + public: + + Interpolator(double lo, double hi, double v1, double v2) : fRange(lo,hi) + { + if (hi != lo) { + // regular case + fCoef = (v2-v1)/(hi-lo); + fOffset = v1 - lo*fCoef; + } else { + // degenerate case, avoids division by zero + fCoef = 0; + fOffset = (v1+v2)/2; + } + } + double operator()(double v) + { + double x = fRange(v); + return fOffset + x*fCoef; + } + + void getLowHigh(double& amin, double& amax) + { + amin = fRange.fLo; + amax = fRange.fHi; + } +}; + +//-------------------------------------------------------------------------------------- +// Interpolator3pt(lo,mi,hi,v1,vm,v2) +// Map values between lo mid hi to values between v1 vm v2 +//-------------------------------------------------------------------------------------- +class Interpolator3pt +{ + + private: + + Interpolator fSegment1; + Interpolator fSegment2; + double fMid; + + public: + + Interpolator3pt(double lo, double mi, double hi, double v1, double vm, double v2) : + fSegment1(lo, mi, v1, vm), + fSegment2(mi, hi, vm, v2), + fMid(mi) {} + double operator()(double x) { return (x < fMid) ? fSegment1(x) : fSegment2(x); } + + void getMappingValues(double& amin, double& amid, double& amax) + { + fSegment1.getLowHigh(amin, amid); + fSegment2.getLowHigh(amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Abstract ValueConverter class. Converts values between UI and Faust representations +//-------------------------------------------------------------------------------------- +class ValueConverter +{ + + public: + + virtual ~ValueConverter() {} + virtual double ui2faust(double x) = 0; + virtual double faust2ui(double x) = 0; +}; + +//-------------------------------------------------------------------------------------- +// A converter than can be updated +//-------------------------------------------------------------------------------------- + +class UpdatableValueConverter : public ValueConverter { + + protected: + + bool fActive; + + public: + + UpdatableValueConverter():fActive(true) + {} + virtual ~UpdatableValueConverter() + {} + + virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) = 0; + virtual void getMappingValues(double& amin, double& amid, double& amax) = 0; + + void setActive(bool on_off) { fActive = on_off; } + bool getActive() { return fActive; } + +}; + + +//-------------------------------------------------------------------------------------- +// Linear conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LinearValueConverter : public ValueConverter +{ + + private: + + Interpolator fUI2F; + Interpolator fF2UI; + + public: + + LinearValueConverter(double umin, double umax, double fmin, double fmax) : + fUI2F(umin,umax,fmin,fmax), fF2UI(fmin,fmax,umin,umax) + {} + + LinearValueConverter() : fUI2F(0.,0.,0.,0.), fF2UI(0.,0.,0.,0.) + {} + virtual double ui2faust(double x) { return fUI2F(x); } + virtual double faust2ui(double x) { return fF2UI(x); } + +}; + +//-------------------------------------------------------------------------------------- +// Two segments linear conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LinearValueConverter2 : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fUI2F; + Interpolator3pt fF2UI; + + public: + + LinearValueConverter2(double amin, double amid, double amax, double min, double init, double max) : + fUI2F(amin, amid, amax, min, init, max), fF2UI(min, init, max, amin, amid, amax) + {} + + LinearValueConverter2() : fUI2F(0.,0.,0.,0.,0.,0.), fF2UI(0.,0.,0.,0.,0.,0.) + {} + + virtual double ui2faust(double x) { return fUI2F(x); } + virtual double faust2ui(double x) { return fF2UI(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double min, double init, double max) + { + fUI2F = Interpolator3pt(amin, amid, amax, min, init, max); + fF2UI = Interpolator3pt(min, init, max, amin, amid, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fUI2F.getMappingValues(amin, amid, amax); + } + +}; + +//-------------------------------------------------------------------------------------- +// Logarithmic conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class LogValueConverter : public LinearValueConverter +{ + + public: + + LogValueConverter(double umin, double umax, double fmin, double fmax) : + LinearValueConverter(umin, umax, std::log(std::max<double>(DBL_MIN, fmin)), std::log(std::max<double>(DBL_MIN, fmax))) + {} + + virtual double ui2faust(double x) { return std::exp(LinearValueConverter::ui2faust(x)); } + virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::log(std::max<double>(x, DBL_MIN))); } + +}; + +//-------------------------------------------------------------------------------------- +// Exponential conversion between ui and Faust values +//-------------------------------------------------------------------------------------- +class ExpValueConverter : public LinearValueConverter +{ + + public: + + ExpValueConverter(double umin, double umax, double fmin, double fmax) : + LinearValueConverter(umin, umax, std::min<double>(DBL_MAX, std::exp(fmin)), std::min<double>(DBL_MAX, std::exp(fmax))) + {} + + virtual double ui2faust(double x) { return std::log(LinearValueConverter::ui2faust(x)); } + virtual double faust2ui(double x) { return LinearValueConverter::faust2ui(std::min<double>(DBL_MAX, std::exp(x))); } + +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using an Up curve (curve 0) +//-------------------------------------------------------------------------------------- +class AccUpConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator3pt fF2A; + + public: + + AccUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmin,fmid,fmax), + fF2A(fmin,fmid,fmax,amin,amid,amax) + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmin, fmid, fmax); + fF2A = Interpolator3pt(fmin, fmid, fmax, amin, amid, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } + +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using a Down curve (curve 1) +//-------------------------------------------------------------------------------------- +class AccDownConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator3pt fF2A; + + public: + + AccDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmax,fmid,fmin), + fF2A(fmin,fmid,fmax,amax,amid,amin) + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmax, fmid, fmin); + fF2A = Interpolator3pt(fmin, fmid, fmax, amax, amid, amin); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using an Up-Down curve (curve 2) +//-------------------------------------------------------------------------------------- +class AccUpDownConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator fF2A; + + public: + + AccUpDownConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmin,fmax,fmin), + fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotonic function + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccUpDownConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmin, fmax, fmin); + fF2A = Interpolator(fmin, fmax, amin, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Convert accelerometer or gyroscope values to Faust values +// Using a Down-Up curve (curve 3) +//-------------------------------------------------------------------------------------- +class AccDownUpConverter : public UpdatableValueConverter +{ + + private: + + Interpolator3pt fA2F; + Interpolator fF2A; + + public: + + AccDownUpConverter(double amin, double amid, double amax, double fmin, double fmid, double fmax) : + fA2F(amin,amid,amax,fmax,fmin,fmax), + fF2A(fmin,fmax,amin,amax) // Special, pseudo inverse of a non monotonic function + {} + + virtual double ui2faust(double x) { return fA2F(x); } + virtual double faust2ui(double x) { return fF2A(x); } + + virtual void setMappingValues(double amin, double amid, double amax, double fmin, double fmid, double fmax) + { + //__android_log_print(ANDROID_LOG_ERROR, "Faust", "AccDownUpConverter update %f %f %f %f %f %f", amin,amid,amax,fmin,fmid,fmax); + fA2F = Interpolator3pt(amin, amid, amax, fmax, fmin, fmax); + fF2A = Interpolator(fmin, fmax, amin, amax); + } + + virtual void getMappingValues(double& amin, double& amid, double& amax) + { + fA2F.getMappingValues(amin, amid, amax); + } +}; + +//-------------------------------------------------------------------------------------- +// Base class for ZoneControl +//-------------------------------------------------------------------------------------- +class ZoneControl +{ + + protected: + + FAUSTFLOAT* fZone; + + public: + + ZoneControl(FAUSTFLOAT* zone) : fZone(zone) {} + virtual ~ZoneControl() {} + + virtual void update(double v) const {} + + virtual void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) {} + virtual void getMappingValues(double& amin, double& amid, double& amax) {} + + FAUSTFLOAT* getZone() { return fZone; } + + virtual void setActive(bool on_off) {} + virtual bool getActive() { return false; } + + virtual int getCurve() { return -1; } + +}; + +//-------------------------------------------------------------------------------------- +// Useful to implement accelerometers metadata as a list of ZoneControl for each axes +//-------------------------------------------------------------------------------------- +class ConverterZoneControl : public ZoneControl +{ + + protected: + + ValueConverter* fValueConverter; + + public: + + ConverterZoneControl(FAUSTFLOAT* zone, ValueConverter* converter) : ZoneControl(zone), fValueConverter(converter) {} + virtual ~ConverterZoneControl() { delete fValueConverter; } // Assuming fValueConverter is not kept elsewhere... + + virtual void update(double v) const { *fZone = fValueConverter->ui2faust(v); } + + ValueConverter* getConverter() { return fValueConverter; } + +}; + +//-------------------------------------------------------------------------------------- +// Association of a zone and a four value converter, each one for each possible curve. +// Useful to implement accelerometers metadata as a list of ZoneControl for each axes +//-------------------------------------------------------------------------------------- +class CurveZoneControl : public ZoneControl +{ + + private: + + std::vector<UpdatableValueConverter*> fValueConverters; + int fCurve; + + public: + + CurveZoneControl(FAUSTFLOAT* zone, int curve, double amin, double amid, double amax, double min, double init, double max) : ZoneControl(zone), fCurve(0) + { + assert(curve >= 0 && curve <= 3); + fValueConverters.push_back(new AccUpConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccDownConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccUpDownConverter(amin, amid, amax, min, init, max)); + fValueConverters.push_back(new AccDownUpConverter(amin, amid, amax, min, init, max)); + fCurve = curve; + } + virtual ~CurveZoneControl() + { + std::vector<UpdatableValueConverter*>::iterator it; + for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) { + delete(*it); + } + } + void update(double v) const { if (fValueConverters[fCurve]->getActive()) *fZone = fValueConverters[fCurve]->ui2faust(v); } + + void setMappingValues(int curve, double amin, double amid, double amax, double min, double init, double max) + { + fValueConverters[curve]->setMappingValues(amin, amid, amax, min, init, max); + fCurve = curve; + } + + void getMappingValues(double& amin, double& amid, double& amax) + { + fValueConverters[fCurve]->getMappingValues(amin, amid, amax); + } + + void setActive(bool on_off) + { + std::vector<UpdatableValueConverter*>::iterator it; + for (it = fValueConverters.begin(); it != fValueConverters.end(); it++) { + (*it)->setActive(on_off); + } + } + + int getCurve() { return fCurve; } +}; + +class ZoneReader +{ + + private: + + FAUSTFLOAT* fZone; + Interpolator fInterpolator; + + public: + + ZoneReader(FAUSTFLOAT* zone, double lo, double hi) : fZone(zone), fInterpolator(lo, hi, 0, 255) {} + + virtual ~ZoneReader() {} + + int getValue() + { + return (fZone != nullptr) ? int(fInterpolator(*fZone)) : 127; + } + +}; + +#endif +/************************** END ValueConverter.h **************************/ + +class APIUI : public PathBuilder, public Meta, public UI +{ + public: + + enum ItemType { kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph }; + + protected: + + enum { kLin = 0, kLog = 1, kExp = 2 }; + + int fNumParameters; + std::vector<std::string> fPaths; + std::vector<std::string> fLabels; + std::map<std::string, int> fPathMap; + std::map<std::string, int> fLabelMap; + std::vector<ValueConverter*> fConversion; + std::vector<FAUSTFLOAT*> fZone; + std::vector<FAUSTFLOAT> fInit; + std::vector<FAUSTFLOAT> fMin; + std::vector<FAUSTFLOAT> fMax; + std::vector<FAUSTFLOAT> fStep; + std::vector<ItemType> fItemType; + std::vector<std::map<std::string, std::string> > fMetaData; + std::vector<ZoneControl*> fAcc[3]; + std::vector<ZoneControl*> fGyr[3]; + + // Screen color control + // "...[screencolor:red]..." etc. + bool fHasScreenControl; // true if control screen color metadata + ZoneReader* fRedReader; + ZoneReader* fGreenReader; + ZoneReader* fBlueReader; + + // Current values controlled by metadata + std::string fCurrentUnit; + int fCurrentScale; + std::string fCurrentAcc; + std::string fCurrentGyr; + std::string fCurrentColor; + std::string fCurrentTooltip; + std::map<std::string, std::string> fCurrentMetadata; + + // Add a generic parameter + virtual void addParameter(const char* label, + FAUSTFLOAT* zone, + FAUSTFLOAT init, + FAUSTFLOAT min, + FAUSTFLOAT max, + FAUSTFLOAT step, + ItemType type) + { + std::string path = buildPath(label); + fPathMap[path] = fLabelMap[label] = fNumParameters++; + fPaths.push_back(path); + fLabels.push_back(label); + fZone.push_back(zone); + fInit.push_back(init); + fMin.push_back(min); + fMax.push_back(max); + fStep.push_back(step); + fItemType.push_back(type); + + // handle scale metadata + switch (fCurrentScale) { + case kLin: + fConversion.push_back(new LinearValueConverter(0, 1, min, max)); + break; + case kLog: + fConversion.push_back(new LogValueConverter(0, 1, min, max)); + break; + case kExp: fConversion.push_back(new ExpValueConverter(0, 1, min, max)); + break; + } + fCurrentScale = kLin; + + if (fCurrentAcc.size() > 0 && fCurrentGyr.size() > 0) { + std::cerr << "warning : 'acc' and 'gyr' metadata used for the same " << label << " parameter !!\n"; + } + + // handle acc metadata "...[acc : <axe> <curve> <amin> <amid> <amax>]..." + if (fCurrentAcc.size() > 0) { + std::istringstream iss(fCurrentAcc); + int axe, curve; + double amin, amid, amax; + iss >> axe >> curve >> amin >> amid >> amax; + + if ((0 <= axe) && (axe < 3) && + (0 <= curve) && (curve < 4) && + (amin < amax) && (amin <= amid) && (amid <= amax)) + { + fAcc[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); + } else { + std::cerr << "incorrect acc metadata : " << fCurrentAcc << std::endl; + } + fCurrentAcc = ""; + } + + // handle gyr metadata "...[gyr : <axe> <curve> <amin> <amid> <amax>]..." + if (fCurrentGyr.size() > 0) { + std::istringstream iss(fCurrentGyr); + int axe, curve; + double amin, amid, amax; + iss >> axe >> curve >> amin >> amid >> amax; + + if ((0 <= axe) && (axe < 3) && + (0 <= curve) && (curve < 4) && + (amin < amax) && (amin <= amid) && (amid <= amax)) + { + fGyr[axe].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, min, init, max)); + } else { + std::cerr << "incorrect gyr metadata : " << fCurrentGyr << std::endl; + } + fCurrentGyr = ""; + } + + // handle screencolor metadata "...[screencolor:red|green|blue|white]..." + if (fCurrentColor.size() > 0) { + if ((fCurrentColor == "red") && (fRedReader == 0)) { + fRedReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "green") && (fGreenReader == 0)) { + fGreenReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "blue") && (fBlueReader == 0)) { + fBlueReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else if ((fCurrentColor == "white") && (fRedReader == 0) && (fGreenReader == 0) && (fBlueReader == 0)) { + fRedReader = new ZoneReader(zone, min, max); + fGreenReader = new ZoneReader(zone, min, max); + fBlueReader = new ZoneReader(zone, min, max); + fHasScreenControl = true; + } else { + std::cerr << "incorrect screencolor metadata : " << fCurrentColor << std::endl; + } + } + fCurrentColor = ""; + + fMetaData.push_back(fCurrentMetadata); + fCurrentMetadata.clear(); + } + + int getZoneIndex(std::vector<ZoneControl*>* table, int p, int val) + { + FAUSTFLOAT* zone = fZone[p]; + for (size_t i = 0; i < table[val].size(); i++) { + if (zone == table[val][i]->getZone()) return int(i); + } + return -1; + } + + void setConverter(std::vector<ZoneControl*>* table, int p, int val, int curve, double amin, double amid, double amax) + { + int id1 = getZoneIndex(table, p, 0); + int id2 = getZoneIndex(table, p, 1); + int id3 = getZoneIndex(table, p, 2); + + // Deactivates everywhere.. + if (id1 != -1) table[0][id1]->setActive(false); + if (id2 != -1) table[1][id2]->setActive(false); + if (id3 != -1) table[2][id3]->setActive(false); + + if (val == -1) { // Means: no more mapping... + // So stay all deactivated... + } else { + int id4 = getZoneIndex(table, p, val); + if (id4 != -1) { + // Reactivate the one we edit... + table[val][id4]->setMappingValues(curve, amin, amid, amax, fMin[p], fInit[p], fMax[p]); + table[val][id4]->setActive(true); + } else { + // Allocate a new CurveZoneControl which is 'active' by default + FAUSTFLOAT* zone = fZone[p]; + table[val].push_back(new CurveZoneControl(zone, curve, amin, amid, amax, fMin[p], fInit[p], fMax[p])); + } + } + } + + void getConverter(std::vector<ZoneControl*>* table, int p, int& val, int& curve, double& amin, double& amid, double& amax) + { + int id1 = getZoneIndex(table, p, 0); + int id2 = getZoneIndex(table, p, 1); + int id3 = getZoneIndex(table, p, 2); + + if (id1 != -1) { + val = 0; + curve = table[val][id1]->getCurve(); + table[val][id1]->getMappingValues(amin, amid, amax); + } else if (id2 != -1) { + val = 1; + curve = table[val][id2]->getCurve(); + table[val][id2]->getMappingValues(amin, amid, amax); + } else if (id3 != -1) { + val = 2; + curve = table[val][id3]->getCurve(); + table[val][id3]->getMappingValues(amin, amid, amax); + } else { + val = -1; // No mapping + curve = 0; + amin = -100.; + amid = 0.; + amax = 100.; + } + } + + public: + + enum Type { kAcc = 0, kGyr = 1, kNoType }; + + APIUI() : fNumParameters(0), fHasScreenControl(false), fRedReader(0), fGreenReader(0), fBlueReader(0), fCurrentScale(kLin) + {} + + virtual ~APIUI() + { + for (auto& it : fConversion) delete it; + for (int i = 0; i < 3; i++) { + for (auto& it : fAcc[i]) delete it; + for (auto& it : fGyr[i]) delete it; + } + delete fRedReader; + delete fGreenReader; + delete fBlueReader; + } + + // -- widget's layouts + + virtual void openTabBox(const char* label) { pushLabel(label); } + virtual void openHorizontalBox(const char* label) { pushLabel(label); } + virtual void openVerticalBox(const char* label) { pushLabel(label); } + virtual void closeBox() { popLabel(); } + + // -- active widgets + + virtual void addButton(const char* label, FAUSTFLOAT* zone) + { + addParameter(label, zone, 0, 0, 1, 1, kButton); + } + + virtual void addCheckButton(const char* label, FAUSTFLOAT* zone) + { + addParameter(label, zone, 0, 0, 1, 1, kCheckButton); + } + + virtual void addVerticalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kVSlider); + } + + virtual void addHorizontalSlider(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kHSlider); + } + + virtual void addNumEntry(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step) + { + addParameter(label, zone, init, min, max, step, kNumEntry); + } + + // -- passive widgets + + virtual void addHorizontalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) + { + addParameter(label, zone, min, min, max, (max-min)/1000.0, kHBargraph); + } + + virtual void addVerticalBargraph(const char* label, FAUSTFLOAT* zone, FAUSTFLOAT min, FAUSTFLOAT max) + { + addParameter(label, zone, min, min, max, (max-min)/1000.0, kVBargraph); + } + + // -- soundfiles + + virtual void addSoundfile(const char* label, const char* filename, Soundfile** sf_zone) {} + + // -- metadata declarations + + virtual void declare(FAUSTFLOAT* zone, const char* key, const char* val) + { + // Keep metadata + fCurrentMetadata[key] = val; + + if (strcmp(key, "scale") == 0) { + if (strcmp(val, "log") == 0) { + fCurrentScale = kLog; + } else if (strcmp(val, "exp") == 0) { + fCurrentScale = kExp; + } else { + fCurrentScale = kLin; + } + } else if (strcmp(key, "unit") == 0) { + fCurrentUnit = val; + } else if (strcmp(key, "acc") == 0) { + fCurrentAcc = val; + } else if (strcmp(key, "gyr") == 0) { + fCurrentGyr = val; + } else if (strcmp(key, "screencolor") == 0) { + fCurrentColor = val; // val = "red", "green", "blue" or "white" + } else if (strcmp(key, "tooltip") == 0) { + fCurrentTooltip = val; + } + } + + virtual void declare(const char* key, const char* val) + {} + + //------------------------------------------------------------------------------- + // Simple API part + //------------------------------------------------------------------------------- + int getParamsCount() { return fNumParameters; } + int getParamIndex(const char* path) + { + if (fPathMap.find(path) != fPathMap.end()) { + return fPathMap[path]; + } else if (fLabelMap.find(path) != fLabelMap.end()) { + return fLabelMap[path]; + } else { + return -1; + } + } + const char* getParamAddress(int p) { return fPaths[p].c_str(); } + const char* getParamLabel(int p) { return fLabels[p].c_str(); } + std::map<const char*, const char*> getMetadata(int p) + { + std::map<const char*, const char*> res; + std::map<std::string, std::string> metadata = fMetaData[p]; + for (auto it : metadata) { + res[it.first.c_str()] = it.second.c_str(); + } + return res; + } + + const char* getMetadata(int p, const char* key) + { + return (fMetaData[p].find(key) != fMetaData[p].end()) ? fMetaData[p][key].c_str() : ""; + } + FAUSTFLOAT getParamMin(int p) { return fMin[p]; } + FAUSTFLOAT getParamMax(int p) { return fMax[p]; } + FAUSTFLOAT getParamStep(int p) { return fStep[p]; } + FAUSTFLOAT getParamInit(int p) { return fInit[p]; } + + FAUSTFLOAT* getParamZone(int p) { return fZone[p]; } + FAUSTFLOAT getParamValue(int p) { return *fZone[p]; } + void setParamValue(int p, FAUSTFLOAT v) { *fZone[p] = v; } + + double getParamRatio(int p) { return fConversion[p]->faust2ui(*fZone[p]); } + void setParamRatio(int p, double r) { *fZone[p] = fConversion[p]->ui2faust(r); } + + double value2ratio(int p, double r) { return fConversion[p]->faust2ui(r); } + double ratio2value(int p, double r) { return fConversion[p]->ui2faust(r); } + + /** + * Return the control type (kAcc, kGyr, or -1) for a given parameter + * + * @param p - the UI parameter index + * + * @return the type + */ + Type getParamType(int p) + { + if (p >= 0) { + if (getZoneIndex(fAcc, p, 0) != -1 + || getZoneIndex(fAcc, p, 1) != -1 + || getZoneIndex(fAcc, p, 2) != -1) { + return kAcc; + } else if (getZoneIndex(fGyr, p, 0) != -1 + || getZoneIndex(fGyr, p, 1) != -1 + || getZoneIndex(fGyr, p, 2) != -1) { + return kGyr; + } + } + return kNoType; + } + + /** + * Return the Item type (kButton = 0, kCheckButton, kVSlider, kHSlider, kNumEntry, kHBargraph, kVBargraph) for a given parameter + * + * @param p - the UI parameter index + * + * @return the Item type + */ + ItemType getParamItemType(int p) + { + return fItemType[p]; + } + + /** + * Set a new value coming from an accelerometer, propagate it to all relevant FAUSTFLOAT* zones. + * + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer + * @param value - the new value + * + */ + void propagateAcc(int acc, double value) + { + for (size_t i = 0; i < fAcc[acc].size(); i++) { + fAcc[acc][i]->update(value); + } + } + + /** + * Used to edit accelerometer curves and mapping. Set curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer (-1 means "no mapping") + * @param curve - between 0 and 3 + * @param amin - mapping 'min' point + * @param amid - mapping 'middle' point + * @param amax - mapping 'max' point + * + */ + void setAccConverter(int p, int acc, int curve, double amin, double amid, double amax) + { + setConverter(fAcc, p, acc, curve, amin, amid, amax); + } + + /** + * Used to edit gyroscope curves and mapping. Set curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope (-1 means "no mapping") + * @param curve - between 0 and 3 + * @param amin - mapping 'min' point + * @param amid - mapping 'middle' point + * @param amax - mapping 'max' point + * + */ + void setGyrConverter(int p, int gyr, int curve, double amin, double amid, double amax) + { + setConverter(fGyr, p, gyr, curve, amin, amid, amax); + } + + /** + * Used to edit accelerometer curves and mapping. Get curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param acc - the acc value to be retrieved (-1 means "no mapping") + * @param curve - the curve value to be retrieved + * @param amin - the amin value to be retrieved + * @param amid - the amid value to be retrieved + * @param amax - the amax value to be retrieved + * + */ + void getAccConverter(int p, int& acc, int& curve, double& amin, double& amid, double& amax) + { + getConverter(fAcc, p, acc, curve, amin, amid, amax); + } + + /** + * Used to edit gyroscope curves and mapping. Get curve and related mapping for a given UI parameter. + * + * @param p - the UI parameter index + * @param gyr - the gyr value to be retrieved (-1 means "no mapping") + * @param curve - the curve value to be retrieved + * @param amin - the amin value to be retrieved + * @param amid - the amid value to be retrieved + * @param amax - the amax value to be retrieved + * + */ + void getGyrConverter(int p, int& gyr, int& curve, double& amin, double& amid, double& amax) + { + getConverter(fGyr, p, gyr, curve, amin, amid, amax); + } + + /** + * Set a new value coming from an gyroscope, propagate it to all relevant FAUSTFLOAT* zones. + * + * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope + * @param value - the new value + * + */ + void propagateGyr(int gyr, double value) + { + for (size_t i = 0; i < fGyr[gyr].size(); i++) { + fGyr[gyr][i]->update(value); + } + } + + /** + * Get the number of FAUSTFLOAT* zones controlled with the accelerometer + * + * @param acc - 0 for X accelerometer, 1 for Y accelerometer, 2 for Z accelerometer + * @return the number of zones + * + */ + int getAccCount(int acc) + { + return (acc >= 0 && acc < 3) ? int(fAcc[acc].size()) : 0; + } + + /** + * Get the number of FAUSTFLOAT* zones controlled with the gyroscope + * + * @param gyr - 0 for X gyroscope, 1 for Y gyroscope, 2 for Z gyroscope + * @param the number of zones + * + */ + int getGyrCount(int gyr) + { + return (gyr >= 0 && gyr < 3) ? int(fGyr[gyr].size()) : 0; + } + + // getScreenColor() : -1 means no screen color control (no screencolor metadata found) + // otherwise return 0x00RRGGBB a ready to use color + int getScreenColor() + { + if (fHasScreenControl) { + int r = (fRedReader) ? fRedReader->getValue() : 0; + int g = (fGreenReader) ? fGreenReader->getValue() : 0; + int b = (fBlueReader) ? fBlueReader->getValue() : 0; + return (r<<16) | (g<<8) | b; + } else { + return -1; + } + } + +}; + +#endif +/************************** END APIUI.h **************************/ + +// NOTE: "faust -scn name" changes the last line above to +// #include <faust/name/name.h> + +//---------------------------------------------------------------------------- +// FAUST Generated Code +//---------------------------------------------------------------------------- + + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +#include <algorithm> +#include <cmath> +#include <math.h> + +static float zitarevmonodsp_faustpower2_f(float value) { + return (value * value); +} + +#ifndef FAUSTCLASS +#define FAUSTCLASS zitarevmonodsp +#endif + +#ifdef __APPLE__ +#define exp10f __exp10f +#define exp10 __exp10 +#endif + +class zitarevmonodsp : public dsp { + + private: + + int IOTA; + float fVec0[16384]; + FAUSTFLOAT fVslider0; + float fRec0[2]; + FAUSTFLOAT fVslider1; + float fRec1[2]; + int fSampleRate; + float fConst0; + float fConst1; + FAUSTFLOAT fVslider2; + FAUSTFLOAT fVslider3; + FAUSTFLOAT fVslider4; + FAUSTFLOAT fVslider5; + float fConst2; + float fConst3; + FAUSTFLOAT fVslider6; + FAUSTFLOAT fVslider7; + FAUSTFLOAT fVslider8; + float fConst4; + FAUSTFLOAT fVslider9; + float fRec15[2]; + float fRec14[2]; + float fVec1[32768]; + float fConst5; + int iConst6; + float fConst7; + FAUSTFLOAT fVslider10; + float fVec2[2048]; + int iConst8; + float fRec12[2]; + float fConst9; + float fConst10; + float fRec19[2]; + float fRec18[2]; + float fVec3[32768]; + float fConst11; + int iConst12; + float fVec4[4096]; + int iConst13; + float fRec16[2]; + float fConst14; + float fConst15; + float fRec23[2]; + float fRec22[2]; + float fVec5[16384]; + float fConst16; + int iConst17; + float fVec6[4096]; + int iConst18; + float fRec20[2]; + float fConst19; + float fConst20; + float fRec27[2]; + float fRec26[2]; + float fVec7[32768]; + float fConst21; + int iConst22; + float fVec8[4096]; + int iConst23; + float fRec24[2]; + float fConst24; + float fConst25; + float fRec31[2]; + float fRec30[2]; + float fVec9[16384]; + float fConst26; + int iConst27; + float fVec10[2048]; + int iConst28; + float fRec28[2]; + float fConst29; + float fConst30; + float fRec35[2]; + float fRec34[2]; + float fVec11[16384]; + float fConst31; + int iConst32; + float fVec12[4096]; + int iConst33; + float fRec32[2]; + float fConst34; + float fConst35; + float fRec39[2]; + float fRec38[2]; + float fVec13[16384]; + float fConst36; + int iConst37; + float fVec14[4096]; + int iConst38; + float fRec36[2]; + float fConst39; + float fConst40; + float fRec43[2]; + float fRec42[2]; + float fVec15[16384]; + float fConst41; + int iConst42; + float fVec16[2048]; + int iConst43; + float fRec40[2]; + float fRec4[3]; + float fRec5[3]; + float fRec6[3]; + float fRec7[3]; + float fRec8[3]; + float fRec9[3]; + float fRec10[3]; + float fRec11[3]; + float fRec3[3]; + float fRec2[3]; + float fRec45[3]; + float fRec44[3]; + + public: + + void metadata(Meta* m) { + m->declare("basics.lib/name", "Faust Basic Element Library"); + m->declare("basics.lib/version", "0.1"); + m->declare("delays.lib/name", "Faust Delay Library"); + m->declare("delays.lib/version", "0.1"); + m->declare("filename", "zitarevmonodsp.dsp"); + m->declare("filters.lib/allpass_comb:author", "Julius O. Smith III"); + m->declare("filters.lib/allpass_comb:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/allpass_comb:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/fir:author", "Julius O. Smith III"); + m->declare("filters.lib/fir:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/fir:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/iir:author", "Julius O. Smith III"); + m->declare("filters.lib/iir:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/iir:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/lowpass0_highpass1", "MIT-style STK-4.3 license"); + m->declare("filters.lib/lowpass0_highpass1:author", "Julius O. Smith III"); + m->declare("filters.lib/lowpass:author", "Julius O. Smith III"); + m->declare("filters.lib/lowpass:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/lowpass:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/name", "Faust Filters Library"); + m->declare("filters.lib/peak_eq_rm:author", "Julius O. Smith III"); + m->declare("filters.lib/peak_eq_rm:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/peak_eq_rm:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/tf1:author", "Julius O. Smith III"); + m->declare("filters.lib/tf1:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/tf1:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/tf1s:author", "Julius O. Smith III"); + m->declare("filters.lib/tf1s:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/tf1s:license", "MIT-style STK-4.3 license"); + m->declare("filters.lib/tf2:author", "Julius O. Smith III"); + m->declare("filters.lib/tf2:copyright", "Copyright (C) 2003-2019 by Julius O. Smith III <jos@ccrma.stanford.edu>"); + m->declare("filters.lib/tf2:license", "MIT-style STK-4.3 license"); + m->declare("maths.lib/author", "GRAME"); + m->declare("maths.lib/copyright", "GRAME"); + m->declare("maths.lib/license", "LGPL with exception"); + m->declare("maths.lib/name", "Faust Math Library"); + m->declare("maths.lib/version", "2.3"); + m->declare("name", "zitarevmonodsp"); + m->declare("platform.lib/name", "Generic Platform Library"); + m->declare("platform.lib/version", "0.1"); + m->declare("reverbs.lib/name", "Faust Reverb Library"); + m->declare("reverbs.lib/version", "0.0"); + m->declare("routes.lib/name", "Faust Signal Routing Library"); + m->declare("routes.lib/version", "0.2"); + m->declare("signals.lib/name", "Faust Signal Routing Library"); + m->declare("signals.lib/version", "0.0"); + } + + virtual int getNumInputs() { + return 1; + } + virtual int getNumOutputs() { + return 1; + } + virtual int getInputRate(int channel) { + int rate; + switch ((channel)) { + case 0: { + rate = 1; + break; + } + default: { + rate = -1; + break; + } + } + return rate; + } + virtual int getOutputRate(int channel) { + int rate; + switch ((channel)) { + case 0: { + rate = 1; + break; + } + default: { + rate = -1; + break; + } + } + return rate; + } + + static void classInit(int sample_rate) { + } + + virtual void instanceConstants(int sample_rate) { + fSampleRate = sample_rate; + fConst0 = std::min<float>(192000.0f, std::max<float>(1.0f, float(fSampleRate))); + fConst1 = (6.28318548f / fConst0); + fConst2 = std::floor(((0.219990999f * fConst0) + 0.5f)); + fConst3 = ((0.0f - (6.90775537f * fConst2)) / fConst0); + fConst4 = (3.14159274f / fConst0); + fConst5 = std::floor(((0.0191229992f * fConst0) + 0.5f)); + iConst6 = int(std::min<float>(16384.0f, std::max<float>(0.0f, (fConst2 - fConst5)))); + fConst7 = (0.00100000005f * fConst0); + iConst8 = int(std::min<float>(1024.0f, std::max<float>(0.0f, (fConst5 + -1.0f)))); + fConst9 = std::floor(((0.256891012f * fConst0) + 0.5f)); + fConst10 = ((0.0f - (6.90775537f * fConst9)) / fConst0); + fConst11 = std::floor(((0.0273330007f * fConst0) + 0.5f)); + iConst12 = int(std::min<float>(16384.0f, std::max<float>(0.0f, (fConst9 - fConst11)))); + iConst13 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst11 + -1.0f)))); + fConst14 = std::floor(((0.192303002f * fConst0) + 0.5f)); + fConst15 = ((0.0f - (6.90775537f * fConst14)) / fConst0); + fConst16 = std::floor(((0.0292910002f * fConst0) + 0.5f)); + iConst17 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst14 - fConst16)))); + iConst18 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst16 + -1.0f)))); + fConst19 = std::floor(((0.210389003f * fConst0) + 0.5f)); + fConst20 = ((0.0f - (6.90775537f * fConst19)) / fConst0); + fConst21 = std::floor(((0.0244210009f * fConst0) + 0.5f)); + iConst22 = int(std::min<float>(16384.0f, std::max<float>(0.0f, (fConst19 - fConst21)))); + iConst23 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst21 + -1.0f)))); + fConst24 = std::floor(((0.125f * fConst0) + 0.5f)); + fConst25 = ((0.0f - (6.90775537f * fConst24)) / fConst0); + fConst26 = std::floor(((0.0134579996f * fConst0) + 0.5f)); + iConst27 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst24 - fConst26)))); + iConst28 = int(std::min<float>(1024.0f, std::max<float>(0.0f, (fConst26 + -1.0f)))); + fConst29 = std::floor(((0.127837002f * fConst0) + 0.5f)); + fConst30 = ((0.0f - (6.90775537f * fConst29)) / fConst0); + fConst31 = std::floor(((0.0316039994f * fConst0) + 0.5f)); + iConst32 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst29 - fConst31)))); + iConst33 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst31 + -1.0f)))); + fConst34 = std::floor(((0.174713001f * fConst0) + 0.5f)); + fConst35 = ((0.0f - (6.90775537f * fConst34)) / fConst0); + fConst36 = std::floor(((0.0229039993f * fConst0) + 0.5f)); + iConst37 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst34 - fConst36)))); + iConst38 = int(std::min<float>(2048.0f, std::max<float>(0.0f, (fConst36 + -1.0f)))); + fConst39 = std::floor(((0.153128996f * fConst0) + 0.5f)); + fConst40 = ((0.0f - (6.90775537f * fConst39)) / fConst0); + fConst41 = std::floor(((0.0203460008f * fConst0) + 0.5f)); + iConst42 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst39 - fConst41)))); + iConst43 = int(std::min<float>(1024.0f, std::max<float>(0.0f, (fConst41 + -1.0f)))); + } + + virtual void instanceResetUserInterface() { + fVslider0 = FAUSTFLOAT(-3.0f); + fVslider1 = FAUSTFLOAT(0.0f); + fVslider2 = FAUSTFLOAT(1500.0f); + fVslider3 = FAUSTFLOAT(0.0f); + fVslider4 = FAUSTFLOAT(315.0f); + fVslider5 = FAUSTFLOAT(0.0f); + fVslider6 = FAUSTFLOAT(2.0f); + fVslider7 = FAUSTFLOAT(6000.0f); + fVslider8 = FAUSTFLOAT(3.0f); + fVslider9 = FAUSTFLOAT(200.0f); + fVslider10 = FAUSTFLOAT(60.0f); + } + + virtual void instanceClear() { + IOTA = 0; + for (int l0 = 0; (l0 < 16384); l0 = (l0 + 1)) { + fVec0[l0] = 0.0f; + } + for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { + fRec0[l1] = 0.0f; + } + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec1[l2] = 0.0f; + } + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fRec15[l3] = 0.0f; + } + for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { + fRec14[l4] = 0.0f; + } + for (int l5 = 0; (l5 < 32768); l5 = (l5 + 1)) { + fVec1[l5] = 0.0f; + } + for (int l6 = 0; (l6 < 2048); l6 = (l6 + 1)) { + fVec2[l6] = 0.0f; + } + for (int l7 = 0; (l7 < 2); l7 = (l7 + 1)) { + fRec12[l7] = 0.0f; + } + for (int l8 = 0; (l8 < 2); l8 = (l8 + 1)) { + fRec19[l8] = 0.0f; + } + for (int l9 = 0; (l9 < 2); l9 = (l9 + 1)) { + fRec18[l9] = 0.0f; + } + for (int l10 = 0; (l10 < 32768); l10 = (l10 + 1)) { + fVec3[l10] = 0.0f; + } + for (int l11 = 0; (l11 < 4096); l11 = (l11 + 1)) { + fVec4[l11] = 0.0f; + } + for (int l12 = 0; (l12 < 2); l12 = (l12 + 1)) { + fRec16[l12] = 0.0f; + } + for (int l13 = 0; (l13 < 2); l13 = (l13 + 1)) { + fRec23[l13] = 0.0f; + } + for (int l14 = 0; (l14 < 2); l14 = (l14 + 1)) { + fRec22[l14] = 0.0f; + } + for (int l15 = 0; (l15 < 16384); l15 = (l15 + 1)) { + fVec5[l15] = 0.0f; + } + for (int l16 = 0; (l16 < 4096); l16 = (l16 + 1)) { + fVec6[l16] = 0.0f; + } + for (int l17 = 0; (l17 < 2); l17 = (l17 + 1)) { + fRec20[l17] = 0.0f; + } + for (int l18 = 0; (l18 < 2); l18 = (l18 + 1)) { + fRec27[l18] = 0.0f; + } + for (int l19 = 0; (l19 < 2); l19 = (l19 + 1)) { + fRec26[l19] = 0.0f; + } + for (int l20 = 0; (l20 < 32768); l20 = (l20 + 1)) { + fVec7[l20] = 0.0f; + } + for (int l21 = 0; (l21 < 4096); l21 = (l21 + 1)) { + fVec8[l21] = 0.0f; + } + for (int l22 = 0; (l22 < 2); l22 = (l22 + 1)) { + fRec24[l22] = 0.0f; + } + for (int l23 = 0; (l23 < 2); l23 = (l23 + 1)) { + fRec31[l23] = 0.0f; + } + for (int l24 = 0; (l24 < 2); l24 = (l24 + 1)) { + fRec30[l24] = 0.0f; + } + for (int l25 = 0; (l25 < 16384); l25 = (l25 + 1)) { + fVec9[l25] = 0.0f; + } + for (int l26 = 0; (l26 < 2048); l26 = (l26 + 1)) { + fVec10[l26] = 0.0f; + } + for (int l27 = 0; (l27 < 2); l27 = (l27 + 1)) { + fRec28[l27] = 0.0f; + } + for (int l28 = 0; (l28 < 2); l28 = (l28 + 1)) { + fRec35[l28] = 0.0f; + } + for (int l29 = 0; (l29 < 2); l29 = (l29 + 1)) { + fRec34[l29] = 0.0f; + } + for (int l30 = 0; (l30 < 16384); l30 = (l30 + 1)) { + fVec11[l30] = 0.0f; + } + for (int l31 = 0; (l31 < 4096); l31 = (l31 + 1)) { + fVec12[l31] = 0.0f; + } + for (int l32 = 0; (l32 < 2); l32 = (l32 + 1)) { + fRec32[l32] = 0.0f; + } + for (int l33 = 0; (l33 < 2); l33 = (l33 + 1)) { + fRec39[l33] = 0.0f; + } + for (int l34 = 0; (l34 < 2); l34 = (l34 + 1)) { + fRec38[l34] = 0.0f; + } + for (int l35 = 0; (l35 < 16384); l35 = (l35 + 1)) { + fVec13[l35] = 0.0f; + } + for (int l36 = 0; (l36 < 4096); l36 = (l36 + 1)) { + fVec14[l36] = 0.0f; + } + for (int l37 = 0; (l37 < 2); l37 = (l37 + 1)) { + fRec36[l37] = 0.0f; + } + for (int l38 = 0; (l38 < 2); l38 = (l38 + 1)) { + fRec43[l38] = 0.0f; + } + for (int l39 = 0; (l39 < 2); l39 = (l39 + 1)) { + fRec42[l39] = 0.0f; + } + for (int l40 = 0; (l40 < 16384); l40 = (l40 + 1)) { + fVec15[l40] = 0.0f; + } + for (int l41 = 0; (l41 < 2048); l41 = (l41 + 1)) { + fVec16[l41] = 0.0f; + } + for (int l42 = 0; (l42 < 2); l42 = (l42 + 1)) { + fRec40[l42] = 0.0f; + } + for (int l43 = 0; (l43 < 3); l43 = (l43 + 1)) { + fRec4[l43] = 0.0f; + } + for (int l44 = 0; (l44 < 3); l44 = (l44 + 1)) { + fRec5[l44] = 0.0f; + } + for (int l45 = 0; (l45 < 3); l45 = (l45 + 1)) { + fRec6[l45] = 0.0f; + } + for (int l46 = 0; (l46 < 3); l46 = (l46 + 1)) { + fRec7[l46] = 0.0f; + } + for (int l47 = 0; (l47 < 3); l47 = (l47 + 1)) { + fRec8[l47] = 0.0f; + } + for (int l48 = 0; (l48 < 3); l48 = (l48 + 1)) { + fRec9[l48] = 0.0f; + } + for (int l49 = 0; (l49 < 3); l49 = (l49 + 1)) { + fRec10[l49] = 0.0f; + } + for (int l50 = 0; (l50 < 3); l50 = (l50 + 1)) { + fRec11[l50] = 0.0f; + } + for (int l51 = 0; (l51 < 3); l51 = (l51 + 1)) { + fRec3[l51] = 0.0f; + } + for (int l52 = 0; (l52 < 3); l52 = (l52 + 1)) { + fRec2[l52] = 0.0f; + } + for (int l53 = 0; (l53 < 3); l53 = (l53 + 1)) { + fRec45[l53] = 0.0f; + } + for (int l54 = 0; (l54 < 3); l54 = (l54 + 1)) { + fRec44[l54] = 0.0f; + } + } + + virtual void init(int sample_rate) { + classInit(sample_rate); + instanceInit(sample_rate); + } + virtual void instanceInit(int sample_rate) { + instanceConstants(sample_rate); + instanceResetUserInterface(); + instanceClear(); + } + + virtual zitarevmonodsp* clone() { + return new zitarevmonodsp(); + } + + virtual int getSampleRate() { + return fSampleRate; + } + + virtual void buildUserInterface(UI* ui_interface) { + ui_interface->declare(0, "0", ""); + ui_interface->declare(0, "tooltip", "~ ZITA REV1 FEEDBACK DELAY NETWORK (FDN) & SCHROEDER ALLPASS-COMB REVERBERATOR (8x8). See Faust's reverbs.lib for documentation and references"); + ui_interface->openHorizontalBox("Zita_Rev1"); + ui_interface->declare(0, "1", ""); + ui_interface->openHorizontalBox("Input"); + ui_interface->declare(&fVslider10, "1", ""); + ui_interface->declare(&fVslider10, "style", "knob"); + ui_interface->declare(&fVslider10, "tooltip", "Delay in ms before reverberation begins"); + ui_interface->declare(&fVslider10, "unit", "ms"); + ui_interface->addVerticalSlider("In Delay", &fVslider10, 60.0f, 20.0f, 100.0f, 1.0f); + ui_interface->closeBox(); + ui_interface->declare(0, "2", ""); + ui_interface->openHorizontalBox("Decay Times in Bands (see tooltips)"); + ui_interface->declare(&fVslider9, "1", ""); + ui_interface->declare(&fVslider9, "scale", "log"); + ui_interface->declare(&fVslider9, "style", "knob"); + ui_interface->declare(&fVslider9, "tooltip", "Crossover frequency (Hz) separating low and middle frequencies"); + ui_interface->declare(&fVslider9, "unit", "Hz"); + ui_interface->addVerticalSlider("LF X", &fVslider9, 200.0f, 50.0f, 1000.0f, 1.0f); + ui_interface->declare(&fVslider8, "2", ""); + ui_interface->declare(&fVslider8, "scale", "log"); + ui_interface->declare(&fVslider8, "style", "knob"); + ui_interface->declare(&fVslider8, "tooltip", "T60 = time (in seconds) to decay 60dB in low-frequency band"); + ui_interface->declare(&fVslider8, "unit", "s"); + ui_interface->addVerticalSlider("Low RT60", &fVslider8, 3.0f, 1.0f, 8.0f, 0.100000001f); + ui_interface->declare(&fVslider6, "3", ""); + ui_interface->declare(&fVslider6, "scale", "log"); + ui_interface->declare(&fVslider6, "style", "knob"); + ui_interface->declare(&fVslider6, "tooltip", "T60 = time (in seconds) to decay 60dB in middle band"); + ui_interface->declare(&fVslider6, "unit", "s"); + ui_interface->addVerticalSlider("Mid RT60", &fVslider6, 2.0f, 1.0f, 8.0f, 0.100000001f); + ui_interface->declare(&fVslider7, "4", ""); + ui_interface->declare(&fVslider7, "scale", "log"); + ui_interface->declare(&fVslider7, "style", "knob"); + ui_interface->declare(&fVslider7, "tooltip", "Frequency (Hz) at which the high-frequency T60 is half the middle-band's T60"); + ui_interface->declare(&fVslider7, "unit", "Hz"); + ui_interface->addVerticalSlider("HF Damping", &fVslider7, 6000.0f, 1500.0f, 23520.0f, 1.0f); + ui_interface->closeBox(); + ui_interface->declare(0, "3", ""); + ui_interface->openHorizontalBox("RM Peaking Equalizer 1"); + ui_interface->declare(&fVslider4, "1", ""); + ui_interface->declare(&fVslider4, "scale", "log"); + ui_interface->declare(&fVslider4, "style", "knob"); + ui_interface->declare(&fVslider4, "tooltip", "Center-frequency of second-order Regalia-Mitra peaking equalizer section 1"); + ui_interface->declare(&fVslider4, "unit", "Hz"); + ui_interface->addVerticalSlider("Eq1 Freq", &fVslider4, 315.0f, 40.0f, 2500.0f, 1.0f); + ui_interface->declare(&fVslider5, "2", ""); + ui_interface->declare(&fVslider5, "style", "knob"); + ui_interface->declare(&fVslider5, "tooltip", "Peak level in dB of second-order Regalia-Mitra peaking equalizer section 1"); + ui_interface->declare(&fVslider5, "unit", "dB"); + ui_interface->addVerticalSlider("Eq1 Level", &fVslider5, 0.0f, -15.0f, 15.0f, 0.100000001f); + ui_interface->closeBox(); + ui_interface->declare(0, "4", ""); + ui_interface->openHorizontalBox("RM Peaking Equalizer 2"); + ui_interface->declare(&fVslider2, "1", ""); + ui_interface->declare(&fVslider2, "scale", "log"); + ui_interface->declare(&fVslider2, "style", "knob"); + ui_interface->declare(&fVslider2, "tooltip", "Center-frequency of second-order Regalia-Mitra peaking equalizer section 2"); + ui_interface->declare(&fVslider2, "unit", "Hz"); + ui_interface->addVerticalSlider("Eq2 Freq", &fVslider2, 1500.0f, 160.0f, 10000.0f, 1.0f); + ui_interface->declare(&fVslider3, "2", ""); + ui_interface->declare(&fVslider3, "style", "knob"); + ui_interface->declare(&fVslider3, "tooltip", "Peak level in dB of second-order Regalia-Mitra peaking equalizer section 2"); + ui_interface->declare(&fVslider3, "unit", "dB"); + ui_interface->addVerticalSlider("Eq2 Level", &fVslider3, 0.0f, -15.0f, 15.0f, 0.100000001f); + ui_interface->closeBox(); + ui_interface->declare(0, "5", ""); + ui_interface->openHorizontalBox("Output"); + ui_interface->declare(&fVslider1, "1", ""); + ui_interface->declare(&fVslider1, "style", "knob"); + ui_interface->declare(&fVslider1, "tooltip", "Dry/Wet Mix: 0 = dry, 1 = wet"); + ui_interface->addVerticalSlider("Wet", &fVslider1, 0.0f, 0.0f, 1.0f, 0.00999999978f); + ui_interface->declare(&fVslider0, "2", ""); + ui_interface->declare(&fVslider0, "style", "knob"); + ui_interface->declare(&fVslider0, "tooltip", "Output scale factor"); + ui_interface->declare(&fVslider0, "unit", "dB"); + ui_interface->addVerticalSlider("Level", &fVslider0, -3.0f, -70.0f, 20.0f, 0.100000001f); + ui_interface->closeBox(); + ui_interface->closeBox(); + } + + virtual void compute(int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) { + FAUSTFLOAT* input0 = inputs[0]; + FAUSTFLOAT* output0 = outputs[0]; + float fSlow0 = (0.00100000005f * std::pow(10.0f, (0.0500000007f * float(fVslider0)))); + float fSlow1 = (0.00100000005f * float(fVslider1)); + float fSlow2 = float(fVslider2); + float fSlow3 = std::pow(10.0f, (0.0500000007f * float(fVslider3))); + float fSlow4 = (fConst1 * (fSlow2 / std::sqrt(std::max<float>(0.0f, fSlow3)))); + float fSlow5 = ((1.0f - fSlow4) / (fSlow4 + 1.0f)); + float fSlow6 = float(fVslider4); + float fSlow7 = std::pow(10.0f, (0.0500000007f * float(fVslider5))); + float fSlow8 = (fConst1 * (fSlow6 / std::sqrt(std::max<float>(0.0f, fSlow7)))); + float fSlow9 = ((1.0f - fSlow8) / (fSlow8 + 1.0f)); + float fSlow10 = float(fVslider6); + float fSlow11 = std::exp((fConst3 / fSlow10)); + float fSlow12 = zitarevmonodsp_faustpower2_f(fSlow11); + float fSlow13 = std::cos((fConst1 * float(fVslider7))); + float fSlow14 = (1.0f - (fSlow12 * fSlow13)); + float fSlow15 = (1.0f - fSlow12); + float fSlow16 = (fSlow14 / fSlow15); + float fSlow17 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow14) / zitarevmonodsp_faustpower2_f(fSlow15)) + -1.0f))); + float fSlow18 = (fSlow16 - fSlow17); + float fSlow19 = (fSlow11 * (fSlow17 + (1.0f - fSlow16))); + float fSlow20 = float(fVslider8); + float fSlow21 = ((std::exp((fConst3 / fSlow20)) / fSlow11) + -1.0f); + float fSlow22 = (1.0f / std::tan((fConst4 * float(fVslider9)))); + float fSlow23 = (1.0f / (fSlow22 + 1.0f)); + float fSlow24 = (1.0f - fSlow22); + int iSlow25 = int(std::min<float>(8192.0f, std::max<float>(0.0f, (fConst7 * float(fVslider10))))); + float fSlow26 = std::exp((fConst10 / fSlow10)); + float fSlow27 = zitarevmonodsp_faustpower2_f(fSlow26); + float fSlow28 = (1.0f - (fSlow27 * fSlow13)); + float fSlow29 = (1.0f - fSlow27); + float fSlow30 = (fSlow28 / fSlow29); + float fSlow31 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow28) / zitarevmonodsp_faustpower2_f(fSlow29)) + -1.0f))); + float fSlow32 = (fSlow30 - fSlow31); + float fSlow33 = (fSlow26 * (fSlow31 + (1.0f - fSlow30))); + float fSlow34 = ((std::exp((fConst10 / fSlow20)) / fSlow26) + -1.0f); + float fSlow35 = std::exp((fConst15 / fSlow10)); + float fSlow36 = zitarevmonodsp_faustpower2_f(fSlow35); + float fSlow37 = (1.0f - (fSlow36 * fSlow13)); + float fSlow38 = (1.0f - fSlow36); + float fSlow39 = (fSlow37 / fSlow38); + float fSlow40 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow37) / zitarevmonodsp_faustpower2_f(fSlow38)) + -1.0f))); + float fSlow41 = (fSlow39 - fSlow40); + float fSlow42 = (fSlow35 * (fSlow40 + (1.0f - fSlow39))); + float fSlow43 = ((std::exp((fConst15 / fSlow20)) / fSlow35) + -1.0f); + float fSlow44 = std::exp((fConst20 / fSlow10)); + float fSlow45 = zitarevmonodsp_faustpower2_f(fSlow44); + float fSlow46 = (1.0f - (fSlow45 * fSlow13)); + float fSlow47 = (1.0f - fSlow45); + float fSlow48 = (fSlow46 / fSlow47); + float fSlow49 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow46) / zitarevmonodsp_faustpower2_f(fSlow47)) + -1.0f))); + float fSlow50 = (fSlow48 - fSlow49); + float fSlow51 = (fSlow44 * (fSlow49 + (1.0f - fSlow48))); + float fSlow52 = ((std::exp((fConst20 / fSlow20)) / fSlow44) + -1.0f); + float fSlow53 = std::exp((fConst25 / fSlow10)); + float fSlow54 = zitarevmonodsp_faustpower2_f(fSlow53); + float fSlow55 = (1.0f - (fSlow54 * fSlow13)); + float fSlow56 = (1.0f - fSlow54); + float fSlow57 = (fSlow55 / fSlow56); + float fSlow58 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow55) / zitarevmonodsp_faustpower2_f(fSlow56)) + -1.0f))); + float fSlow59 = (fSlow57 - fSlow58); + float fSlow60 = (fSlow53 * (fSlow58 + (1.0f - fSlow57))); + float fSlow61 = ((std::exp((fConst25 / fSlow20)) / fSlow53) + -1.0f); + float fSlow62 = std::exp((fConst30 / fSlow10)); + float fSlow63 = zitarevmonodsp_faustpower2_f(fSlow62); + float fSlow64 = (1.0f - (fSlow63 * fSlow13)); + float fSlow65 = (1.0f - fSlow63); + float fSlow66 = (fSlow64 / fSlow65); + float fSlow67 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow64) / zitarevmonodsp_faustpower2_f(fSlow65)) + -1.0f))); + float fSlow68 = (fSlow66 - fSlow67); + float fSlow69 = (fSlow62 * (fSlow67 + (1.0f - fSlow66))); + float fSlow70 = ((std::exp((fConst30 / fSlow20)) / fSlow62) + -1.0f); + float fSlow71 = std::exp((fConst35 / fSlow10)); + float fSlow72 = zitarevmonodsp_faustpower2_f(fSlow71); + float fSlow73 = (1.0f - (fSlow72 * fSlow13)); + float fSlow74 = (1.0f - fSlow72); + float fSlow75 = (fSlow73 / fSlow74); + float fSlow76 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow73) / zitarevmonodsp_faustpower2_f(fSlow74)) + -1.0f))); + float fSlow77 = (fSlow75 - fSlow76); + float fSlow78 = (fSlow71 * (fSlow76 + (1.0f - fSlow75))); + float fSlow79 = ((std::exp((fConst35 / fSlow20)) / fSlow71) + -1.0f); + float fSlow80 = std::exp((fConst40 / fSlow10)); + float fSlow81 = zitarevmonodsp_faustpower2_f(fSlow80); + float fSlow82 = (1.0f - (fSlow81 * fSlow13)); + float fSlow83 = (1.0f - fSlow81); + float fSlow84 = (fSlow82 / fSlow83); + float fSlow85 = std::sqrt(std::max<float>(0.0f, ((zitarevmonodsp_faustpower2_f(fSlow82) / zitarevmonodsp_faustpower2_f(fSlow83)) + -1.0f))); + float fSlow86 = (fSlow84 - fSlow85); + float fSlow87 = (fSlow80 * (fSlow85 + (1.0f - fSlow84))); + float fSlow88 = ((std::exp((fConst40 / fSlow20)) / fSlow80) + -1.0f); + float fSlow89 = (0.0f - (std::cos((fConst1 * fSlow6)) * (fSlow9 + 1.0f))); + float fSlow90 = (0.0f - (std::cos((fConst1 * fSlow2)) * (fSlow5 + 1.0f))); + for (int i = 0; (i < count); i = (i + 1)) { + float fTemp0 = float(input0[i]); + fVec0[(IOTA & 16383)] = fTemp0; + fRec0[0] = (fSlow0 + (0.999000013f * fRec0[1])); + fRec1[0] = (fSlow1 + (0.999000013f * fRec1[1])); + fRec15[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec15[1]) - (fRec11[1] + fRec11[2])))); + fRec14[0] = ((fSlow18 * fRec14[1]) + (fSlow19 * (fRec11[1] + (fSlow21 * fRec15[0])))); + fVec1[(IOTA & 32767)] = ((0.353553385f * fRec14[0]) + 9.99999968e-21f); + float fTemp1 = (0.300000012f * fVec0[((IOTA - iSlow25) & 16383)]); + float fTemp2 = (((0.600000024f * fRec12[1]) + fVec1[((IOTA - iConst6) & 32767)]) - fTemp1); + fVec2[(IOTA & 2047)] = fTemp2; + fRec12[0] = fVec2[((IOTA - iConst8) & 2047)]; + float fRec13 = (0.0f - (0.600000024f * fTemp2)); + fRec19[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec19[1]) - (fRec7[1] + fRec7[2])))); + fRec18[0] = ((fSlow32 * fRec18[1]) + (fSlow33 * (fRec7[1] + (fSlow34 * fRec19[0])))); + fVec3[(IOTA & 32767)] = ((0.353553385f * fRec18[0]) + 9.99999968e-21f); + float fTemp3 = (((0.600000024f * fRec16[1]) + fVec3[((IOTA - iConst12) & 32767)]) - fTemp1); + fVec4[(IOTA & 4095)] = fTemp3; + fRec16[0] = fVec4[((IOTA - iConst13) & 4095)]; + float fRec17 = (0.0f - (0.600000024f * fTemp3)); + fRec23[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec23[1]) - (fRec9[1] + fRec9[2])))); + fRec22[0] = ((fSlow41 * fRec22[1]) + (fSlow42 * (fRec9[1] + (fSlow43 * fRec23[0])))); + fVec5[(IOTA & 16383)] = ((0.353553385f * fRec22[0]) + 9.99999968e-21f); + float fTemp4 = (fVec5[((IOTA - iConst17) & 16383)] + (fTemp1 + (0.600000024f * fRec20[1]))); + fVec6[(IOTA & 4095)] = fTemp4; + fRec20[0] = fVec6[((IOTA - iConst18) & 4095)]; + float fRec21 = (0.0f - (0.600000024f * fTemp4)); + fRec27[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec27[1]) - (fRec5[1] + fRec5[2])))); + fRec26[0] = ((fSlow50 * fRec26[1]) + (fSlow51 * (fRec5[1] + (fSlow52 * fRec27[0])))); + fVec7[(IOTA & 32767)] = ((0.353553385f * fRec26[0]) + 9.99999968e-21f); + float fTemp5 = (fVec7[((IOTA - iConst22) & 32767)] + (fTemp1 + (0.600000024f * fRec24[1]))); + fVec8[(IOTA & 4095)] = fTemp5; + fRec24[0] = fVec8[((IOTA - iConst23) & 4095)]; + float fRec25 = (0.0f - (0.600000024f * fTemp5)); + fRec31[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec31[1]) - (fRec10[1] + fRec10[2])))); + fRec30[0] = ((fSlow59 * fRec30[1]) + (fSlow60 * (fRec10[1] + (fSlow61 * fRec31[0])))); + fVec9[(IOTA & 16383)] = ((0.353553385f * fRec30[0]) + 9.99999968e-21f); + float fTemp6 = (fVec9[((IOTA - iConst27) & 16383)] - (fTemp1 + (0.600000024f * fRec28[1]))); + fVec10[(IOTA & 2047)] = fTemp6; + fRec28[0] = fVec10[((IOTA - iConst28) & 2047)]; + float fRec29 = (0.600000024f * fTemp6); + fRec35[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec35[1]) - (fRec6[1] + fRec6[2])))); + fRec34[0] = ((fSlow68 * fRec34[1]) + (fSlow69 * (fRec6[1] + (fSlow70 * fRec35[0])))); + fVec11[(IOTA & 16383)] = ((0.353553385f * fRec34[0]) + 9.99999968e-21f); + float fTemp7 = (fVec11[((IOTA - iConst32) & 16383)] - (fTemp1 + (0.600000024f * fRec32[1]))); + fVec12[(IOTA & 4095)] = fTemp7; + fRec32[0] = fVec12[((IOTA - iConst33) & 4095)]; + float fRec33 = (0.600000024f * fTemp7); + fRec39[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec39[1]) - (fRec8[1] + fRec8[2])))); + fRec38[0] = ((fSlow77 * fRec38[1]) + (fSlow78 * (fRec8[1] + (fSlow79 * fRec39[0])))); + fVec13[(IOTA & 16383)] = ((0.353553385f * fRec38[0]) + 9.99999968e-21f); + float fTemp8 = ((fTemp1 + fVec13[((IOTA - iConst37) & 16383)]) - (0.600000024f * fRec36[1])); + fVec14[(IOTA & 4095)] = fTemp8; + fRec36[0] = fVec14[((IOTA - iConst38) & 4095)]; + float fRec37 = (0.600000024f * fTemp8); + fRec43[0] = (0.0f - (fSlow23 * ((fSlow24 * fRec43[1]) - (fRec4[1] + fRec4[2])))); + fRec42[0] = ((fSlow86 * fRec42[1]) + (fSlow87 * (fRec4[1] + (fSlow88 * fRec43[0])))); + fVec15[(IOTA & 16383)] = ((0.353553385f * fRec42[0]) + 9.99999968e-21f); + float fTemp9 = ((fVec15[((IOTA - iConst42) & 16383)] + fTemp1) - (0.600000024f * fRec40[1])); + fVec16[(IOTA & 2047)] = fTemp9; + fRec40[0] = fVec16[((IOTA - iConst43) & 2047)]; + float fRec41 = (0.600000024f * fTemp9); + float fTemp10 = (fRec41 + fRec37); + float fTemp11 = (fRec29 + (fRec33 + fTemp10)); + fRec4[0] = (fRec12[1] + (fRec16[1] + (fRec20[1] + (fRec24[1] + (fRec28[1] + (fRec32[1] + (fRec36[1] + (fRec40[1] + (fRec13 + (fRec17 + (fRec21 + (fRec25 + fTemp11)))))))))))); + fRec5[0] = ((fRec28[1] + (fRec32[1] + (fRec36[1] + (fRec40[1] + fTemp11)))) - (fRec12[1] + (fRec16[1] + (fRec20[1] + (fRec24[1] + (fRec13 + (fRec17 + (fRec25 + fRec21)))))))); + float fTemp12 = (fRec33 + fRec29); + fRec6[0] = ((fRec20[1] + (fRec24[1] + (fRec36[1] + (fRec40[1] + (fRec21 + (fRec25 + fTemp10)))))) - (fRec12[1] + (fRec16[1] + (fRec28[1] + (fRec32[1] + (fRec13 + (fRec17 + fTemp12))))))); + fRec7[0] = ((fRec12[1] + (fRec16[1] + (fRec36[1] + (fRec40[1] + (fRec13 + (fRec17 + fTemp10)))))) - (fRec20[1] + (fRec24[1] + (fRec28[1] + (fRec32[1] + (fRec21 + (fRec25 + fTemp12))))))); + float fTemp13 = (fRec41 + fRec33); + float fTemp14 = (fRec37 + fRec29); + fRec8[0] = ((fRec16[1] + (fRec24[1] + (fRec32[1] + (fRec40[1] + (fRec17 + (fRec25 + fTemp13)))))) - (fRec12[1] + (fRec20[1] + (fRec28[1] + (fRec36[1] + (fRec13 + (fRec21 + fTemp14))))))); + fRec9[0] = ((fRec12[1] + (fRec20[1] + (fRec32[1] + (fRec40[1] + (fRec13 + (fRec21 + fTemp13)))))) - (fRec16[1] + (fRec24[1] + (fRec28[1] + (fRec36[1] + (fRec17 + (fRec25 + fTemp14))))))); + float fTemp15 = (fRec41 + fRec29); + float fTemp16 = (fRec37 + fRec33); + fRec10[0] = ((fRec12[1] + (fRec24[1] + (fRec28[1] + (fRec40[1] + (fRec13 + (fRec25 + fTemp15)))))) - (fRec16[1] + (fRec20[1] + (fRec32[1] + (fRec36[1] + (fRec17 + (fRec21 + fTemp16))))))); + fRec11[0] = ((fRec16[1] + (fRec20[1] + (fRec28[1] + (fRec40[1] + (fRec17 + (fRec21 + fTemp15)))))) - (fRec12[1] + (fRec24[1] + (fRec32[1] + (fRec36[1] + (fRec13 + (fRec25 + fTemp16))))))); + float fTemp17 = (0.370000005f * (fRec5[0] + fRec6[0])); + float fTemp18 = (fSlow89 * fRec3[1]); + fRec3[0] = (fTemp17 - (fTemp18 + (fSlow9 * fRec3[2]))); + float fTemp19 = (fSlow9 * fRec3[0]); + float fTemp20 = (0.5f * ((fTemp19 + (fRec3[2] + (fTemp17 + fTemp18))) + (fSlow7 * ((fTemp19 + (fTemp18 + fRec3[2])) - fTemp17)))); + float fTemp21 = (fSlow90 * fRec2[1]); + fRec2[0] = (fTemp20 - (fTemp21 + (fSlow5 * fRec2[2]))); + float fTemp22 = (fSlow5 * fRec2[0]); + float fTemp23 = (fTemp0 * (1.0f - fRec1[0])); + float fTemp24 = (0.370000005f * (fRec5[0] - fRec6[0])); + float fTemp25 = (fSlow89 * fRec45[1]); + fRec45[0] = (fTemp24 - (fTemp25 + (fSlow9 * fRec45[2]))); + float fTemp26 = (fSlow9 * fRec45[0]); + float fTemp27 = (0.5f * ((fTemp26 + (fRec45[2] + (fTemp24 + fTemp25))) + (fSlow7 * ((fTemp26 + (fTemp25 + fRec45[2])) - fTemp24)))); + float fTemp28 = (fSlow90 * fRec44[1]); + fRec44[0] = (fTemp27 - (fTemp28 + (fSlow5 * fRec44[2]))); + float fTemp29 = (fSlow5 * fRec44[0]); + output0[i] = FAUSTFLOAT((fRec0[0] * (((0.5f * (fRec1[0] * ((fTemp22 + (fRec2[2] + (fTemp20 + fTemp21))) + (fSlow3 * ((fTemp22 + (fTemp21 + fRec2[2])) - fTemp20))))) + fTemp23) + (fTemp23 + (0.5f * (fRec1[0] * ((fTemp29 + (fRec44[2] + (fTemp27 + fTemp28))) + (fSlow3 * ((fTemp29 + (fTemp28 + fRec44[2])) - fTemp27))))))))); + IOTA = (IOTA + 1); + fRec0[1] = fRec0[0]; + fRec1[1] = fRec1[0]; + fRec15[1] = fRec15[0]; + fRec14[1] = fRec14[0]; + fRec12[1] = fRec12[0]; + fRec19[1] = fRec19[0]; + fRec18[1] = fRec18[0]; + fRec16[1] = fRec16[0]; + fRec23[1] = fRec23[0]; + fRec22[1] = fRec22[0]; + fRec20[1] = fRec20[0]; + fRec27[1] = fRec27[0]; + fRec26[1] = fRec26[0]; + fRec24[1] = fRec24[0]; + fRec31[1] = fRec31[0]; + fRec30[1] = fRec30[0]; + fRec28[1] = fRec28[0]; + fRec35[1] = fRec35[0]; + fRec34[1] = fRec34[0]; + fRec32[1] = fRec32[0]; + fRec39[1] = fRec39[0]; + fRec38[1] = fRec38[0]; + fRec36[1] = fRec36[0]; + fRec43[1] = fRec43[0]; + fRec42[1] = fRec42[0]; + fRec40[1] = fRec40[0]; + fRec4[2] = fRec4[1]; + fRec4[1] = fRec4[0]; + fRec5[2] = fRec5[1]; + fRec5[1] = fRec5[0]; + fRec6[2] = fRec6[1]; + fRec6[1] = fRec6[0]; + fRec7[2] = fRec7[1]; + fRec7[1] = fRec7[0]; + fRec8[2] = fRec8[1]; + fRec8[1] = fRec8[0]; + fRec9[2] = fRec9[1]; + fRec9[1] = fRec9[0]; + fRec10[2] = fRec10[1]; + fRec10[1] = fRec10[0]; + fRec11[2] = fRec11[1]; + fRec11[1] = fRec11[0]; + fRec3[2] = fRec3[1]; + fRec3[1] = fRec3[0]; + fRec2[2] = fRec2[1]; + fRec2[1] = fRec2[0]; + fRec45[2] = fRec45[1]; + fRec45[1] = fRec45[0]; + fRec44[2] = fRec44[1]; + fRec44[1] = fRec44[0]; + } + } + +}; + +#endif |