Skip to content

Commit

Permalink
Merge pull request #194 from viniciusfersil123/granular_player
Browse files Browse the repository at this point in the history
feat: adds new granular player module and sampling folder
  • Loading branch information
beserge committed Dec 11, 2023
2 parents 0b23cbb + e0ad5e6 commit 5c111c9
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 0 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Source/PhysicalModeling/pluck.cpp
Source/PhysicalModeling/resonator.cpp
Source/PhysicalModeling/KarplusString.cpp
Source/PhysicalModeling/stringvoice.cpp
Source/Sampling/granularplayer.cpp
Source/Synthesis/blosc.cpp
Source/Synthesis/fm2.cpp
Source/Synthesis/formantosc.cpp
Expand Down
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ resonator \
stringvoice
#PolyPluck

SAMPLING_MOD_DIR = Sampling
SAMPLING_MODULES = \
granularplayer \

SYNTHESIS_MOD_DIR = Synthesis
SYNTHESIS_MODULES = \
blosc \
Expand Down Expand Up @@ -114,6 +118,7 @@ CPP_SOURCES += $(addsuffix .cpp, $(MODULE_DIR)/$(EFFECTS_MOD_DIR)/$(EFFECTS_MODU
CPP_SOURCES += $(addsuffix .cpp, $(MODULE_DIR)/$(FILTER_MOD_DIR)/$(FILTER_MODULES))
CPP_SOURCES += $(addsuffix .cpp, $(MODULE_DIR)/$(NOISE_MOD_DIR)/$(NOISE_MODULES))
CPP_SOURCES += $(addsuffix .cpp, $(MODULE_DIR)/$(PHYSICAL_MODELING_MOD_DIR)/$(PHYSICAL_MODELING_MODULES))
CPP_SOURCES += $(addsuffix .cpp, $(MODULE_DIR)/$(SAMPLING_MOD_DIR)/$(SAMPLING_MODULES))
CPP_SOURCES += $(addsuffix .cpp, $(MODULE_DIR)/$(SYNTHESIS_MOD_DIR)/$(SYNTHESIS_MODULES))
CPP_SOURCES += $(addsuffix .cpp, $(MODULE_DIR)/$(UTILITY_MOD_DIR)/$(UTILITY_MODULES))

Expand Down Expand Up @@ -196,6 +201,7 @@ C_INCLUDES = \
-I$(MODULE_DIR)/$(FILTER_MOD_DIR) \
-I$(MODULE_DIR)/$(NOISE_MOD_DIR) \
-I$(MODULE_DIR)/$(PHYSICAL_MODELING_MOD_DIR) \
-I$(MODULE_DIR)/$(SAMPLING_MOD_DIR) \
-I$(MODULE_DIR)/$(SYNTHESIS_MOD_DIR) \
-I$(MODULE_DIR)/$(UTILITY_MOD_DIR)

Expand Down
79 changes: 79 additions & 0 deletions Source/Sampling/granularplayer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include "granularplayer.h"

using namespace daisysp;

void GranularPlayer::Init(float* sample, int size, float sample_rate)
{
/*initialize variables to private members*/
sample_ = sample;
size_ = size;
sample_rate_ = sample_rate;
/*initialize phasors. phs2_ is initialized with a phase offset of 0.5f to create an overlapping effect*/
phs_.Init(sample_rate_, 0, 0);
phsImp_.Init(sample_rate_, 0, 0);
phs2_.Init(sample_rate_, 0, 0.5f);
phsImp2_.Init(sample_rate_, 0, 0);
/*calculate sample frequency*/
sample_frequency_ = sample_rate_ / size_;
/*initialize half cosine envelope*/
for(int i = 0; i < 256; i++)
{
cosEnv_[i] = sinf((i / 256.0f) * M_PI);
}
}

uint32_t GranularPlayer::WrapIdx(uint32_t idx, uint32_t sz)
{
/*wraps idx to sz*/
if(idx > sz)
{
idx = idx - sz;
return idx;
}

return idx;
}

float GranularPlayer::CentsToRatio(float cents)
{
/*converts cents to ratio*/
return powf(2.0f, cents / 1200.0f);
}


float GranularPlayer::MsToSamps(float ms, float samplerate)
{
/*converts milliseconds to number of samples*/
return (ms * 0.001f) * samplerate;
}

float GranularPlayer::NegativeInvert(Phasor* phs, float frequency)
{
/*inverts the phase of the phasor if the frequency is negative, mimicking pure data's phasor~ object*/
return (frequency > 0) ? phs->Process() : ((phs->Process() * -1) + 1);
}

float GranularPlayer::Process(float speed,
float transposition,
float grain_size)
{
grain_size_ = grain_size;
speed_ = speed * sample_frequency_;
transposition_ = (CentsToRatio(transposition) - speed)
* (grain_size >= 1 ? 1000 / grain_size_ : 1);
phs_.SetFreq(fabs((speed_ / 2)));
phs2_.SetFreq(fabs((speed_ / 2)));
phsImp_.SetFreq(fabs(transposition_));
phsImp2_.SetFreq(fabs(transposition_));
idxSpeed_ = NegativeInvert(&phs_, speed_) * size_;
idxSpeed2_ = NegativeInvert(&phs2_, speed_) * size_;
idxTransp_ = (NegativeInvert(&phsImp_, transposition_)
* MsToSamps(grain_size_, sample_rate_));
idxTransp2_ = (NegativeInvert(&phsImp2_, transposition_)
* MsToSamps(grain_size_, sample_rate_));
idx_ = WrapIdx((uint32_t)(idxSpeed_ + idxTransp_), size_);
idx2_ = WrapIdx((uint32_t)(idxSpeed2_ + idxTransp2_), size_);
sig_ = sample_[idx_] * cosEnv_[(uint32_t)(phs_.Process() * 256)];
sig2_ = sample_[idx2_] * cosEnv_[(uint32_t)(phs2_.Process() * 256)];
return (sig_ + sig2_) / 2;
}
88 changes: 88 additions & 0 deletions Source/Sampling/granularplayer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#pragma once
#ifndef DSY_GRANULARPLAYER_H
#define DSY_GRANULARPLAYER_H

#include <stdint.h>
#include <cmath>
#include "Control/phasor.h"
#ifdef __cplusplus
#ifndef M_PI
#define M_PI 3.14159265358979323846 /* pi */
#endif

namespace daisysp
{
/** GranularPlayer Module
Date: November, 2023
Author: Vinícius Fernandes
GranularPlayer is a lookup table player that provides independent time stretching and pitch shifting
via granulation.
Inspired by the grain.player object from else pure data's library.
*/

class GranularPlayer
{
public:
GranularPlayer() {}
~GranularPlayer() {}

/** Initializes the GranularPlayer module.
\param sample pointer to the sample to be played
\param size number of elements in the sample array
\param sample_rate audio engine sample rate
*/
void Init(float* sample, int size, float sample_rate);

/** Processes the granular player.
\param speed playback speed. 1 is normal speed, 2 is double speed, 0.5 is half speed, etc. Negative values play the sample backwards.
\param transposition transposition in cents. 100 cents is one semitone. Negative values transpose down, positive values transpose up.
\param grain_size grain size in milliseconds. 1 is 1 millisecond, 1000 is 1 second. Does not accept negative values. Minimum value is 1.
*/
float Process(float speed, float transposition, float grain_size);

private:
//Wraps an index to the size of the sample array
uint32_t WrapIdx(uint32_t idx, uint32_t size);

//Converts cents(1/100th of a semitone) to a ratio
float CentsToRatio(float cents);

//Converts milliseconds to number of samples
float MsToSamps(float ms, float samplerate);

//Inverts the phase of the phasor if the frequency is negative, mimicking pure data's phasor~ object
float NegativeInvert(Phasor* phs, float frequency);


float* sample_; //pointer to the sample to be played
float sample_rate_; //audio engine sample rate
int size_; //number of elements in the sample array
float grain_size_; //grain size in milliseconds
float speed_; //processed playback speed.
float transposition_; //processed transpotion.
float sample_frequency_;
float cosEnv_[256] = {0}; //cosine envelope for crossfading between grains
float
idxTransp_; // Adjusted Transposition value contribution to idx of first grain
float
idxTransp2_; // Adjusted Transposition value contribution to idx of second grain
float idxSpeed_; // Adjusted Speed value contribution to idx of first grain
float
idxSpeed2_; // Adjusted Speed value contribution to idx of second grain
float sig_; // Output of first grain
float sig2_; // Output of second grain

uint32_t idx_; // Index of first grain
uint32_t idx2_; // Index of second grain

Phasor phs_; // Phasor for speed
Phasor phsImp_; // Phasor for transposition
Phasor phs2_; // Phasor for speed
Phasor phsImp2_; // Phasor for transposition
};
} // namespace daisysp
#endif
#endif
3 changes: 3 additions & 0 deletions Source/daisysp.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@
#include "PhysicalModeling/KarplusString.h"
#include "PhysicalModeling/stringvoice.h"

/** Sampling Modules */
#include "Sampling/granularplayer.h"

/** Synthesis Modules */
#include "Synthesis/blosc.h"
#include "Synthesis/fm2.h"
Expand Down

0 comments on commit 5c111c9

Please sign in to comment.