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 bb980028 by Henrik von Coler

Added granular synthesis example!

parent 3a1c585e
# define the compiler
CC = g++
# path(s) to include files
CFLAGS = -I.
# libraries to link
LDFLAGS = -ljack -llo -lsndfile
SRCS = main.cpp granular_example.cpp oscman.cpp singlesample.cpp grainplayer.cpp triangularwindow.cpp
DEPS = granular_example.h oscman.h singlesample.h grainplayer.h triangularwindow.h
all: granular_example
%.o: %.c $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
granular_example: main.o granular_example.o oscman.o singlesample.o grainplayer.o triangularwindow.o
$(CC) -o granular_example main.o granular_example.o oscman.o singlesample.o grainplayer.o triangularwindow.o $(LDFLAGS)
clean:
rm *.o granular_example
wav_example {#mainpage}
=======================
# BRIEF
This example introduces the libsndfile
for reading a wav file and playing it
back in a loop. It is able to process
mono and stereo files.
The playback speed (resampling) can be
controlled using OSC. The OSC path
and port are hardcoded in the file
WavExample.cpp and oscman.cpp.
# Libraries
The following libraries are used within this project:
* JACK
* libsndfile
* liblo
# Usage
The binaray gain_example expects one command
line argument for wav file to read with the
flag '-f':
./gain_example -f ../wav/stereo_sine.wav
\ No newline at end of file
PROJECT_NAME = "Wav_Example"
PROJECT_BRIEF = "Simple wav file looper with adjustable playback speed!"
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
#include "grainplayer.h"
GrainPlayer::GrainPlayer(std::string filePath, int fs, int win_size, int nWin)
{
cout << "Creating GrainPlayer with " << win_size << " samples window size! " << endl;
nWindows = nWin;
sample = new SingleSample(filePath, fs);
windows = new TriangularWindow[nWindows];
for(int i=0; i<nWindows; i++)
{
windows[i].initialize(win_size,i* (win_size/nWindows),i* (win_size/nWindows));
}
}
double GrainPlayer::get_sample()
{
double output = 0;
for(int windowCNT=0; windowCNT<nWindows; windowCNT++)
{
double tmpGain = windows[windowCNT].get_value();
int tmpPos = windows[windowCNT].get_relative_position();
double tmpOut = 0;
if(tmpPos >= 0 && tmpPos <= sample->get_nFrames())
{
tmpOut = tmpGain * sample->get_sample(0,tmpPos);
}
output += tmpOut;
int f = windows[windowCNT].step();
if(f==1)
{
int tmpVal = windows[windowCNT].increment_relative_position(relative_stepsize,sample->get_nFrames());
}
}
return output;
}
void GrainPlayer::set_relative_distance(double r)
{
relative_stepsize = r;
}
double GrainPlayer::get_position()
{
}
#ifndef GRAINPLAYER_H
#define GRAINPLAYER_H
#include "singlesample.h"
#include "triangularwindow.h"
class GrainPlayer
{
public:
GrainPlayer(std::string filePath, int fs, int win_size, int nWin);
double get_sample();
void set_relative_distance(double r);
double get_position();
private:
SingleSample *sample;
double relative_stepsize;
TriangularWindow *windows;
int nWindows;
};
#endif // GRAINPLAYER_H
/**
*
* \file granular_example.cpp
* \class GranularExample
*
* \brief Read a wave file and play it back in a granular style.
*
* \author Henrik von Coler
*
* \date $Date: 2019/03/23 $
*
*/
#include"granular_example.h"
using std::cout;
using std::endl;
GranularExample::GranularExample(std::string filename, int win_size, int nWindows)
{
// creating an OSC manager instance
oscman = new OscMan(5111);
this->client = jack_client_open("Grain_Example", JackNullOption, &status, NULL);
fs = jack_get_sample_rate(client);
// hard coded for now
grainer = new GrainPlayer(filename, fs, win_size, nWindows);
nChannels = 1;
cout << "Starting Jack Client! " << endl;
jack_set_process_callback(this->client, this->callback_process, this);
output_port = new jack_port_t*[nChannels];
for (int i=0; i<nOutputs; i++)
{
std::string tmp_str = "output_" + std::to_string(i+1);
output_port[i] = jack_port_register (client, tmp_str.c_str(),
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput, 0);
}
jack_activate(this->client);
jack_connect(client, jack_port_name(output_port[0]), "system:playback_1");
jack_connect(client, jack_port_name(output_port[1]), "system:playback_2");
sleep(-1);
}
int GranularExample::process (jack_nframes_t nframes)
{
// allocate output buffer array
jack_default_audio_sample_t **out = new jack_default_audio_sample_t*[nChannels];
for ( int i=0 ; i<nChannels; i++)
{
// buffer size can change
out[i] = (jack_default_audio_sample_t *) jack_port_get_buffer(this->output_port[i], jack_get_buffer_size(client));
}
grainer->set_relative_distance(oscman->get_speed());
// loop over all samples of output buffer
for(int sampCNT=0; sampCNT<nframes; sampCNT++)
{
out[0][sampCNT] = grainer->get_sample();
}
return 0;
}
int GranularExample::callback_process(jack_nframes_t x, void* object)
{
return static_cast<GranularExample*>(object)->process(x);
}
/**
* \file wav_example.h
* \class WavExample
*
* \brief Simple example, passing the input to the output
* with gain modification through OSC.
*
* \author Henrik von Coler
*
* \date $Date: 2019/03/18 $
*
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<jack/jack.h>
#include "oscman.h"
#include "grainplayer.h"
using std::cout;
using std::endl;
class GranularExample{
private:
///
/// \brief sample
///
GrainPlayer *grainer;
///
/// \brief fs
/// sample rate of the jack server
int fs = 0;
///
/// \brief nOutputs
/// number off jack output ports
int nOutputs = 2;
///
/// \brief nChannels
/// number of channels in the sample
int nChannels;
///
/// \brief ports
/// the jack output ports
const char **ports;
jack_client_t *client;
jack_status_t status;
jack_port_t **output_port;
/// The OSC manager object
OscMan *oscman;
///
/// \brief process
/// get the next buffer from the sample and playback
/// \param nframes
/// \return
///
int process(jack_nframes_t nframes);
///
/// \brief callback_process
/// \param x
/// \param object
/// \return
///
static int callback_process(jack_nframes_t x, void* object);
public:
///
/// \brief GainExample
/// Constructor!
GranularExample(std::string filename, int win_size, int nWindows);
};
/**
* \file main.cpp
*
* \brief Simple example for wav playback.
*
* \author Henrik von Coler
*
* \date $Date: 2019/03/22 $
*
*/
#include"granular_example.h"
using std::cout;
using std::endl;
int main(int argc, char *argv[]){
std::string filename;
int window_size;
int nWindows;
for (int i = 1; i < argc; i++)
{
if (i + 1 != argc)
{
if (strcmp(argv[i], "-f") == 0)
{
filename = argv[i + 1];
i++;
}
if (strcmp(argv[i], "-l") == 0)
{
window_size = atoi(argv[i + 1]);
i++;
}
if (strcmp(argv[i], "-n") == 0)
{
nWindows = atoi(argv[i + 1]);
i++;
}
}
}
GranularExample * t = new GranularExample(filename, window_size, nWindows);
}
/**
* \file oscman.cpp
* \class OscMan
*
* \brief Class which parses the incoming OSC messages.
*
* \author Henrik von Coler
*
* \date 2019/03/18
*
*/
#include "oscman.h"
OscMan::OscMan(int p)
{
port = p;
speed = 0;
try
{
st = new lo::ServerThread(port);
}
catch (int e)
{
std::cout << "Possibly a bad port!" << std::endl;
}
// Add the example handler to the server!
st->add_method("/speed", "f", speed_callback, this);
st->start();
std::cout << "Started OSC Server!" << std::endl;
}
int OscMan::speed_callback(const char *path, const char *types, lo_arg ** argv,
int argc, void *data, void *user_data )
{
// Do a static cast
OscMan* statCast = static_cast<OscMan*>(user_data);
std::cout << "Changed playback speed to: " << argv[0]->f << std::endl;
statCast->speed = argv[0]->f;
}
double OscMan::get_speed()
{
return speed;
}
/**
* \file oscman.h
* \class OscMan
*
* \brief Class which parses the incoming OSC messages.
*
* \author Henrik von Coler
*
* \date 2019/03/18$
*
*/
#ifndef OSCMAN_H
#define OSCMAN_H
// works with 'self built'
#include <lo/lo.h>
#include <lo/lo_cpp.h>
#include<iostream>
#include<vector>
#include<string>
class OscMan
{
private:
/// the port to be opened
int port;
lo::ServerThread *st;
double speed;
///
////// \brief OscMan::speed_callback
////// \param path
////// \param types
////// \param argv
////// \param argc
////// \param data
////// \param user_data
////// \return
static int speed_callback(const char *path, const char *types, lo_arg ** argv,
int argc, void *data, void *user_data);
public:
///
/// \brief OscMan::OscMan
/// \param p
///
OscMan(int p);
///
/// \brief OscMan::get_speed
/// \return
///
double get_speed();
};
#endif
/**
* \file singlesample.cpp
* \class SingleSample
*
* \brief Class which holds the waveform of a sample in an array.
*
* This class holds the waveform of a sample in an array and provides
* all necessary methods for reading and accessing it.
*
* \author Henrik von Coler
*
* \version $Revision: 0.52$
*
* \date 2016-11-22
*
*
*/
#include "singlesample.h"
SingleSample::SingleSample(std::string filePath, int fs)
{
// set default values
rate = 1;
pos = 0.0;
FS = fs;
// read the data
read_wavefile(filePath);
}
double SingleSample::get_sample(int chan, double pos)
{
// Use linear interpolation to
// get the next sample at the floating point
// position index!
if(pos>=0 && pos<=L)
{
double nearRound;
double fract = modf(pos,&nearRound);
int lowerBound = (int) nearRound;
int upperBound = lowerBound +1;
double lowerSP = x[chan][lowerBound];
double upperSP = x[chan][upperBound];
double interp = (1-fract)*lowerSP + fract*upperSP;
return interp;
}
else
{
return 0;
cout << "Out of range!: " << endl;
}
}
void SingleSample::step()
{
double R = rate * resFac;
pos = pos+R;
// loop in forward playback
if(pos>nFrames)
pos = 0 + pos-(double)nFrames;
// loop in reverse playback
if(pos<0)
pos = nFrames;
}
void SingleSample::read_wavefile(std::string filePath)
{
cout << "Reading: " << filePath << endl;
// Open the audio file using sndfile!
info.format = 0;
sf = sf_open(filePath.c_str(),SFM_READ,&info);
if (sf == NULL)
{
printf("Failed to open the audio file.\n");
//exit(-1);
}
// write wav parameters to member variables
nFrames = info.frames;
fs_sample = info.samplerate;
nChannels = info.channels;
L = info.frames * info.channels;
//
resFac = ((double) fs_sample / (double) FS );
cout << "GR: " << resFac << endl;
cout << "Fs:\t\t" << fs_sample << endl;
cout << "Num Channels:\t" << nChannels << endl;
cout << "Num Samples:\t" << nFrames << endl;
// read interleaved data
double* tmp = new double[L];
sf_count_t num = sf_read_double(sf,tmp,L);
cout << "Read Frames:\t" << num << " of " << L << endl;
sf_close(sf);
// allocate memory for deinterleaved channels
x = new double*[nChannels];
for(int i=0; i< nChannels; i++)
{
x[i] = new double[nFrames];
}
// deinterleave into 'separate' arrays for L and R
int i=0;
for(int sampCNT=0; sampCNT<nFrames; sampCNT++)
{
for(int chanCNT=0; chanCNT<nChannels; chanCNT++)
{
x[chanCNT][sampCNT] = tmp[i];
i++;
}
}
}
SingleSample::~SingleSample()
{
}
double SingleSample::get_rate()
{
return rate;
}
void SingleSample::set_rate(double r)
{
rate = r;
}
double SingleSample::get_pos()
{
return pos;
}
void SingleSample::set_pos(double p)
{
}
int SingleSample::get_nFrames()
{
return nFrames;
}
double** SingleSample::get_x()
{
return x;
}
void SingleSample::set_f(int in)
{
nFrames = in;
}
void SingleSample::set_sr(int in)
{
fs_sample = in;
}
void SingleSample::set_nChannels(int in)
{
nChannels = in;
}
int SingleSample::get_nChannels()
{
return nChannels;
}
double SingleSample::get_sample(int chan, int pos)