Files
lmms/plugins/Xpressive/Xpressive.cpp
Levin Oehlmann f742710758 Macro cleanup (#6095)
Summary:

* `NULL` -> `nullptr`
* `gui` -> Function `getGUI()`
* `pluginFactory` -> Function `getPluginFactory()`
* `assert` (redefinition) -> using `NDEBUG` instead, which standard `assert` respects.
* `powf` (C stdlib symbol clash) -> removed and all expansions replaced with calls to `std::pow`.
* `exp10` (nonstandard function symbol clash) -> removed and all expansions replaced with calls to `std::pow`.
* `PATH_DEV_DSP` -> File-scope QString of identical name and value.
* `VST_SNC_SHM_KEY_FILE` -> constexpr char* with identical name and value.
* `MM_ALLOC` and `MM_FREE` -> Functions with identical name and implementation.
* `INVAL`, `OUTVAL`, etc. for automation nodes -> Functions with identical names and implementations.
* BandLimitedWave.h: All integer constant macros replaced with constexpr ints of same name and value.
* `FAST_RAND_MAX` -> constexpr int of same name and value.
* `QSTR_TO_STDSTR` -> Function with identical name and equivalent implementation.
* `CCONST` -> constexpr function template with identical name and implementation.
* `F_OPEN_UTF8` -> Function with identical name and equivalent implementation.
* `LADSPA_PATH_SEPARATOR` -> constexpr char with identical name and value.
* `UI_CTRL_KEY` -> constexpr char* with identical name and value.
* `ALIGN_SIZE` -> Renamed to `LMMS_ALIGN_SIZE` and converted from a macro to a constexpr size_t.
* `JACK_MIDI_BUFFER_MAX` -> constexpr size_t with identical name and value.
* versioninfo.h: `PLATFORM`, `MACHINE` and `COMPILER_VERSION` -> prefixed with `LMMS_BUILDCONF_` and converted from macros to constexpr char* literals.
* Header guard _OSCILLOSCOPE -> renamed to OSCILLOSCOPE_H
* Header guard _TIME_DISPLAY_WIDGET -> renamed to TIME_DISPLAY_WIDGET_H
* C-style typecasts in DrumSynth.cpp have been replaced with `static_cast`.
* constexpr numerical constants are initialized with assignment notation instead of curly brace intializers.
* In portsmf, `Alg_seq::operator[]` will throw an exception instead of returning null if the operator index is out of range.

Additionally, in many places, global constants that were declared as `const T foo = bar;` were changed from const to constexpr, leaving them const and making them potentially evaluable at compile time.

Some macros that only appeared in single source files and were unused in those files have been removed entirely.
2021-09-30 18:01:27 +02:00

904 lines
32 KiB
C++

/*
* Xpressive.cpp - Instrument which uses a mathematical formula parser
*
* Copyright (c) 2016-2017 Orr Dvori
*
* This file is part of LMMS - https://lmms.io
*
* This program 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 2 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 (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#include "Xpressive.h"
#include <QDomElement>
#include "AudioEngine.h"
#include "Engine.h"
#include "Graph.h"
#include "GuiApplication.h"
#include "InstrumentTrack.h"
#include "Knob.h"
#include "LedCheckbox.h"
#include "MainWindow.h"
#include "NotePlayHandle.h"
#include "Oscillator.h"
#include "PixmapButton.h"
#include "Song.h"
#include "SubWindow.h"
#include "ToolTip.h"
#include "base64.h"
#include "lmms_constants.h"
#include "embed.h"
#include "ExprSynth.h"
#include "plugin_export.h"
extern "C" {
Plugin::Descriptor PLUGIN_EXPORT xpressive_plugin_descriptor = { STRINGIFY(
PLUGIN_NAME), "Xpressive", QT_TRANSLATE_NOOP("PluginBrowser",
"Mathematical expression parser"), "Orr Dvori", 0x0100,
Plugin::Instrument, new PluginPixmapLoader("logo"), nullptr, nullptr };
}
/*
* nice test:
O1 -> trianglew(2t*f)*(0.5+0.5sinew(12*A1*t+0.5))+sinew(t*f)*(0.5+0.5sinew(12*A1*t))
O2 -> trianglew(2t*f)*(0.5+0.5sinew(12*A1*t))+sinew(t*f)*(0.5+0.5sinew(12*A1*t+0.5))
*/
/***********************************************************************
*
* class Xpressive
*
* lmms - plugin
*
***********************************************************************/
#define GRAPH_LENGTH 4096
Xpressive::Xpressive(InstrumentTrack* instrument_track) :
Instrument(instrument_track, &xpressive_plugin_descriptor),
m_graphO1(-1.0f, 1.0f, 360, this),
m_graphO2(-1.0f, 1.0f, 360, this),
m_graphW1(-1.0f, 1.0f, GRAPH_LENGTH, this),
m_graphW2(-1.0f, 1.0f, GRAPH_LENGTH, this),
m_graphW3(-1.0f, 1.0f, GRAPH_LENGTH, this),
m_rawgraphW1(-1.0f, 1.0f, GRAPH_LENGTH, this),
m_rawgraphW2(-1.0f, 1.0f, GRAPH_LENGTH, this),
m_rawgraphW3(-1.0f, 1.0f, GRAPH_LENGTH, this),
m_selectedGraph(0, 0, 6, this, tr("Selected graph")),
m_parameterA1(1, -1.0f, 1.0f, 0.01f, this, tr("A1")),
m_parameterA2(1, -1.0f, 1.0f, 0.01f, this, tr("A2")),
m_parameterA3(1, -1.0f, 1.0f, 0.01f, this, tr("A3")),
m_smoothW1(0, 0.0f, 70.0f, 1.0f, this, tr("W1 smoothing")),
m_smoothW2(0, 0.0f, 70.0f, 1.0f, this, tr("W2 smoothing")),
m_smoothW3(0, 0.0f, 70.0f, 1.0f, this, tr("W3 smoothing")),
m_interpolateW1(false, this),
m_interpolateW2(false, this),
m_interpolateW3(false, this),
m_panning1( 1, -1.0f, 1.0f, 0.01f, this, tr("Panning 1")),
m_panning2(-1, -1.0f, 1.0f, 0.01f, this, tr("Panning 2")),
m_relTransition(50.0f, 0.0f, 500.0f, 1.0f, this, tr("Rel trans")),
m_W1(GRAPH_LENGTH),
m_W2(GRAPH_LENGTH),
m_W3(GRAPH_LENGTH),
m_exprValid(false, this)
{
m_outputExpression[0]="sinew(integrate(f*(1+0.05sinew(12t))))*(2^(-(1.1+A2)*t)*(0.4+0.1(1+A3)+0.4sinew((2.5+2A1)t))^2)";
m_outputExpression[1]="expw(integrate(f*atan(500t)*2/pi))*0.5+0.12";
}
Xpressive::~Xpressive() {
}
void Xpressive::saveSettings(QDomDocument & _doc, QDomElement & _this) {
// Save plugin version
_this.setAttribute("version", "0.1");
_this.setAttribute("O1", QString(m_outputExpression[0]));
_this.setAttribute("O2", QString(m_outputExpression[1]));
_this.setAttribute("W1", QString(m_wavesExpression[0]));
// Save sample shape base64-encoded
QString sampleString;
base64::encode( (const char*)m_rawgraphW1.samples(),
m_rawgraphW1.length() * sizeof(float), sampleString );
_this.setAttribute( "W1sample", sampleString );
_this.setAttribute("W2", QString(m_wavesExpression[1]));
base64::encode( (const char*)m_rawgraphW2.samples(),
m_rawgraphW2.length() * sizeof(float), sampleString );
_this.setAttribute( "W2sample", sampleString );
_this.setAttribute("W3", QString(m_wavesExpression[2]));
base64::encode( (const char*)m_rawgraphW3.samples(),
m_rawgraphW3.length() * sizeof(float), sampleString );
_this.setAttribute( "W3sample", sampleString );
m_smoothW1.saveSettings(_doc,_this,"smoothW1");
m_smoothW2.saveSettings(_doc,_this,"smoothW2");
m_smoothW3.saveSettings(_doc,_this,"smoothW3");
m_interpolateW1.saveSettings(_doc,_this,"interpolateW1");
m_interpolateW2.saveSettings(_doc,_this,"interpolateW2");
m_interpolateW3.saveSettings(_doc,_this,"interpolateW3");
m_parameterA1.saveSettings(_doc,_this,"A1");
m_parameterA2.saveSettings(_doc,_this,"A2");
m_parameterA3.saveSettings(_doc,_this,"A3");
m_panning1.saveSettings(_doc,_this,"PAN1");
m_panning2.saveSettings(_doc,_this,"PAN2");
m_relTransition.saveSettings(_doc,_this,"RELTRANS");
}
void Xpressive::loadSettings(const QDomElement & _this) {
m_outputExpression[0]=_this.attribute( "O1").toLatin1();
m_outputExpression[1]=_this.attribute( "O2").toLatin1();
m_wavesExpression[0]=_this.attribute( "W1").toLatin1();
m_wavesExpression[1]=_this.attribute( "W2").toLatin1();
m_wavesExpression[2]=_this.attribute( "W3").toLatin1();
m_smoothW1.loadSettings(_this,"smoothW1");
m_smoothW2.loadSettings(_this,"smoothW2");
m_smoothW3.loadSettings(_this,"smoothW3");
m_interpolateW1.loadSettings(_this,"interpolateW1");
m_interpolateW2.loadSettings(_this,"interpolateW2");
m_interpolateW3.loadSettings(_this,"interpolateW3");
m_parameterA1.loadSettings(_this,"A1");
m_parameterA2.loadSettings(_this,"A2");
m_parameterA3.loadSettings(_this,"A3");
m_panning1.loadSettings(_this,"PAN1");
m_panning2.loadSettings(_this,"PAN2");
m_relTransition.loadSettings(_this,"RELTRANS");
int size = 0;
char * dst = 0;
base64::decode( _this.attribute( "W1sample"), &dst, &size );
m_rawgraphW1.setSamples( (float*) dst );
delete[] dst;
base64::decode( _this.attribute( "W2sample"), &dst, &size );
m_rawgraphW2.setSamples( (float*) dst );
delete[] dst;
base64::decode( _this.attribute( "W3sample"), &dst, &size );
m_rawgraphW3.setSamples( (float*) dst );
delete[] dst;
smooth(m_smoothW1.value(),&m_rawgraphW1,&m_graphW1);
smooth(m_smoothW2.value(),&m_rawgraphW2,&m_graphW2);
smooth(m_smoothW3.value(),&m_rawgraphW3,&m_graphW3);
m_W1.copyFrom(&m_graphW1);
m_W2.copyFrom(&m_graphW2);
m_W3.copyFrom(&m_graphW3);
}
QString Xpressive::nodeName() const {
return (xpressive_plugin_descriptor.name);
}
void Xpressive::playNote(NotePlayHandle* nph, sampleFrame* working_buffer) {
m_A1=m_parameterA1.value();
m_A2=m_parameterA2.value();
m_A3=m_parameterA3.value();
if (nph->totalFramesPlayed() == 0 || nph->m_pluginData == nullptr) {
ExprFront *exprO1 = new ExprFront(m_outputExpression[0].constData(),Engine::audioEngine()->processingSampleRate());//give the "last" function a whole second
ExprFront *exprO2 = new ExprFront(m_outputExpression[1].constData(),Engine::audioEngine()->processingSampleRate());
auto init_expression_step1 = [this, nph](ExprFront* e) { //lambda function to init exprO1 and exprO2
//add the constants and the variables to the expression.
e->add_constant("key", nph->key());//the key that was pressed.
e->add_constant("bnote", nph->instrumentTrack()->baseNote()); // the base note
e->add_constant("srate", Engine::audioEngine()->processingSampleRate());// sample rate of the audio engine
e->add_constant("v", nph->getVolume() / 255.0); //volume of the note.
e->add_constant("tempo", Engine::getSong()->getTempo());//tempo of the song.
e->add_variable("A1", m_A1);//A1,A2,A3: general purpose input controls.
e->add_variable("A2", m_A2);
e->add_variable("A3", m_A3);
};
init_expression_step1(exprO1);
init_expression_step1(exprO2);
m_W1.setInterpolate(m_interpolateW1.value());//set interpolation according to the user selection.
m_W2.setInterpolate(m_interpolateW2.value());
m_W3.setInterpolate(m_interpolateW3.value());
nph->m_pluginData = new ExprSynth(&m_W1, &m_W2, &m_W3, exprO1, exprO2, nph,
Engine::audioEngine()->processingSampleRate(), &m_panning1, &m_panning2, m_relTransition.value());
}
ExprSynth *ps = static_cast<ExprSynth*>(nph->m_pluginData);
const fpp_t frames = nph->framesLeftForCurrentPeriod();
const f_cnt_t offset = nph->noteOffset();
ps->renderOutput(frames, working_buffer + offset);
instrumentTrack()->processAudioBuffer(working_buffer, frames + offset, nph);
}
void Xpressive::deleteNotePluginData(NotePlayHandle* nph) {
delete static_cast<ExprSynth *>(nph->m_pluginData);
}
PluginView * Xpressive::instantiateView(QWidget* parent) {
return (new XpressiveView(this, parent));
}
class XpressiveKnob: public Knob {
public:
void setStyle()
{
setFixedSize(29, 29);
setCenterPointX(14.5);
setCenterPointY(14.5);
setInnerRadius(4);
setOuterRadius(9);
setTotalAngle(300.0);
setLineWidth(3);
}
XpressiveKnob(QWidget * _parent, const QString & _name) :
Knob(knobStyled, _parent,_name) {
setStyle();
}
XpressiveKnob(QWidget * _parent) :
Knob(knobStyled, _parent) {
setStyle();
}
};
XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) :
InstrumentViewFixedSize(_instrument, _parent)
{
const int COL_KNOBS = 191;
const int BASE_START = 2;
const int ROW_KNOBSA1 = BASE_START;
const int ROW_KNOBSA2 = BASE_START + 32;
const int ROW_KNOBSA3 = BASE_START + 64;
const int ROW_KNOBSP1 = BASE_START + 100;
const int ROW_KNOBSP2 = BASE_START + 100 + 32;
const int ROW_KNOBREL = BASE_START + 100 + 64;
const int ROW_BTN = BASE_START + 85;
const int ROW_WAVEBTN = BASE_START + 233 - 26;
const int EXPR_TEXT_Y = BASE_START + 102;
const int EXPR_TEXT_H = 90;
setAutoFillBackground(true);
QPalette pal;
pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork"));
setPalette(pal);
m_graph = new Graph(this, Graph::LinearStyle, 180, 81);
m_graph->move(3, BASE_START + 1);
m_graph->setAutoFillBackground(true);
m_graph->setGraphColor(QColor(255, 255, 255));
m_graph->setEnabled(false);
ToolTip::add(m_graph, tr("Draw your own waveform here "
"by dragging your mouse on this graph."));
pal = QPalette();
pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("wavegraph"));
m_graph->setPalette(pal);
PixmapButton * m_w1Btn;
PixmapButton * m_w2Btn;
PixmapButton * m_w3Btn;
PixmapButton * m_o1Btn;
PixmapButton * m_o2Btn;
PixmapButton * m_helpBtn;
m_w1Btn = new PixmapButton(this, nullptr);
m_w1Btn->move(3, ROW_BTN);
m_w1Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("w1_active"));
m_w1Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("w1_inactive"));
ToolTip::add(m_w1Btn, tr("Select oscillator W1"));
m_w2Btn = new PixmapButton(this, nullptr);
m_w2Btn->move(26, ROW_BTN);
m_w2Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("w2_active"));
m_w2Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("w2_inactive"));
ToolTip::add(m_w2Btn, tr("Select oscillator W2"));
m_w3Btn = new PixmapButton(this, nullptr);
m_w3Btn->move(49, ROW_BTN);
m_w3Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("w3_active"));
m_w3Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("w3_inactive"));
ToolTip::add(m_w3Btn, tr("Select oscillator W3"));
m_o1Btn = new PixmapButton(this, nullptr);
m_o1Btn->move(79, ROW_BTN);
m_o1Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("o1_active"));
m_o1Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("o1_inactive"));
ToolTip::add(m_o1Btn, tr("Select output O1"));
m_o2Btn = new PixmapButton(this, nullptr);
m_o2Btn->move(101, ROW_BTN);
m_o2Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("o2_active"));
m_o2Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("o2_inactive"));
ToolTip::add(m_o2Btn, tr("Select output O2"));
m_helpBtn = new PixmapButton(this, nullptr);
m_helpBtn->move(133, ROW_BTN);
m_helpBtn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("help_active"));
m_helpBtn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("help_inactive"));
ToolTip::add(m_helpBtn, tr("Open help window"));
m_selectedGraphGroup = new automatableButtonGroup(this);
m_selectedGraphGroup->addButton(m_w1Btn);
m_selectedGraphGroup->addButton(m_w2Btn);
m_selectedGraphGroup->addButton(m_w3Btn);
m_selectedGraphGroup->addButton(m_o1Btn);
m_selectedGraphGroup->addButton(m_o2Btn);
Xpressive *e = castModel<Xpressive>();
m_selectedGraphGroup->setModel(&e->selectedGraph());
m_sinWaveBtn = new PixmapButton(this, tr("Sine wave"));
m_sinWaveBtn->move(4, ROW_WAVEBTN);
m_sinWaveBtn->setActiveGraphic(embed::getIconPixmap("sin_wave_active"));
m_sinWaveBtn->setInactiveGraphic(embed::getIconPixmap("sin_wave_inactive"));
ToolTip::add(m_sinWaveBtn, tr("Sine wave"));
m_moogWaveBtn = new PixmapButton(this, tr("Moog-saw wave"));
m_moogWaveBtn->move(4, ROW_WAVEBTN-14);
m_moogWaveBtn->setActiveGraphic(
embed::getIconPixmap( "moog_saw_wave_active" ) );
m_moogWaveBtn->setInactiveGraphic(embed::getIconPixmap("moog_saw_wave_inactive"));
ToolTip::add(m_moogWaveBtn, tr("Moog-saw wave"));
m_expWaveBtn = new PixmapButton(this, tr("Exponential wave"));
m_expWaveBtn->move(4 +14, ROW_WAVEBTN-14);
m_expWaveBtn->setActiveGraphic(embed::getIconPixmap( "exp_wave_active" ) );
m_expWaveBtn->setInactiveGraphic(embed::getIconPixmap( "exp_wave_inactive" ) );
ToolTip::add(m_expWaveBtn, tr("Exponential wave"));
m_sawWaveBtn = new PixmapButton(this, tr("Saw wave"));
m_sawWaveBtn->move(4 + 14 * 2, ROW_WAVEBTN-14);
m_sawWaveBtn->setActiveGraphic(embed::getIconPixmap("saw_wave_active"));
m_sawWaveBtn->setInactiveGraphic(embed::getIconPixmap("saw_wave_inactive"));
ToolTip::add(m_sawWaveBtn, tr("Saw wave"));
m_usrWaveBtn = new PixmapButton(this, tr("User-defined wave"));
m_usrWaveBtn->move(4 + 14 * 3, ROW_WAVEBTN-14);
m_usrWaveBtn->setActiveGraphic(embed::getIconPixmap("usr_wave_active"));
m_usrWaveBtn->setInactiveGraphic(embed::getIconPixmap("usr_wave_inactive"));
ToolTip::add(m_usrWaveBtn, tr("User-defined wave"));
m_triangleWaveBtn = new PixmapButton(this, tr("Triangle wave"));
m_triangleWaveBtn->move(4 + 14, ROW_WAVEBTN);
m_triangleWaveBtn->setActiveGraphic(
embed::getIconPixmap("triangle_wave_active"));
m_triangleWaveBtn->setInactiveGraphic(
embed::getIconPixmap("triangle_wave_inactive"));
ToolTip::add(m_triangleWaveBtn, tr("Triangle wave"));
m_sqrWaveBtn = new PixmapButton(this, tr("Square wave"));
m_sqrWaveBtn->move(4 + 14 * 2, ROW_WAVEBTN);
m_sqrWaveBtn->setActiveGraphic(embed::getIconPixmap("square_wave_active"));
m_sqrWaveBtn->setInactiveGraphic(
embed::getIconPixmap("square_wave_inactive"));
ToolTip::add(m_sqrWaveBtn, tr("Square wave"));
m_whiteNoiseWaveBtn = new PixmapButton(this, tr("White noise"));
m_whiteNoiseWaveBtn->move(4 + 14 * 3, ROW_WAVEBTN);
m_whiteNoiseWaveBtn->setActiveGraphic(
embed::getIconPixmap("white_noise_wave_active"));
m_whiteNoiseWaveBtn->setInactiveGraphic(
embed::getIconPixmap("white_noise_wave_inactive"));
ToolTip::add(m_whiteNoiseWaveBtn, tr("White noise"));
m_waveInterpolate = new LedCheckBox("Interpolate", this, tr("WaveInterpolate"),
LedCheckBox::Green);
m_waveInterpolate->move(2, 230);
m_expressionValidToggle = new LedCheckBox("", this, tr("ExpressionValid"),
LedCheckBox::Red);
m_expressionValidToggle->move(168, EXPR_TEXT_Y+EXPR_TEXT_H-2);
m_expressionValidToggle->setEnabled( false );
m_expressionEditor = new QPlainTextEdit(this);
m_expressionEditor->move(3, EXPR_TEXT_Y);
m_expressionEditor->resize(180, EXPR_TEXT_H);
m_generalPurposeKnob[0] = new XpressiveKnob(this,"A1");
m_generalPurposeKnob[0]->setHintText(tr("General purpose 1:"), "");
m_generalPurposeKnob[0]->move(COL_KNOBS, ROW_KNOBSA1);
m_generalPurposeKnob[1] = new XpressiveKnob(this,"A2");
m_generalPurposeKnob[1]->setHintText(tr("General purpose 2:"), "");
m_generalPurposeKnob[1]->move(COL_KNOBS, ROW_KNOBSA2);
m_generalPurposeKnob[2] = new XpressiveKnob(this,"A3");
m_generalPurposeKnob[2]->setHintText(tr("General purpose 3:"), "");
m_generalPurposeKnob[2]->move(COL_KNOBS, ROW_KNOBSA3);
m_panningKnob[0] = new XpressiveKnob(this,"O1 panning");
m_panningKnob[0]->setHintText(tr("O1 panning:"), "");
m_panningKnob[0]->move(COL_KNOBS, ROW_KNOBSP1);
m_panningKnob[1] = new XpressiveKnob(this,"O2 panning");
m_panningKnob[1]->setHintText(tr("O2 panning:"), "");
m_panningKnob[1]->move(COL_KNOBS, ROW_KNOBSP2);
m_relKnob = new XpressiveKnob(this,"Release transition");
m_relKnob->setHintText(tr("Release transition:"), "ms");
m_relKnob->move(COL_KNOBS, ROW_KNOBREL);
m_smoothKnob=new Knob(knobStyled, this, "Smoothness");
m_smoothKnob->setFixedSize(25, 25);
m_smoothKnob->setCenterPointX(12.5);
m_smoothKnob->setCenterPointY(12.5);
m_smoothKnob->setInnerRadius(4);
m_smoothKnob->setOuterRadius(9);
m_smoothKnob->setTotalAngle(280.0);
m_smoothKnob->setLineWidth(3);
m_smoothKnob->setHintText(tr("Smoothness"), "");
m_smoothKnob->move(66, EXPR_TEXT_Y + EXPR_TEXT_H + 4);
connect(m_generalPurposeKnob[0], SIGNAL(sliderMoved(float)), this,
SLOT(expressionChanged()));
connect(m_generalPurposeKnob[1], SIGNAL(sliderMoved(float)), this,
SLOT(expressionChanged()));
connect(m_generalPurposeKnob[2], SIGNAL(sliderMoved(float)), this,
SLOT(expressionChanged()));
connect(m_expressionEditor, SIGNAL(textChanged()), this,
SLOT(expressionChanged()));
connect(m_smoothKnob, SIGNAL(sliderMoved(float)), this,
SLOT(smoothChanged()));
connect(m_graph, SIGNAL(drawn()), this,
SLOT(graphDrawn()));
connect(m_sinWaveBtn, SIGNAL(clicked()), this, SLOT(sinWaveClicked()));
connect(m_triangleWaveBtn, SIGNAL(clicked()), this,
SLOT(triangleWaveClicked()));
connect(m_expWaveBtn, SIGNAL(clicked()), this, SLOT(expWaveClicked()));
connect(m_moogWaveBtn, SIGNAL(clicked()), this,
SLOT(moogSawWaveClicked()));
connect(m_sawWaveBtn, SIGNAL(clicked()), this, SLOT(sawWaveClicked()));
connect(m_sqrWaveBtn, SIGNAL(clicked()), this, SLOT(sqrWaveClicked()));
connect(m_whiteNoiseWaveBtn, SIGNAL(clicked()), this,
SLOT(noiseWaveClicked()));
connect(m_usrWaveBtn, SIGNAL(clicked()), this, SLOT(usrWaveClicked()));
connect(m_helpBtn, SIGNAL(clicked()), this, SLOT(helpClicked()));
connect(m_w1Btn, SIGNAL(clicked()), this, SLOT(updateLayout()));
connect(m_w2Btn, SIGNAL(clicked()), this, SLOT(updateLayout()));
connect(m_w3Btn, SIGNAL(clicked()), this, SLOT(updateLayout()));
connect(m_o1Btn, SIGNAL(clicked()), this, SLOT(updateLayout()));
connect(m_o2Btn, SIGNAL(clicked()), this, SLOT(updateLayout()));
updateLayout();
}
XpressiveView::~XpressiveView()
{
}
void XpressiveView::expressionChanged() {
Xpressive * e = castModel<Xpressive>();
QByteArray text = m_expressionEditor->toPlainText().toLatin1();
switch (m_selectedGraphGroup->model()->value()) {
case W1_EXPR:
e->wavesExpression(0) = text;
break;
case W2_EXPR:
e->wavesExpression(1) = text;
break;
case W3_EXPR:
e->wavesExpression(2) = text;
break;
case O1_EXPR:
e->outputExpression(0) = text;
break;
case O2_EXPR:
e->outputExpression(1) = text;
break;
}
if (m_wave_expr)
m_graph->setEnabled(m_smoothKnob->model()->value() == 0 && text.size() == 0);
if (text.size()>0)
{
const unsigned int sample_rate=m_raw_graph->length();
ExprFront expr(text.constData(),sample_rate);
float t=0;
const float f=10,key=5,v=0.5;
unsigned int i;
expr.add_variable("t", t);
if (m_output_expr)
{
expr.add_constant("f", f);
expr.add_constant("key", key);
expr.add_constant("rel", 0);
expr.add_constant("trel", 0);
expr.add_constant("bnote",e->instrumentTrack()->baseNote());
expr.add_constant("v", v);
expr.add_constant("tempo", Engine::getSong()->getTempo());
expr.add_constant("A1", e->parameterA1().value());
expr.add_constant("A2", e->parameterA2().value());
expr.add_constant("A3", e->parameterA3().value());
expr.add_cyclic_vector("W1",e->graphW1().samples(),e->graphW1().length());
expr.add_cyclic_vector("W2",e->graphW2().samples(),e->graphW2().length());
expr.add_cyclic_vector("W3",e->graphW3().samples(),e->graphW3().length());
}
expr.setIntegrate(&i,sample_rate);
expr.add_constant("srate",sample_rate);
const bool parse_ok=expr.compile();
if (parse_ok) {
e->exprValid().setValue(0);
const int length = m_raw_graph->length();
float * const samples = new float[length];
for (i = 0; i < length; i++) {
t = i / (float) length;
samples[i] = expr.evaluate();
if (std::isinf(samples[i]) != 0 || std::isnan(samples[i]) != 0)
samples[i] = 0;
}
m_raw_graph->setSamples(samples);
delete[] samples;
if (m_wave_expr)
{
smoothChanged();
}
else
{
Engine::getSong()->setModified();
}
}
else
{
e->exprValid().setValue(1);
if (m_output_expr)
m_raw_graph->clear();
}
}
else
{
e->exprValid().setValue(0);
if (m_output_expr)
m_raw_graph->clear();
}
}
void Xpressive::smooth(float smoothness,const graphModel * in,graphModel * out)
{
out->setSamples(in->samples());
if (smoothness>0)
{
const int guass_size = (int)(smoothness * 5) | 1;
const int guass_center = guass_size/2;
const float delta = smoothness;
const float a= 1.0f / (sqrtf(2.0f * F_PI) * delta);
float * const guassian = new float [guass_size];
float sum = 0.0f;
float temp = 0.0f;
int i;
for (i = 0; i < guass_size; i++ )
{
temp = (i - guass_center) / delta;
sum += guassian[i] = a * powf(F_E, -0.5f * temp * temp);
}
for (i = 0; i < guass_size; i++ )
{
guassian[i] = guassian[i] / sum;
}
out->convolve(guassian, guass_size, guass_center);
delete [] guassian;
}
}
void XpressiveView::smoothChanged()
{
Xpressive * e = castModel<Xpressive>();
float smoothness=0;
switch (m_selectedGraphGroup->model()->value()) {
case W1_EXPR:
smoothness=e->smoothW1().value();
break;
case W2_EXPR:
smoothness=e->smoothW2().value();
break;
case W3_EXPR:
smoothness=e->smoothW3().value();
break;
}
Xpressive::smooth(smoothness,m_raw_graph,m_graph->model());
switch (m_selectedGraphGroup->model()->value()) {
case W1_EXPR:
e->W1().copyFrom(m_graph->model());
break;
case W2_EXPR:
e->W2().copyFrom(m_graph->model());
break;
case W3_EXPR:
e->W3().copyFrom(m_graph->model());
break;
}
Engine::getSong()->setModified();
m_graph->setEnabled(m_smoothKnob->model()->value() == 0 && m_expressionEditor->toPlainText().size() == 0);
}
void XpressiveView::graphDrawn()
{
m_raw_graph->setSamples(m_graph->model()->samples());
Xpressive * e = castModel<Xpressive>();
switch (m_selectedGraphGroup->model()->value()) {
case W1_EXPR:
e->W1().copyFrom(m_graph->model());
break;
case W2_EXPR:
e->W2().copyFrom(m_graph->model());
break;
case W3_EXPR:
e->W3().copyFrom(m_graph->model());
break;
}
Engine::getSong()->setModified();
}
void XpressiveView::modelChanged() {
Xpressive * b = castModel<Xpressive>();
m_expressionValidToggle->setModel( &b->exprValid() );
m_generalPurposeKnob[0]->setModel( &b->parameterA1() );
m_generalPurposeKnob[1]->setModel( &b->parameterA2() );
m_generalPurposeKnob[2]->setModel( &b->parameterA3() );
m_panningKnob[0]->setModel( &b->panning1() );
m_panningKnob[1]->setModel( &b->panning2() );
m_relKnob->setModel( &b->relTransition() );
m_selectedGraphGroup->setModel( &b->selectedGraph() );
updateLayout();
}
void XpressiveView::updateLayout() {
Xpressive * e = castModel<Xpressive>();
m_output_expr=false;
m_wave_expr=false;
switch (m_selectedGraphGroup->model()->value()) {
case W1_EXPR:
m_wave_expr=true;
m_graph->setModel(&e->graphW1(), true);
m_raw_graph=&(e->rawgraphW1());
m_expressionEditor->setPlainText(e->wavesExpression(0));
m_smoothKnob->setModel(&e->smoothW1());
m_graph->setEnabled((e->smoothW1().value() == 0 && e->wavesExpression(0).size() == 0));
m_waveInterpolate->setModel(&e->interpolateW1());
m_smoothKnob->show();
m_usrWaveBtn->show();
m_waveInterpolate->show();
break;
case W2_EXPR:
m_wave_expr=true;
m_graph->setModel(&e->graphW2(), true);
m_raw_graph=&(e->rawgraphW2());
m_expressionEditor->setPlainText(e->wavesExpression(1));
m_smoothKnob->setModel(&e->smoothW2());
m_graph->setEnabled((e->smoothW2().value() == 0 && e->wavesExpression(1).size() == 0));
m_waveInterpolate->setModel(&e->interpolateW2());
m_smoothKnob->show();
m_usrWaveBtn->show();
m_waveInterpolate->show();
break;
case W3_EXPR:
m_wave_expr=true;
m_graph->setModel(&e->graphW3(), true);
m_raw_graph=&(e->rawgraphW3());
m_expressionEditor->setPlainText(e->wavesExpression(2));
m_smoothKnob->setModel(&e->smoothW3());
m_graph->setEnabled((e->smoothW3().value() == 0 && e->wavesExpression(2).size() == 0));
m_waveInterpolate->setModel(&e->interpolateW3());
m_smoothKnob->show();
m_usrWaveBtn->show();
m_waveInterpolate->show();
break;
case O1_EXPR:
m_output_expr=true;
m_graph->setModel(&e->graphO1(), true);
m_raw_graph=&(e->graphO1());
m_expressionEditor->setPlainText(e->outputExpression(0));
m_smoothKnob->hide();
m_graph->setEnabled(false);
m_usrWaveBtn->hide();
m_waveInterpolate->hide();
break;
case O2_EXPR:
m_output_expr=true;
m_graph->setModel(&e->graphO2(), true);
m_raw_graph=&(e->graphO2());
m_expressionEditor->setPlainText(e->outputExpression(1));
m_smoothKnob->hide();
m_graph->setEnabled(false);
m_usrWaveBtn->hide();
m_waveInterpolate->hide();
break;
}
}
void XpressiveView::sinWaveClicked() {
if (m_output_expr)
m_expressionEditor->appendPlainText("sinew(integrate(f))");
else
m_expressionEditor->appendPlainText("sinew(t)");
Engine::getSong()->setModified();
}
void XpressiveView::triangleWaveClicked() {
if (m_output_expr)
m_expressionEditor->appendPlainText("trianglew(integrate(f))");
else
m_expressionEditor->appendPlainText("trianglew(t)");
Engine::getSong()->setModified();
}
void XpressiveView::sawWaveClicked() {
if (m_output_expr)
m_expressionEditor->appendPlainText("saww(integrate(f))");
else
m_expressionEditor->appendPlainText("saww(t)");
Engine::getSong()->setModified();
}
void XpressiveView::sqrWaveClicked() {
if (m_output_expr)
m_expressionEditor->appendPlainText("squarew(integrate(f))");
else
m_expressionEditor->appendPlainText("squarew(t)");
Engine::getSong()->setModified();
}
void XpressiveView::noiseWaveClicked() {
m_expressionEditor->appendPlainText("randsv(t*srate,0)");
Engine::getSong()->setModified();
}
void XpressiveView::moogSawWaveClicked()
{
if (m_output_expr)
m_expressionEditor->appendPlainText("moogsaww(integrate(f))");
else
m_expressionEditor->appendPlainText("moogsaww(t)");
Engine::getSong()->setModified();
}
void XpressiveView::expWaveClicked()
{
if (m_output_expr)
m_expressionEditor->appendPlainText("expw(integrate(f))");
else
m_expressionEditor->appendPlainText("expw(t)");
Engine::getSong()->setModified();
}
void XpressiveView::usrWaveClicked() {
m_expressionEditor->setPlainText("");
QString fileName = m_raw_graph->setWaveToUser();
smoothChanged();
Engine::getSong()->setModified();
}
QString XpressiveHelpView::s_helpText=
"<b>O1, O2</b> - Two output waves. Panning is controlled by PN1 and PN2.<br>"
"<b>W1, W2, W3</b> - Wave samples evaluated by expression. In these samples, t variable ranges [0,1).<br>"
"These waves can be used as functions inside the output waves (O1, O2). The wave period is 1.<br>"
"<h4>Available variables:</h4><br>"
"<b>t</b> - Time in seconds.<br>"
"<b>f</b> - Note's pitched frequency. Available only in the output expressions.<br>"
"<b>key</b> - Note's keyboard key. 0 denotes C-1, 60 denotes C4, 127 denotes G9. Available only in the output expressions.<br>"
"<b>bnote</b> - Base note. By default it is 69 which means A4, unless you change it.<br>"
"<b>srate</b> - Sample rate. In wave expression it returns the wave's number of samples.<br>"
"<b>tempo</b> - Song's Tempo. Available only in the output expressions.<br>"
"<b>v</b> - Note's volume. Note that the output is already multiplied by the volume. Available only in the output expressions.<br>"
"<b>rel</b> - Gives 0.0 while the key is held, and 1.0 after the key release. Available only in the output expressions.<br>"
"<b>trel</b> - Time after release. While the note is held, it gives 0.0. Afterwards, it starts counting seconds.<br>"
"The time it takes to shift from 0.0 to 1.0 after key release is determined by the REL knob<br>"
"<b>seed</b> - A random value that remains consistent in the lifetime of a single wave. Meant to be used with <b>randsv</b><br>"
"<b>A1, A2, A3</b> - General purpose knobs. You can reference them only in O1 and O2. In range [-1,1].<br>"
"<h4>Available functions:</h4><br>"
"<b>W1, W2, W3</b> - As mentioned before. You can reference them only in O1 and O2.<br>"
"<b>cent(x)</b> - Gives pow(2,x/1200), so you can multiply it with the f variable to pitch the frequency.<br>"
"100 cents equals one semitone<br>"
"<b>semitone(x)</b> - Gives pow(2,x/12), so you can multiply it with the f variable to pitch the frequency.<br>"
"<b>last(n)</b> - Gives you the last n'th evaluated sample. In O1 and O2 it keeps a whole second. Thus the argument n must be in the range [1,srate], or else, it will return 0.<br>"
"<b>integrate(x)</b> - Integrates x by delta t (It sums values and divides them by sample rate).<br>"
"If you use notes with automated frequency, you should use:<br>"
"sinew(integrate(f)) instead of sinew(t*f)<br>"
"<b>randv(x)</b> - A random vector. Each cell is reference by an integer index in the range [0,2^31]<br>"
"Each evaluation of an expression results in different random vector.<br>"
"Although, it remains consistent in the lifetime of a single wave.<br>"
"If you want a single random values you can use randv(0),randv(1)... <br>"
"and every reference to randv(a) will give you the same value."
"If you want a random wave you can use randv(t*srate).<br>"
"Each random value is in the range [-1,1).<br>"
"<b>randsv(x,seed)</b> - works exactly like randv(x),<br>"
"except that it lets you to select the seed manualy,<br>"
"if you want to try different random values and make it consistent in each evaluation.<br>"
"<b>sinew(x)</b> - A sine wave with period of 1 (In contrast to real sine wave which have a period of 2*pi).<br>"
"<b>trianglew(x)</b> - A triangle wave with period of 1.<br>"
"<b>squarew(x)</b> - A square wave with period of 1.<br>"
"<b>saww(x)</b> - A saw wave with period of 1.<br>"
"<b>clamp(min_val,x,max_val)</b> - If x is in range of (min_val,max_val) it returns x. Otherwise if it's greater than max_val it returns max_val, else returns min_val.<br>"
"<b>abs, sin, cos, tan, cot, asin, acos, atan, atan2, sinh, cosh, tanh, asinh, acosh, atanh, sinc, "
"hypot, exp, log, log2, log10, logn, pow, sqrt, min, max, floor, ceil, round, trunc, frac, "
"avg, sgn, mod, etc. are also available.</b><br>"
"<b>Operands + - * / % ^ &gt; &lt; &gt;= &lt;= == != &amp; | are also available.</b><br>"
"<b>Amplitude Modulation</b> - W1(t*f)*(1+W2(t*f))<br>"
"<b>Ring Modulation</b> - W1(t * f)*W2(t * f)<br>"
"<b>Mix Modulation</b> - 0.5*( W1(t * f) + W2(t * f) )<br>"
"<b>Frequency Modulation</b> - [vol1]*W1( integrate( f + srate*[vol2]*W2( integrate(f) ) ) )<br>"
"<b>Phase Modulation</b> - [vol1]*W1( integrate(f) + [vol2]*W2( integrate(f) ) )<br>"
;
XpressiveHelpView::XpressiveHelpView():QTextEdit(s_helpText)
{
setWindowTitle ( "Xpressive Help" );
setTextInteractionFlags ( Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse );
getGUI()->mainWindow()->addWindowedWidget( this );
parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false );
parentWidget()->setWindowIcon( PLUGIN_NAME::getIconPixmap( "logo" ) );
parentWidget()->setFixedSize( 300, 500);
// No maximize button
Qt::WindowFlags flags = parentWidget()->windowFlags();
flags &= ~Qt::WindowMaximizeButtonHint;
parentWidget()->setWindowFlags( flags );
}
void XpressiveView::helpClicked() {
XpressiveHelpView::getInstance()->show();
}
extern "C" {
// necessary for getting instance out of shared lib
PLUGIN_EXPORT Plugin * lmms_plugin_main(Model *m, void *) {
return (new Xpressive(static_cast<InstrumentTrack *>(m)));
}
}