Yet Another Synthesizer Engine
 
Loading...
Searching...
No Matches
yase::Controls Class Reference

#include <controls.hh>

+ Inheritance diagram for yase::Controls:
+ Collaboration diagram for yase::Controls:

Detailed Description

Use a Controls object to connect MIDI knobs and faders to Module inputs.

For example, to add a volume knob to your project, you can do

Container synth;
Gain gain;
Controls controls;
MidiInput midi("Your Device");
synth.add(gain)
.add(conrols)
.add(midi)
.propagate_to(controls); // Important!
// module, input name, min, max, midi id
controls.map(gain, "amplitude", 0, 1, 123);
A module and event manager that can contain other modules.
Definition container.hh:63
Container & propagate_to(EventManager &em)
Definition container.cc:95
Container & add(Module &module)
Definition container.cc:62
Use a Controls object to connect MIDI knobs and faders to Module inputs.
Definition controls.hh:82
Controls & map(Fader &fader, int midi_id)
Definition controls.cc:50
Amplifies (or attenuates) its input signal to get the output signal.
Definition gain.hh:38
A Midi Input manager.
Definition midi_input.hh:48

Note that a controls object is a Container, just like the synth Container above. Thus, it can receive events from the MidiInput object. However, you must explicitly tell the synth container to propagate events to the controls container.

Using the example above, you can now turn the knob on your controller with MIDI id 123 and modulate the amlitude of the gain module. The controls object maps the MIDI value, which ranges from 0 to 127, to the interval [0,1]. By default, this mapping is linear. But you can make it exponential if you want more sensititvy at the low end. For example,

controls.map(gain, "amplitude", 0, 1, 123)
.exponential(1e4);
Controls & exponential(double base)
Definition controls.cc:150

Here, the mapping between the MIDI knob position and the amplitude will follow

\(\frac{(y-x)b^{m/127-1}}{b-1}+x\)

where x and y are the minimum and maximum value of the control, b is the base of the exponential (1e4 in this example), and m is the MIDI value.

This is easy to see graphically. In the image below, the control output ranges from 2.0 to 5.0 and the response to the MIDI input is shown for various bases.

Example

A complete example is listed below using a custom module to display the value of each control in real time as the associated knob is adjusted.

//
// YASE Example
//
// Copyright (C) 2022 Eric Klavins
// This file is part of YASE
//
// YASE 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.
//
// YASE 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 YASE. If not, see <https://www.gnu.org/licenses/>.
//
#include <iostream>
#include <ncurses.h>
#include "yase.hh"
using namespace yase;
const int SIZE=20;
const string device_name = "Arturia MiniLab mkII";
const int midi_ids[4] = { 74, 71, 18, 19 };
class Report : public Module {
public:
Report() {
for ( int i=0; i<4; i++ )
add_input("signal_" + to_string(i));
buf[SIZE-1] = 0;
}
void init() {
count = 0;
}
void update() {
if ( count++ > 1000 ) {
count = 0;
for ( int i=0; i<4; i++ ) {
for ( int j=0; j<SIZE-1; j++ )
buf[j] = j < (int) inputs[i] ? '*' : ' ';
mvprintw(3*(i/2)+1, 2 + (SIZE+5) * (i%2), "Knob %d: %0.2f", i, inputs[i]);
mvprintw(3*(i/2)+2, 2 + (SIZE+5) * (i%2), "[%s]", buf);
}
refresh();
}
}
char buf[SIZE];
int count;
};
int main(int argc, char * argv[]) {
Container synth;
Controls controls;
Audio audio; // There is no sound, but the audio module regulates timing
Report report;
MidiInput midi(device_name);
synth.add(audio)
.add(midi)
.add(controls)
.propagate_to(controls);
controls.map(report, 0, 0, SIZE, midi_ids[0]).linear();
controls.map(report, 1, 0, SIZE, midi_ids[1]).exponential(1e-2);
controls.map(report, 2, 0, SIZE, midi_ids[2]).exponential(1e2);
controls.map(report, 3, 0, SIZE, midi_ids[3]).exponential(1e5);
initscr();
curs_set(0);
endwin();
return 0;
}
A module wrapper for a stereo PortAudio interface (http://www.portaudio.com/).
Definition audio.hh:46
void run(int num_steps)
Definition container.cc:374
Controls & linear()
Set the most recently added control's response to linear.
Definition controls.cc:141
An abstract base class for modules.
Definition module.hh:39
#define UNTIL_INTERRUPTED
Use as in Synthesizer::run(UNTIL_INTERUPTED)
Definition globals.hh:27
Definition additive_saw.cc:24

Public Member Functions

void init ()
 
void update ()
 
Controlsmap (Fader &fader, int midi_id)
 
Controlsmap (Module &module, string name, double min, double max, int midi_id)
 
Controlsmap (Module &module, int index, double min, double max, int midi_id)
 
Controlsmap (Module &module, string name, json spec)
 
Controlsset_tracking_gain (double x)
 
Controlslinear ()
 Set the most recently added control's response to linear.
 
Controlsexponential (double base)
 
void randomize ()
 Randomly assign a value to all controls.
 
- Public Member Functions inherited from yase::Container
void init ()
 
void update ()
 
Containeradd (Module &module)
 
Containeradd_if_new (Module &module)
 
Containerpropagate_to (EventManager &em)
 
void run (int num_steps)
 
void run_again (int num_steps)
 
void stop ()
 
Containerconnect (Module &source, string output, Module &dest, string input)
 
Containerconnect (Module &source, string output, Module &dest, int input)
 
Containerconnect (AutoLoad &loader, string category, Module &module)
 
Containerconnect (Module &source, Module &dest)
 
Containerpath (Module &a, Module &b, Module &c)
 
Containerpath (Module &a, Module &b, Module &c, Module &d)
 
Containerpath (Module &a, Module &b, Module &c, Module &d, Module &e)
 
Containerdisconnect (Module &source, string output, Module &dest, string input)
 
bool connected (Module *module, string input_name)
 
Containerequate_input (string input, Module &sub_module, string sub_input)
 
Containerattach_inputs (std::vector< std::tuple< string, Module &, string > > attachments)
 
Containerequate_output (string output, Module &sub_module, string sub_output)
 
Containerattach_outputs (std::vector< std::tuple< string, Module &, string > > attachments)
 
Containerset_thread_number (int n)
 EXPERIMENTAL.
 
Containeradd (Module &module, int n)
 EXPERIMENTAL.
 
void run_threaded (int num_steps)
 EXPERIMENTAL.
 
void update_threaded ()
 EXPERIMENTAL.
 
void thread_loop (int i)
 EXPERIMENTAL.
 
- Public Member Functions inherited from yase::Module
virtual void init ()=0
 
virtual void update ()=0
 
int add_input (string name)
 
int add_output (string name)
 
int get_input_index (string name) const
 
string get_input_name (int index) const
 
string get_output_name (int index) const
 
int get_output_index (string name) const
 
void set_input (string name, double value)
 
void set_input (int index, double value)
 
double get_input (int index) const
 
double get_input (string name) const
 
double get_output (string name) const
 
double get_output (int index) const
 
void set_output (int index, double value)
 
void copy_inputs (const Module &source)
 
void copy_outputs (Module &destination) const
 
void configure (std::vector< std::tuple< string, double > > assignments)
 
int num_inputs () const
 
int num_outputs () const
 
void emit (Event e)
 
void set_ts (double s)
 
double get_ts ()
 
- Public Member Functions inherited from yase::EventManager
 EventManager ()
 Make a new event manager.
 
void process_events (vector< Module * > &modules)
 
void respond_to (const Event &event)
 
EventManagerlisten (int event_type, function< void(const Event &)> handler)
 
EventManagerlisten (int event_type, int port, function< void(const Event &)> handler)
 

Member Function Documentation

◆ exponential()

Controls & yase::Controls::exponential ( double  base)

Set the most recently added control's response to exponential with the given base

Parameters
baseThe base of the exponential response.

◆ init()

void yase::Controls::init ( )
virtual

This method should be overridden by derived classes. It will usually be called once, after all modules and connections objects have been added to a synthesizer, but before the synthesizer starts running.

Reimplemented from yase::Container.

◆ map() [1/4]

Controls & yase::Controls::map ( Fader fader,
int  midi_id 
)

Associate a MIDI listener with a Fader. Typically this method is not called directly by the user.

Parameters
faderA reference to a Fader object
midi_itThe MIDI id of the controller

◆ map() [2/4]

Controls & yase::Controls::map ( Module module,
int  index,
double  min,
double  max,
int  midi_id 
)

Associate a controller with an input of a module.

Parameters
moduleThe target module
indexThe index of the target module's input
minThe minimum output of the control
maxThe minimum maximum value of the control
midi_idThe MIDI id of the knob or fader on your MIDI controller

◆ map() [3/4]

Controls & yase::Controls::map ( Module module,
string  name,
double  min,
double  max,
int  midi_id 
)

Associate a controller with an input of a module.

Parameters
moduleThe target module
nameThe name of the target module's input
minThe minimum output of the control
maxThe minimum maximum value of the control
midi_idThe MIDI id of the knob or fader on your MIDI controller

◆ map() [4/4]

Controls & yase::Controls::map ( Module module,
string  name,
json  spec 
)

Associate a controller with an input of a module.

Parameters
moduleThe target module
nameThe name of the target module's input
specA json object containing fields min, max, mid, type, and base (if type is exponential)

This version of the control method can be called with a json specification of the control, which is especially useful when setting up controls from json files. For example, if a json file called "config.json" had

{
"attack": {
"midi": 77,
"min": 0.005,
"max": 1.0,
"default": 0.005,
"type": "exponential",
"base": 10000
}
}

in it. Then you could do

Controls controls;
Envelope envelope;
json config = get_config("config.json")
controls.map(envelope, "attack", config["attack"]);
An ADSR envelope.
Definition envelope.hh:50

◆ set_tracking_gain()

Controls & yase::Controls::set_tracking_gain ( double  x)

Sets the tracking gain of the most recently added control. See Fader::set_tracking_gain(double x).

Parameters
xThe new tracking gain (e.g. 0.1*FADER_GAIN)
Returns
A reference for method chaining

◆ update()

void yase::Controls::update ( )
virtual

This method should be overridden by derived classes. It will be called repeatedly by a synthesizer at a frequency determined by SAMPLE_RATE.

Reimplemented from yase::Container.


The documentation for this class was generated from the following files: