This GitLab instance reached the end of its service life. It won't be possible to create new users or projects.

Please read the deprecation notice for more information concerning the deprecation timeline

Visit migration.git.tu-berlin.de (internal network only) to import your old projects to the new GitLab platform 📥

Commit a9034df2 by Henrik von Coler

Added subtractive example!

parent 76cd669a
# Parts of this Makefile (especially the explanations) are based on this
# StackOverflow answer:
# https://stackoverflow.com/a/31610364
# Specify the name of the executable that will be compiled:
EXECUTABLE = subtractive_example
# Specify source code directory:
SRC_DIR = src
# Get a list of all the source files:
SRC = $(wildcard $(SRC_DIR)/*.cpp)
# Specify where to put the object (.o) files:
OBJ_DIR = obj
# Get a list of the object files that correspond to the source files:
OBJ = $(SRC:$(SRC_DIR)/%.cpp=$(OBJ_DIR)/%.o)
# From the object file list, get the dependency file list to handle automatic
# recompilation when a header file is modified:
DEP = $(OBJ:.o=.d)
# Required flags to enable the automatic dependency generation by the compiler:
CPPFLAGS = -MMD -MP
# C++ compiler flags:
CXXFLAGS = -std=c++11 -Wall -I.
# Libraries to link (linker flags):
LDFLAGS = -ljack -llo -lyaml-cpp -lsndfile -lrtmidi
# Tell make that these target are not real files:
.PHONY: all clean
all: $(EXECUTABLE)
$(EXECUTABLE): $(OBJ)
$(CXX) $(LDFLAGS) $^ -o $@
# The build directory is custom so we need to tell make how to do it, also it
# must exist before trying to compile:
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp | $(OBJ_DIR)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $<
# Create obj directory if it doesn't exist:
$(OBJ_DIR):
mkdir $@
# On clean, remove obj directory and executable
clean:
$(RM) -r $(OBJ_DIR) $(EXECUTABLE) *.o
-include $(DEP)
port: 6666
ctl1: 14
ctl2: 15
ctl3: 16
ctl4: 17
path1: "/attack"
path2: "/release"
path3: "/pulsewidth"
path4: "/expo"
\ No newline at end of file
subtractive_example {#mainpage}
=======================
# BRIEF
This example uses a square wave
oscillator and a biquad filter.
# Libraries
The following libraries are used within this project:
* jack
* YAML
* LIBLO
# OSC server config
The file 'config.yml' is read by
fm_example in order to set the port
and the OSC paths used.
\ No newline at end of file
PROJECT_NAME = "Subtractive_Example"
PROJECT_BRIEF = "Simple Subtractive with OSC control!"
OUTPUT_DIRECTORY = "./"
INPUT = "../" README.md
FILE_PATTERNS = *.cpp *.h
RECURSIVE = YES
FILTER_SOURCE_FILES = NO
# deactivate when done with major changes
EXTRACT_PRIVATE = YES
HAVE_DOT = YES
UML_LOOK = YES
USE_MOFILE_AS_MAINPAGE = README.md
\ No newline at end of file
/**
* \class Biquad
* \file Biquad.h
*
*
* \link https://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
*
* \author Henrik von Coler
*
* \version 0.1
*
* \date 2019-05/20
*
*/
#include <math.h>
#include "Biquad.h"
Biquad::Biquad() {
type = bq_type_lowpass;
a0 = 1.0;
a1 = a2 = b1 = b2 = 0.0;
Fc = 0.50;
Q = 0.707;
peakGain = 0.0;
z1 = z2 = 0.0;
}
Biquad::Biquad(int type, double Fc, double Q, double peakGainDB) {
setBiquad(type, Fc, Q, peakGainDB);
z1 = z2 = 0.0;
}
Biquad::~Biquad() {
}
void Biquad::setType(int type) {
this->type = type;
calcBiquad();
}
void Biquad::setQ(double Q) {
this->Q = Q;
calcBiquad();
}
void Biquad::setFc(double Fc) {
this->Fc = Fc;
calcBiquad();
}
void Biquad::setPeakGain(double peakGainDB) {
this->peakGain = peakGainDB;
calcBiquad();
}
void Biquad::setBiquad(int type, double Fc, double Q, double peakGainDB) {
this->type = type;
this->Q = Q;
this->Fc = Fc;
setPeakGain(peakGainDB);
}
void Biquad::calcBiquad(void) {
double norm;
double V = pow(10, fabs(peakGain) / 20.0);
double K = tan(M_PI * Fc);
switch (this->type) {
case bq_type_lowpass:
norm = 1 / (1 + K / Q + K * K);
a0 = K * K * norm;
a1 = 2 * a0;
a2 = a0;
b1 = 2 * (K * K - 1) * norm;
b2 = (1 - K / Q + K * K) * norm;
break;
case bq_type_highpass:
norm = 1 / (1 + K / Q + K * K);
a0 = 1 * norm;
a1 = -2 * a0;
a2 = a0;
b1 = 2 * (K * K - 1) * norm;
b2 = (1 - K / Q + K * K) * norm;
break;
case bq_type_bandpass:
norm = 1 / (1 + K / Q + K * K);
a0 = K / Q * norm;
a1 = 0;
a2 = -a0;
b1 = 2 * (K * K - 1) * norm;
b2 = (1 - K / Q + K * K) * norm;
break;
case bq_type_notch:
norm = 1 / (1 + K / Q + K * K);
a0 = (1 + K * K) * norm;
a1 = 2 * (K * K - 1) * norm;
a2 = a0;
b1 = a1;
b2 = (1 - K / Q + K * K) * norm;
break;
case bq_type_peak:
if (peakGain >= 0) { // boost
norm = 1 / (1 + 1/Q * K + K * K);
a0 = (1 + V/Q * K + K * K) * norm;
a1 = 2 * (K * K - 1) * norm;
a2 = (1 - V/Q * K + K * K) * norm;
b1 = a1;
b2 = (1 - 1/Q * K + K * K) * norm;
}
else { // cut
norm = 1 / (1 + V/Q * K + K * K);
a0 = (1 + 1/Q * K + K * K) * norm;
a1 = 2 * (K * K - 1) * norm;
a2 = (1 - 1/Q * K + K * K) * norm;
b1 = a1;
b2 = (1 - V/Q * K + K * K) * norm;
}
break;
case bq_type_lowshelf:
if (peakGain >= 0) { // boost
norm = 1 / (1 + sqrt(2) * K + K * K);
a0 = (1 + sqrt(2*V) * K + V * K * K) * norm;
a1 = 2 * (V * K * K - 1) * norm;
a2 = (1 - sqrt(2*V) * K + V * K * K) * norm;
b1 = 2 * (K * K - 1) * norm;
b2 = (1 - sqrt(2) * K + K * K) * norm;
}
else { // cut
norm = 1 / (1 + sqrt(2*V) * K + V * K * K);
a0 = (1 + sqrt(2) * K + K * K) * norm;
a1 = 2 * (K * K - 1) * norm;
a2 = (1 - sqrt(2) * K + K * K) * norm;
b1 = 2 * (V * K * K - 1) * norm;
b2 = (1 - sqrt(2*V) * K + V * K * K) * norm;
}
break;
case bq_type_highshelf:
if (peakGain >= 0) { // boost
norm = 1 / (1 + sqrt(2) * K + K * K);
a0 = (V + sqrt(2*V) * K + K * K) * norm;
a1 = 2 * (K * K - V) * norm;
a2 = (V - sqrt(2*V) * K + K * K) * norm;
b1 = 2 * (K * K - 1) * norm;
b2 = (1 - sqrt(2) * K + K * K) * norm;
}
else { // cut
norm = 1 / (V + sqrt(2*V) * K + K * K);
a0 = (1 + sqrt(2) * K + K * K) * norm;
a1 = 2 * (K * K - 1) * norm;
a2 = (1 - sqrt(2) * K + K * K) * norm;
b1 = 2 * (K * K - V) * norm;
b2 = (V - sqrt(2*V) * K + K * K) * norm;
}
break;
}
return;
}
/**
* \class Biquad
* \brief A biquad filter class!
*
* \file Biquad.h
*
*
* \link https://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
*
* \author Henrik von Coler
*
* \version 0.1
*
* \date 2019-05/20
*
*/
#ifndef Biquad_h
#define Biquad_h
enum {
bq_type_lowpass = 0,
bq_type_highpass,
bq_type_bandpass,
bq_type_notch,
bq_type_peak,
bq_type_lowshelf,
bq_type_highshelf
};
class Biquad {
public:
Biquad();
Biquad(int type, double Fc, double Q, double peakGainDB);
~Biquad();
void setType(int type);
void setQ(double Q);
void setFc(double Fc);
void setPeakGain(double peakGainDB);
void setBiquad(int type, double Fc, double Q, double peakGain);
float process(float in);
protected:
void calcBiquad(void);
int type;
double a0, a1, a2, b1, b2;
double Fc, Q, peakGain;
double z1, z2;
};
inline float Biquad::process(float in) {
double out = in * a0 + z1;
z1 = in * a1 + z2 - b1 * out;
z2 = in * a2 - b2 * out;
return out;
}
#endif // Biquad_h
/**
* \struct noteMessage
*
*
* \brief Structure for holding Note (on,off) messages.
*
*
* \author Henrik von Coler
*
* \version $Revision: 0.5 $
*
* \date $Date: 2005/04/14 14:16:20 $
*
* Contact: von_coler@intelligent-noise-solutions.de
*
*
*/
#ifndef DATATYPES_H
#define DATATYPES_H
typedef struct {
int noteNumber = -1; /// @var noteNumber The note number
int velocity = -1;
bool hasBeenProcessed = false;
}noteMessage;
typedef struct {
int ctlNumber = -1; /// @var noteNumber The note number
int value = -1;
bool hasBeenProcessed = false;
}ctlMessage;
#endif // DATATYPES_H
#include "envelope.h"
Envelope::Envelope(double att, double rel, double expo, int fs)
{
L_attack = att;
L_release = rel;
exponent =expo;
t = 0.0;
active = false;
FS = fs;
}
void Envelope::trigger()
{
t = 0;
active = true;
}
double Envelope::get_next_value()
{
double outVal;
if(t<=L_attack)
{
outVal = 1.0/(L_attack) * t;
}
else
{
outVal = pow(1.0-((1/(L_release))*t), exponent);
}
// step ahead
t = t + (1.0 / (double) FS );
// stop and end of release
if(t>=(L_attack+L_release))
{
active=false;
}
return outVal;
}
bool Envelope::is_active()
{
return active;
}
void Envelope::set_exponent(int e)
{
exponent = e;
}
void Envelope::set_attack(double a)
{
L_attack = a;
}
void Envelope::set_release(double r)
{
L_release = r;
}
#ifndef ENVELOPE_H
#define ENVELOPE_H
#include<iostream>
#include<math.h>
using std::cout;
class Envelope
{
public:
Envelope(double att, double rel, double expo, int fs);
void trigger();
double get_next_value();
bool is_active();
void set_exponent(int e);
void set_attack(double a);
void set_release(double r);
private:
int FS;
double L_attack;
double L_release;
int exponent;
double t;
bool active;
};
#endif // ENVELOPE_H
/**
* \file main.cpp
*
* \brief Just the main file ...
*
* \author Henrik von Coler
*
* \date 2019-04-15
*
*/
#include"yamlman.h"
#include"subtractive_example.h"
#include<iostream>
#include<stdlib.h>
#include<unistd.h>
using std::cout;
using std::endl;
int main(int argc, char *argv[]){
if (argc < 3)
cout << "Need config file to start!" << endl;
else{
// process command line arguments
std::string configfile;
int id;
std::string mode;
for (int i = 1; i < argc; i++)
{
if (i + 1 != argc)
{
if (strcmp(argv[i], "-c") == 0)
{
configfile = argv[i + 1];
i++;
}
if (strcmp(argv[i], "-ID") == 0)
{
id = atoi(argv[i + 1]);
i++;
}
if (strcmp(argv[i], "-m") == 0)
{
mode = argv[i + 1];
i++;
}
}
}
// initialize objects
YamlMan *yaml_manager = new YamlMan(configfile);
SubtractiveExample *t = new SubtractiveExample(yaml_manager, id, mode);
}
}
#include "midiman.h"
MidiMan::MidiMan(int ID)
{
// rtmidid intit
midiin = new RtMidiIn(RtMidiIn::Api::UNSPECIFIED ,std::string("RtMidi Input Client"),(unsigned int) 100);
midiin->openPort(ID);
midiin->setCallback( &main_callback, this);
// Don't ignore sysex, timing, or active sensing messages.
midiin->ignoreTypes( false, false, false );
}
void MidiMan::setVerbose()
{
isVerbose = true;
}
void MidiMan::main_callback(double deltatime, std::vector< unsigned char > *message, void *userData )
{
// static cast to this object
MidiMan* statCast = static_cast<MidiMan*>(userData);
// is this a note message?
int byte0 = (int)message->at(0);
if(byte0 == 144)
{
if(statCast->isVerbose==true)
std::cout << "Received MIDI note message! " << "(" << (int)message->at(1) << "-" << (int)message->at(2) << ")" << std::endl;
noteMessage tmpMes;
tmpMes.noteNumber = (int)message->at(1);
tmpMes.velocity = (int)message->at(2);
statCast->note_messages.push_back(tmpMes);
}
if(byte0 == 176)
{
if(statCast->isVerbose==true)
std::cout << "Received MIDI control message! " << "(" << (int)message->at(1) << "-" << (int)message->at(2) << ")" << std::endl;
ctlMessage tmpMes;
tmpMes.ctlNumber = (int)message->at(1);
tmpMes.value = (int)message->at(2);
statCast->ctl_messages.push_back(tmpMes);
}
}
std::vector<noteMessage>* MidiMan::get_noteMessages()
{
return &note_messages;
}
std::vector<ctlMessage> *MidiMan::get_ctlMessages()
{
return &ctl_messages;
}
void MidiMan::flush_all_messages()
{
note_messages.clear();
ctl_messages.clear();
}
/**
* \class MidiMan
*
*
* \brief Class which parses and stores the incoming MIDI messages.
*
*
* \author Henrik von Coler
*
* \version $Revision: 0.5 $
*
* \date $Date: 2005/04/14 14:16:20 $
*
* Contact: von_coler@tu-berlin.de
*
*
*/
#ifndef MIDIMAN_H
#define MIDIMAN_H
#include <iostream>
#include <cstdlib>
#include <signal.h>
#include <stdlib.h>
#include <string>
#include <vector>
#include <iostream>
#include <jack/midiport.h>
#include <rtmidi/RtMidi.h>
#include "datatypes.h"
class MidiMan
{
public:
///
/// \brief a midi message
typedef struct {
int byte1 = -1;
int byte2 = -1;
double byte3 = -1;
bool hasBeenProcessed = false;
}midiMessage;
MidiMan(int ID);
~MidiMan();
std::vector<noteMessage> *get_noteMessages();
std::vector<ctlMessage> *get_ctlMessages();
void flush_all_messages();
void setVerbose();
private:
RtMidiIn *midiin;