diff --git a/src/yaw-tab/DistrhoPluginInfo.h b/src/yaw-tab/DistrhoPluginInfo.h index 4c22069..28dc136 100644 --- a/src/yaw-tab/DistrhoPluginInfo.h +++ b/src/yaw-tab/DistrhoPluginInfo.h @@ -17,9 +17,9 @@ #ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED #define DISTRHO_PLUGIN_INFO_H_INCLUDED -#define DISTRHO_PLUGIN_BRAND "DISTRHO" -#define DISTRHO_PLUGIN_NAME "Info" -#define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/Info" +#define DISTRHO_PLUGIN_BRAND "yaw-audio" +#define DISTRHO_PLUGIN_NAME "yaw-tab" +#define DISTRHO_PLUGIN_URI "https://yaw.man/plugins/yaw-tab" #define DISTRHO_PLUGIN_HAS_UI 1 #define DISTRHO_PLUGIN_IS_RT_SAFE 1 diff --git a/src/yaw-tab/dsp.cpp b/src/yaw-tab/dsp.cpp index e69de29..2534d32 100644 --- a/src/yaw-tab/dsp.cpp +++ b/src/yaw-tab/dsp.cpp @@ -0,0 +1,277 @@ +#include "DistrhoPlugin.hpp" + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------------------------------------------- + +/** + Plugin to show how to get some basic information sent to the UI. + */ + class InfoExamplePlugin : public Plugin +{ +public: + InfoExamplePlugin() + : Plugin(kParameterCount, 0, 0) + { + // clear all parameters + std::memset(fParameters, 0, sizeof(float) * kParameterCount); + + // we can know some things right at the start + fParameters[kParameterBufferSize] = getBufferSize(); + fParameters[kParameterCanRequestParameterValueChanges] = canRequestParameterValueChanges(); + } + +protected: + /* -------------------------------------------------------------------------------------------------------- + * Information */ + + /** + Get the plugin label. + This label is a short restricted name consisting of only _, a-z, A-Z and 0-9 characters. + */ + const char* getLabel() const override + { + return "Info"; + } + + /** + Get an extensive comment/description about the plugin. + */ + const char* getDescription() const override + { + return "Plugin to show how to get some basic information sent to the UI."; + } + + /** + Get the plugin author/maker. + */ + const char* getMaker() const override + { + return "DISTRHO"; + } + + /** + Get the plugin homepage. + */ + const char* getHomePage() const override + { + return "https://github.com/DISTRHO/DPF"; + } + + /** + Get the plugin license name (a single line of text). + For commercial plugins this should return some short copyright information. + */ + const char* getLicense() const override + { + return "ISC"; + } + + /** + Get the plugin version, in hexadecimal. + */ + uint32_t getVersion() const override + { + return d_version(1, 0, 0); + } + + /** + Get the plugin unique Id. + This value is used by LADSPA, DSSI and VST plugin formats. + */ + int64_t getUniqueId() const override + { + return d_cconst('d', 'N', 'f', 'o'); + } + + /* -------------------------------------------------------------------------------------------------------- + * Init */ + + /** + Initialize the parameter @a index. + This function will be called once, shortly after the plugin is created. + */ + void initParameter(uint32_t index, Parameter& parameter) override + { + parameter.hints = kParameterIsAutomable | kParameterIsOutput; + parameter.ranges.def = 0.0f; + parameter.ranges.min = 0.0f; + parameter.ranges.max = 16777216.0f; + + switch (index) + { + case kParameterBufferSize: + parameter.name = "BufferSize"; + parameter.symbol = "buffer_size"; + break; + case kParameterCanRequestParameterValueChanges: + parameter.name = "Parameter Changes"; + parameter.symbol = "parameter_changes"; + parameter.hints |= kParameterIsBoolean; + parameter.ranges.max = 1.0f; + break; + case kParameterTimePlaying: + parameter.name = "TimePlaying"; + parameter.symbol = "time_playing"; + parameter.hints |= kParameterIsBoolean; + parameter.ranges.max = 1.0f; + break; + case kParameterTimeFrame: + parameter.name = "TimeFrame"; + parameter.symbol = "time_frame"; + break; + case kParameterTimeValidBBT: + parameter.name = "TimeValidBBT"; + parameter.symbol = "time_validbbt"; + parameter.hints |= kParameterIsBoolean; + parameter.ranges.max = 1.0f; + break; + case kParameterTimeBar: + parameter.name = "TimeBar"; + parameter.symbol = "time_bar"; + break; + case kParameterTimeBeat: + parameter.name = "TimeBeat"; + parameter.symbol = "time_beat"; + break; + case kParameterTimeTick: + parameter.name = "TimeTick"; + parameter.symbol = "time_tick"; + break; + case kParameterTimeBarStartTick: + parameter.name = "TimeBarStartTick"; + parameter.symbol = "time_barstarttick"; + break; + case kParameterTimeBeatsPerBar: + parameter.name = "TimeBeatsPerBar"; + parameter.symbol = "time_beatsperbar"; + break; + case kParameterTimeBeatType: + parameter.name = "TimeBeatType"; + parameter.symbol = "time_beattype"; + break; + case kParameterTimeTicksPerBeat: + parameter.name = "TimeTicksPerBeat"; + parameter.symbol = "time_ticksperbeat"; + break; + case kParameterTimeBeatsPerMinute: + parameter.name = "TimeBeatsPerMinute"; + parameter.symbol = "time_beatsperminute"; + break; + } + } + + /* -------------------------------------------------------------------------------------------------------- + * Internal data */ + + /** + Get the current value of a parameter. + The host may call this function from any context, including realtime processing. + */ + float getParameterValue(uint32_t index) const override + { + return fParameters[index]; + + } + + /** + Change a parameter value. + The host may call this function from any context, including realtime processing. + When a parameter is marked as automable, you must ensure no non-realtime operations are performed. + @note This function will only be called for parameter inputs. + */ + void setParameterValue(uint32_t, float) override + { + // this is only called for input parameters, which we have none of. + } + + /* -------------------------------------------------------------------------------------------------------- + * Audio/MIDI Processing */ + + /** + Run/process function for plugins without MIDI input. + @note Some parameters might be null if there are no audio inputs or outputs. + */ + void run(const float** inputs, float** outputs, uint32_t frames) override + { + /** + This plugin does nothing, it just demonstrates information usage. + So here we directly copy inputs over outputs, leaving the audio untouched. + We need to be careful in case the host re-uses the same buffer for both inputs and outputs. + */ + if (outputs[0] != inputs[0]) + std::memcpy(outputs[0], inputs[0], sizeof(float) * frames); + + if (outputs[1] != inputs[1]) + std::memcpy(outputs[1], inputs[1], sizeof(float) * frames); + + // get time position + const TimePosition& timePos(getTimePosition()); + + // set basic values + fParameters[kParameterTimePlaying] = timePos.playing ? 1.0f : 0.0f; + fParameters[kParameterTimeFrame] = timePos.frame; + fParameters[kParameterTimeValidBBT] = timePos.bbt.valid ? 1.0f : 0.0f; + + // set bbt + if (timePos.bbt.valid) + { + fParameters[kParameterTimeBar] = timePos.bbt.bar; + fParameters[kParameterTimeBeat] = timePos.bbt.beat; + fParameters[kParameterTimeTick] = timePos.bbt.tick; + fParameters[kParameterTimeBarStartTick] = timePos.bbt.barStartTick; + fParameters[kParameterTimeBeatsPerBar] = timePos.bbt.beatsPerBar; + fParameters[kParameterTimeBeatType] = timePos.bbt.beatType; + fParameters[kParameterTimeTicksPerBeat] = timePos.bbt.ticksPerBeat; + fParameters[kParameterTimeBeatsPerMinute] = timePos.bbt.beatsPerMinute; + } + else + { + fParameters[kParameterTimeBar] = 0.0f; + fParameters[kParameterTimeBeat] = 0.0f; + fParameters[kParameterTimeTick] = 0.0f; + fParameters[kParameterTimeBarStartTick] = 0.0f; + fParameters[kParameterTimeBeatsPerBar] = 0.0f; + fParameters[kParameterTimeBeatType] = 0.0f; + fParameters[kParameterTimeTicksPerBeat] = 0.0f; + fParameters[kParameterTimeBeatsPerMinute] = 0.0f; + } + } + + /* -------------------------------------------------------------------------------------------------------- + * Callbacks (optional) */ + + /** + Optional callback to inform the plugin about a buffer size change. + This function will only be called when the plugin is deactivated. + @note This value is only a hint! + Hosts might call run() with a higher or lower number of frames. + */ + void bufferSizeChanged(uint32_t newBufferSize) override + { + fParameters[kParameterBufferSize] = newBufferSize; + } + + // ------------------------------------------------------------------------------------------------------- + +private: + // Parameters + float fParameters[kParameterCount]; + + /** + Set our plugin class as non-copyable and add a leak detector just in case. + */ + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(InfoExamplePlugin) +}; + +/* ------------------------------------------------------------------------------------------------------------ + * Plugin entry point, called by DPF to create a new plugin instance. */ + +Plugin* createPlugin() +{ + return new InfoExamplePlugin(); +} + +// ----------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO diff --git a/src/yaw-tab/ui.cpp b/src/yaw-tab/ui.cpp index e69de29..0834612 100644 --- a/src/yaw-tab/ui.cpp +++ b/src/yaw-tab/ui.cpp @@ -0,0 +1,279 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2021 Filipe Coelho + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "DistrhoUI.hpp" + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------------------------------------------- + +class InfoExampleUI : public UI +{ + static const uint kInitialWidth = 405; + static const uint kInitialHeight = 256; + +public: + InfoExampleUI() + : UI(kInitialWidth, kInitialHeight), + fSampleRate(getSampleRate()), + fResizable(isResizable()), + fScale(1.0f), + fScaleFactor(getScaleFactor()) + { + std::memset(fParameters, 0, sizeof(float) * kParameterCount); + std::memset(fStrBuf, 0, sizeof(char) * (0xff + 1)); + +#ifdef DGL_NO_SHARED_RESOURCES + createFontFromFile("sans", "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf"); +#else + loadSharedResources(); +#endif + + setGeometryConstraints(kInitialWidth, kInitialHeight, true); + } + +protected: + /* -------------------------------------------------------------------------------------------------------- + * DSP/Plugin Callbacks */ + + /** + A parameter has changed on the plugin side. + This is called by the host to inform the UI about parameter changes. + */ + void parameterChanged(uint32_t index, float value) override + { + // some hosts send parameter change events for output parameters even when nothing changed + // we catch that here in order to prevent excessive repaints + if (d_isEqual(fParameters[index], value)) + return; + + fParameters[index] = value; + repaint(); + } + + /* -------------------------------------------------------------------------------------------------------- + * DSP/Plugin Callbacks (optional) */ + + /** + Optional callback to inform the UI about a sample rate change on the plugin side. + */ + void sampleRateChanged(double newSampleRate) override + { + fSampleRate = newSampleRate; + repaint(); + } + + /* -------------------------------------------------------------------------------------------------------- + * Widget Callbacks */ + + /** + The NanoVG drawing function. + */ + void onNanoDisplay() override + { + const float lineHeight = 20 * fScale; + + fontSize(15.0f * fScale); + textLineHeight(lineHeight); + + float x = 0.0f * fScale; + float y = 15.0f * fScale; + + // buffer size + drawLeft(x, y, "Buffer Size:"); + drawRight(x, y, getTextBufInt(fParameters[kParameterBufferSize])); + y += lineHeight; + + // sample rate + drawLeft(x, y, "Sample Rate:"); + drawRight(x, y, getTextBufFloat(fSampleRate)); + y += lineHeight; + + // separator + y += lineHeight; + + // time stuff + drawLeft(x, y, "Playing:"); + drawRight(x, y, (fParameters[kParameterTimePlaying] > 0.5f) ? "Yes" : "No"); + y += lineHeight; + + drawLeft(x, y, "Frame:"); + drawRight(x, y, getTextBufInt(fParameters[kParameterTimeFrame])); + y += lineHeight; + + drawLeft(x, y, "Time:"); + drawRight(x, y, getTextBufTime(fParameters[kParameterTimeFrame])); + y += lineHeight; + + // separator + y += lineHeight; + + // param changes + drawLeft(x, y, "Param Changes:", 20); + drawRight(x, y, (fParameters[kParameterCanRequestParameterValueChanges] > 0.5f) ? "Yes" : "No", 40); + y += lineHeight; + + // resizable + drawLeft(x, y, "Host resizable:", 20); + drawRight(x, y, fResizable ? "Yes" : "No", 40); + y += lineHeight; + + // host scale factor + drawLeft(x, y, "Host scale factor:", 20); + drawRight(x, y, getTextBufFloat(fScaleFactor), 40); + y += lineHeight; + + // BBT + x = 200.0f * fScale; + y = 15.0f * fScale; + + const bool validBBT(fParameters[kParameterTimeValidBBT] > 0.5f); + drawLeft(x, y, "BBT Valid:"); + drawRight(x, y, validBBT ? "Yes" : "No"); + y += lineHeight; + + if (!validBBT) + return; + + drawLeft(x, y, "Bar:"); + drawRight(x, y, getTextBufInt(fParameters[kParameterTimeBar])); + y += lineHeight; + + drawLeft(x, y, "Beat:"); + drawRight(x, y, getTextBufInt(fParameters[kParameterTimeBeat])); + y += lineHeight; + + drawLeft(x, y, "Tick:"); + drawRight(x, y, getTextBufFloatExtra(fParameters[kParameterTimeTick])); + y += lineHeight; + + drawLeft(x, y, "Bar Start Tick:"); + drawRight(x, y, getTextBufFloat(fParameters[kParameterTimeBarStartTick])); + y += lineHeight; + + drawLeft(x, y, "Beats Per Bar:"); + drawRight(x, y, getTextBufFloat(fParameters[kParameterTimeBeatsPerBar])); + y += lineHeight; + + drawLeft(x, y, "Beat Type:"); + drawRight(x, y, getTextBufFloat(fParameters[kParameterTimeBeatType])); + y += lineHeight; + + drawLeft(x, y, "Ticks Per Beat:"); + drawRight(x, y, getTextBufFloat(fParameters[kParameterTimeTicksPerBeat])); + y += lineHeight; + + drawLeft(x, y, "BPM:"); + drawRight(x, y, getTextBufFloat(fParameters[kParameterTimeBeatsPerMinute])); + y += lineHeight; + } + + void onResize(const ResizeEvent& ev) override + { + fScale = static_cast(ev.size.getHeight()) / static_cast(kInitialHeight); + + UI::onResize(ev); + } + + void uiScaleFactorChanged(const double scaleFactor) override + { + fScaleFactor = scaleFactor; + } + + // ------------------------------------------------------------------------------------------------------- + +private: + // Parameters + float fParameters[kParameterCount]; + double fSampleRate; + + // UI stuff + bool fResizable; + float fScale; // our internal scaling + double fScaleFactor; // host reported scale factor + + // temp buf for text + char fStrBuf[0xff + 1]; + + // helpers for putting text into fStrBuf and returning it + const char* getTextBufInt(const int value) + { + std::snprintf(fStrBuf, 0xff, "%i", value); + return fStrBuf; + } + + const char* getTextBufFloat(const float value) + { + std::snprintf(fStrBuf, 0xff, "%.1f", value); + return fStrBuf; + } + + const char* getTextBufFloatExtra(const float value) + { + std::snprintf(fStrBuf, 0xff, "%.2f", value + 0.001f); + return fStrBuf; + } + + const char* getTextBufTime(const uint64_t frame) + { + const uint32_t time = frame / uint64_t(fSampleRate); + const uint32_t secs = time % 60; + const uint32_t mins = (time / 60) % 60; + const uint32_t hrs = (time / 3600) % 60; + std::snprintf(fStrBuf, 0xff, "%02i:%02i:%02i", hrs, mins, secs); + return fStrBuf; + } + + // helpers for drawing text + void drawLeft(float x, const float y, const char* const text, const int offset = 0) + { + const float width = (100.0f + offset) * fScale; + x += offset * fScale; + beginPath(); + fillColor(200, 200, 200); + textAlign(ALIGN_RIGHT | ALIGN_TOP); + textBox(x, y, width, text); + closePath(); + } + + void drawRight(float x, const float y, const char* const text, const int offset = 0) + { + const float width = (100.0f + offset) * fScale; + x += offset * fScale; + beginPath(); + fillColor(255, 255, 255); + textAlign(ALIGN_LEFT | ALIGN_TOP); + textBox(x + (105 * fScale), y, width, text); + closePath(); + } + + /** + Set our UI class as non-copyable and add a leak detector just in case. + */ + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(InfoExampleUI) +}; + +/* ------------------------------------------------------------------------------------------------------------ + * UI entry point, called by DPF to create a new UI instance. */ + +UI* createUI() +{ + return new InfoExampleUI(); +} + +// ----------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO