Skip to content

Added C++ version of MySaw #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions 02b-MySaw/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
set(FILENAME "MySaw2.cpp") #specify the .cpp file here
cmake_minimum_required (VERSION 2.8)
get_filename_component(PROJECT ${FILENAME} NAME_WE) #automatically sets project name from the filename
# set(PROJECT "my_name") #alternatively set project name manually
message(STATUS "Project name is ${PROJECT}")
project (${PROJECT})

include_directories(${SC_PATH}/include/plugin_interface)
include_directories(${SC_PATH}/include/common)
include_directories(${SC_PATH}/common)

set(CMAKE_SHARED_MODULE_PREFIX "")
if(APPLE OR WIN32)
set(CMAKE_SHARED_MODULE_SUFFIX ".scx")
endif()

option(SUPERNOVA "Build plugins for supernova" OFF)
if (SUPERNOVA)
include_directories(${SC_PATH}/external_libraries/nova-tt)
# actually just boost.atomic
include_directories(${SC_PATH}/external_libraries/boost)
include_directories(${SC_PATH}/external_libraries/boost_lockfree)
include_directories(${SC_PATH}/external_libraries/boost-lockfree)
endif()

option(CPP11 "Build with c++11." ON)

if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CMAKE_COMPILER_IS_CLANG 1)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG)
add_definitions(-fvisibility=hidden)

include (CheckCCompilerFlag)
include (CheckCXXCompilerFlag)

CHECK_C_COMPILER_FLAG(-msse HAS_SSE)
CHECK_CXX_COMPILER_FLAG(-msse HAS_CXX_SSE)

if (HAS_SSE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse")
endif()
if (HAS_CXX_SSE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse")
endif()

CHECK_C_COMPILER_FLAG(-msse2 HAS_SSE2)
CHECK_CXX_COMPILER_FLAG(-msse2 HAS_CXX_SSE2)

if (HAS_SSE2)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse2")
endif()
if (HAS_CXX_SSE2)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse2")
endif()

CHECK_C_COMPILER_FLAG(-mfpmath=sse HAS_FPMATH_SSE)
CHECK_CXX_COMPILER_FLAG(-mfpmath=sse HAS_CXX_FPMATH_SSE)

if (HAS_FPMATH_SSE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse")
endif()
if (HAS_CXX_FPMATH_SSE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse")
endif()

if(NATIVE)
add_definitions(-march=native)
endif()

if(CPP11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
if(CMAKE_COMPILER_IS_CLANG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif()
endif()
endif()
if(MINGW)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mstackrealign")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mstackrealign")
endif()

add_library(${PROJECT} MODULE ${FILENAME})
if(SUPERNOVA)
add_library(${PROJECT}_supernova MODULE ${FILENAME})
set_property(TARGET ${PROJECT}_supernova
PROPERTY COMPILE_DEFINITIONS SUPERNOVA)
endif()
136 changes: 136 additions & 0 deletions 02b-MySaw/MySaw.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#include "SC_PlugIn.hpp"

// InterfaceTable contains pointers to functions in the host (server).
static InterfaceTable *ft;

// declare struct to hold unit generator state
struct MySaw : public SCUnit{

// Constructor usually does 3 things.
// 1. set the calculation function.
// 2. initialize the unit generator state variables.
// 3. calculate one sample of output.
public:
MySaw() {
// 1. set the calculation function.
if (isAudioRateIn(0)) {
// if the frequency argument is audio rate
set_calc_function<MySaw,&MySaw::next_a>();
} else {
// if thene frequency argument is control rate (or a scalar).
set_calc_function<MySaw,&MySaw::next_k>();
}

// 2. initialize the unit generator state variables.
// initialize a constant for multiplying the frequency
mFreqMul = 2.0 * sampleDur();
// get initial phase of oscillator
mPhase = in0(1);

// 3. calculate one sample of output.
if (isAudioRateIn(0)) {
next_a(1);
} else {
next_k(1);
}

}

private:
double mPhase; // phase of the oscillator, from -1 to 1.
float mFreqMul; // a constant for multiplying frequency

//////////////////////////////////////////////////////////////////

// The calculation function executes once per control period
// which is typically 64 samples.

// calculation function for an audio rate frequency argument
void next_a(int inNumSamples)
{
// get the pointer to the output buffer
float *outBuf = out(0);

// get the pointer to the input buffer
const float *freq = in(0);

// get phase and freqmul constant from struct and store it in a
// local variable.
// The optimizer will cause them to be loaded it into a register.
float freqmul = mFreqMul;
double phase = mPhase;

// perform a loop for the number of samples in the control period.
// If this unit is audio rate then inNumSamples will be 64 or whatever
// the block size is. If this unit is control rate then inNumSamples will
// be 1.
for (int i=0; i < inNumSamples; ++i)
{
// out must be written last for in place operation
float z = phase;
phase += freq[i] * freqmul;

// these if statements wrap the phase a +1 or -1.
if (phase >= 1.f) phase -= 2.f;
else if (phase <= -1.f) phase += 2.f;

// write the output
outBuf[i] = z;
}

// store the phase back to the struct
mPhase = phase;
}

//////////////////////////////////////////////////////////////////

// calculation function for a control rate frequency argument
void next_k(int inNumSamples)
{
// get the pointer to the output buffer
float *outBuf = out(0);

// freq is control rate, so calculate it once.
float freq = in0(0) * mFreqMul;

// get phase from struct and store it in a local variable.
// The optimizer will cause it to be loaded it into a register.
double phase = mPhase;

// since the frequency is not changing then we can simplify the loops
// by separating the cases of positive or negative frequencies.
// This will make them run faster because there is less code inside the loop.
if (freq >= 0.f) {
// positive frequencies
for (int i=0; i < inNumSamples; ++i)
{
outBuf[i] = phase;
phase += freq;
if (phase >= 1.f) phase -= 2.f;
}
} else {
// negative frequencies
for (int i=0; i < inNumSamples; ++i)
{
outBuf[i] = phase;
phase += freq;
if (phase <= -1.f) phase += 2.f;
}
}

// store the phase back to the struct
mPhase = phase;
}
};

// the entry point is called by the host when the plug-in is loaded
PluginLoad(MySawUGens)
{
// InterfaceTable *inTable implicitly given as argument to the load function
ft = inTable; // store pointer to InterfaceTable

// registerUnit takes the place of the Define*Unit functions. It automatically checks for the presence of a
// destructor function.
// However, it does not seem to be possible to disable buffer aliasing with the C++ header.
registerUnit<MySaw>(ft, "MySaw");
}
9 changes: 9 additions & 0 deletions 02b-MySaw/MySaw.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// without mul and add.
MySaw : UGen {
*ar { arg freq = 440.0, iphase = 0.0;
^this.multiNew('audio', freq, iphase)
}
*kr { arg freq = 440.0, iphase = 0.0;
^this.multiNew('control', freq, iphase)
}
}
136 changes: 136 additions & 0 deletions 02b-MySaw/MySaw2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#include "SC_PlugIn.hpp"

// InterfaceTable contains pointers to functions in the host (server).
static InterfaceTable *ft;

// declare struct to hold unit generator state
struct MySaw2 : public SCUnit{

// Constructor usually does 3 things.
// 1. set the calculation function.
// 2. initialize the unit generator state variables.
// 3. calculate one sample of output.
public:
MySaw2() {
// 1. set the calculation function.
if (isAudioRateIn(0)) {
// if the frequency argument is audio rate
set_calc_function<MySaw2,&MySaw2::next_a>();
} else {
// if thene frequency argument is control rate (or a scalar).
set_calc_function<MySaw2,&MySaw2::next_k>();
}

// 2. initialize the unit generator state variables.
// initialize a constant for multiplying the frequency
mFreqMul = 2.0 * sampleDur();
// get initial phase of oscillator
mPhase = in0(1);

// 3. calculate one sample of output.
if (isAudioRateIn(0)) {
next_a(1);
} else {
next_k(1);
}

}

private:
double mPhase; // phase of the oscillator, from -1 to 1.
float mFreqMul; // a constant for multiplying frequency

//////////////////////////////////////////////////////////////////

// The calculation function executes once per control period
// which is typically 64 samples.

// calculation function for an audio rate frequency argument
void next_a(int inNumSamples)
{
// get the pointer to the output buffer
float *outBuf = out(0);

// get the pointer to the input buffer
const float *freq = in(0);

// get phase and freqmul constant from struct and store it in a
// local variable.
// The optimizer will cause them to be loaded it into a register.
float freqmul = mFreqMul;
double phase = mPhase;

// perform a loop for the number of samples in the control period.
// If this unit is audio rate then inNumSamples will be 64 or whatever
// the block size is. If this unit is control rate then inNumSamples will
// be 1.
for (int i=0; i < inNumSamples; ++i)
{
// out must be written last for in place operation
float z = phase;
phase += freq[i] * freqmul;

// these if statements wrap the phase a +1 or -1.
if (phase >= 1.f) phase -= 2.f;
else if (phase <= -1.f) phase += 2.f;

// write the output
outBuf[i] = z;
}

// store the phase back to the struct
mPhase = phase;
}

//////////////////////////////////////////////////////////////////

// calculation function for a control rate frequency argument
void next_k(int inNumSamples)
{
// get the pointer to the output buffer
float *outBuf = out(0);

// freq is control rate, so calculate it once.
float freq = in0(0) * mFreqMul;

// get phase from struct and store it in a local variable.
// The optimizer will cause it to be loaded it into a register.
double phase = mPhase;

// since the frequency is not changing then we can simplify the loops
// by separating the cases of positive or negative frequencies.
// This will make them run faster because there is less code inside the loop.
if (freq >= 0.f) {
// positive frequencies
for (int i=0; i < inNumSamples; ++i)
{
outBuf[i] = phase;
phase += freq;
if (phase >= 1.f) phase -= 2.f;
}
} else {
// negative frequencies
for (int i=0; i < inNumSamples; ++i)
{
outBuf[i] = phase;
phase += freq;
if (phase <= -1.f) phase += 2.f;
}
}

// store the phase back to the struct
mPhase = phase;
}
};

// the entry point is called by the host when the plug-in is loaded
PluginLoad(MySaw2UGens)
{
// InterfaceTable *inTable implicitly given as argument to the load function
ft = inTable; // store pointer to InterfaceTable

// registerUnit takes the place of the Define*Unit functions. It automatically checks for the presence of a
// destructor function.
// However, it does not seem to be possible to disable buffer aliasing with the C++ header.
registerUnit<MySaw2>(ft, "MySaw2");
}
9 changes: 9 additions & 0 deletions 02b-MySaw/MySaw2.sc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// without mul and add.
MySaw2 : UGen {
*ar { arg freq = 440.0, iphase = 0.0;
^this.multiNew('audio', freq, iphase)
}
*kr { arg freq = 440.0, iphase = 0.0;
^this.multiNew('control', freq, iphase)
}
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Beyond this repository, the reader is encouraged to look at [sc3-plugins](https:
- 01a-BoringMixer -- minimal example of a plugin
- 01b-BoringMixer -- using a newer C++ wrapper
- 02-MySaw -- introduces multiple calculation functions and state variables
- 02b-MySaw -- using a newer C++ wrapper
- 03-AnalogEcho -- introduces memory allocation and cubic interpolation

## Compiling
Expand Down