Skip to content

Commit 2d1c7f9

Browse files
authored
Merge pull request #3 from supercollider/chapter-29
Chapter 29
2 parents a112d4e + b5fc2c8 commit 2d1c7f9

13 files changed

+1010
-211
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build**
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
cmake_minimum_required (VERSION 3.5)
2+
3+
set(PROJECT "Flanger")
4+
message(STATUS "Project name is ${PROJECT}")
5+
project (${PROJECT})
6+
7+
# Specify the source file
8+
set(FILENAME "Figure_29-10_Flanger_2.cpp") # old-style version
9+
# set(FILENAME "Figure_29-10b_Flanger_2 C++ interface.cpp") # modern C++ version
10+
11+
include (CheckCXXCompilerFlag)
12+
13+
# set default build type to Release
14+
if (NOT CMAKE_BUILD_TYPE)
15+
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE)
16+
endif()
17+
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
18+
19+
set(SC_PATH "" CACHE PATH "Path to SuperCollider source code")
20+
message(STATUS "SC_PATH: ${SC_PATH}")
21+
include_directories(${SC_PATH}/include/plugin_interface)
22+
include_directories(${SC_PATH}/include/common)
23+
include_directories(${SC_PATH}/common)
24+
25+
if (WIN32)
26+
set(SC_INSTALLDIR "$ENV{LOCALAPPDATA}/SuperCollider/Extensions/" CACHE PATH "Installation directoy")
27+
elseif(APPLE)
28+
set(SC_INSTALLDIR "~/Library/Application Support/SuperCollider/Extensions/" CACHE PATH "Installation directoy")
29+
else()
30+
set(SC_INSTALLDIR "~/.local/share/SuperCollider/Extensions/" CACHE PATH "Installation directoy")
31+
endif()
32+
message(STATUS "SC_INSTALLDIR: ${SC_INSTALLDIR}")
33+
34+
option(SUPERNOVA "Build plugins for supernova" OFF)
35+
message(STATUS "Build Supernova version: ${SUPERNOVA}")
36+
37+
# no library prefix!
38+
set(CMAKE_SHARED_MODULE_PREFIX "")
39+
# set plugin extension
40+
if (APPLE OR WIN32)
41+
set(CMAKE_SHARED_MODULE_SUFFIX ".scx")
42+
endif()
43+
44+
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
45+
set(CMAKE_COMPILER_IS_CLANG 1)
46+
endif()
47+
48+
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG)
49+
# hide all symbols by default
50+
add_definitions(-fvisibility=hidden)
51+
52+
CHECK_CXX_COMPILER_FLAG(-msse HAS_SSE)
53+
if (HAS_SSE)
54+
add_compile_options(-msse)
55+
endif()
56+
57+
CHECK_CXX_COMPILER_FLAG(-msse2 HAS_SSE2)
58+
if (HAS_SSE2)
59+
add_compile_options(-msse2)
60+
endif()
61+
62+
# it's safe to assume at least SSE3
63+
CHECK_CXX_COMPILER_FLAG(-msse3 HAS_SSE3)
64+
if (HAS_SSE3)
65+
add_compile_options(-msse3)
66+
endif()
67+
68+
CHECK_CXX_COMPILER_FLAG(-mfpmath=sse HAS_FPMATH_SSE)
69+
if (HAS_FPMATH_SSE)
70+
add_compile_options(-mfpmath=sse)
71+
endif()
72+
73+
option(NATIVE "Optimize for this machine (not portable!)" OFF)
74+
if (NATIVE)
75+
add_compile_options(-march=native)
76+
endif()
77+
78+
if (CMAKE_COMPILER_IS_CLANG)
79+
add_compile_options(-stdlib=libc++)
80+
endif()
81+
endif()
82+
83+
if(MINGW)
84+
add_compile_options(-mstackrealign)
85+
endif()
86+
87+
add_library(${PROJECT} MODULE ${FILENAME})
88+
89+
if(SUPERNOVA)
90+
add_library(${PROJECT}_supernova MODULE ${FILENAME})
91+
target_compile_definitions(${PROJECT}_supernova PRIVATE SUPERNOVA)
92+
target_include_directories(${PROJECT}_supernova PRIVATE ${SC_PATH}/external_libraries/nova-tt)
93+
# for <boost/atomic.hpp>
94+
target_include_directories(${PROJECT}_supernova PRIVATE ${SC_PATH}/external_libraries/boost)
95+
endif()
96+
97+
# installation
98+
99+
install(TARGETS ${PROJECT} DESTINATION "${SC_INSTALLDIR}/${PROJECT}/plugins")
100+
101+
if (SUPERNOVA)
102+
install(TARGETS ${PROJECT}_supernova DESTINATION "${SC_INSTALLDIR}/${PROJECT}/plugins")
103+
endif()
104+
105+
install(FILES "Flanger.sc" DESTINATION "${SC_INSTALLDIR}/${PROJECT}/classes")
106+
install(FILES "Flanger.schelp" DESTINATION "${SC_INSTALLDIR}/${PROJECT}/HelpSource/Classes")
107+
install(FILES "README.md" "LICENSE.txt" DESTINATION "${SC_INSTALLDIR}/${PROJECT}")

Ch 29 Writing Unit Generator Plug-ins/Figure_29-10_Flanger_2.cpp

Lines changed: 41 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ static InterfaceTable *ft;
88
struct Flanger : public Unit {
99
// it is a convention to use some kind of prefix (or postfix)
1010
// to distinguish member variables from local variables
11-
float mRate;
11+
float mModRate;
1212
float mDelaysize;
1313
float mAdvance;
1414
float mReadpos;
1515
int mWritepos;
16-
// a pointer to the memory we'll use for our internal delay
16+
// a pointer to the memory we'll use for our internal delay
1717
float *mDelayline;
1818
};
1919

@@ -27,90 +27,81 @@ void Flanger_Ctor(Flanger *unit) {
2727
// Rather than using rate directly, we're going to calculate the size of
2828
// jumps we must make each time to scan through the delayline at "rate"
2929
unit->mAdvance = ((unit->mDelaysize * rate) / SAMPLERATE) + 1.0f;
30-
unit->mRate = rate;
30+
unit->mModRate = rate;
3131
unit->mWritepos = 0;
3232
unit->mReadpos = 0;
33-
34-
// Allocate the delay line
33+
34+
// Allocate the delay line
3535
unit->mDelayline = (float*)RTAlloc(unit->mWorld, (int)unit->mDelaysize * sizeof(float));
3636
// Check the result of RTAlloc because it can fail if the RT pool is too small!
37-
if (!unit->mDelayline)
38-
{
39-
Print("RTAlloc failed!");
40-
// clear outputs, set calc function and set done
41-
ClearUnitOutputs(unit, 1);
42-
SETCALC(ClearUnitOutputs);
43-
unit->mDone = true;
44-
return;
45-
}
37+
ClearUnitIfMemFailed(unit->mDelayline);
4638
// Set the delay line to zeros.
4739
memset(unit->mDelayline, 0, unit->mDelaysize * sizeof(float));
48-
49-
// IMPORTANT: This tells scsynth the name of the calculation function for this UGen.
50-
SETCALC(Flanger_next);
51-
52-
// Should also calc 1 sample's worth of output - ensures each ugen's "pipes" are "primed"
53-
Flanger_next(unit, 1);
40+
41+
// IMPORTANT: This tells scsynth the name of the calculation function for this UGen.
42+
SETCALC(Flanger_next);
43+
44+
// Should also calc 1 sample's worth of output - ensures each ugen's "pipes" are "primed"
45+
Flanger_next(unit, 1);
5446
}
5547

5648
void Flanger_next(Flanger *unit, int inNumSamples) {
57-
float *in = IN(0);
49+
float *in = IN(0);
5850
float *out = OUT(0);
5951

6052
// "rate" and "depth" can be modulated at control rate
6153
float currate = IN0(1);
62-
float depth = IN0(2);
63-
54+
float depth = IN0(2);
55+
6456
// The compiler doesn't know that "out" can't possibly point
6557
// to one of our members, so it would have to reload them from
6658
// memory on every loop iteration. To prevent this from happening,
6759
// we temporarily store them in local variables.
68-
float rate = unit->mRate;
60+
float rate = unit->mModRate;
6961
float advance = unit->mAdvance;
7062
float readpos = unit->mReadpos;
7163
int writepos = unit->mWritepos;
7264
const float delaysize = unit->mDelaysize; // this one is fixed
7365
float *delayline = unit->mDelayline;
74-
66+
7567
if (rate != currate) {
76-
// rate input needs updating
77-
rate = currate;
78-
advance = ((delaysize * rate * 2) / SAMPLERATE) + 1.0f;
79-
}
80-
68+
// rate input needs updating
69+
rate = currate;
70+
advance = ((delaysize * rate) / SAMPLERATE) + 1.0f;
71+
}
72+
8173
for (int i = 0; i < inNumSamples; ++i) {
8274
float val = in[i];
83-
84-
// Write to the delay line
85-
delayline[writepos++] = val;
75+
76+
// Write to the delay line
77+
delayline[writepos++] = val;
8678
if(writepos == delaysize)
87-
writepos = 0;
88-
89-
// Read from the delay line
79+
writepos = 0;
80+
81+
// Read from the delay line
9082
float delayed = delayline[(int)readpos];
9183
readpos += advance;
92-
// Update position, NB we may be moving forwards or backwards (depending on input)
84+
// Update position, NB we may be moving forwards or backwards (depending on input)
9385
while(readpos >= delaysize)
94-
readpos -= delaysize;
86+
readpos -= delaysize;
9587
while(readpos < 0)
96-
readpos += delaysize;
97-
98-
// Mix dry and wet together, and output them
99-
out[i] = val + (delayed * depth);
100-
}
101-
88+
readpos += delaysize;
89+
90+
// Mix dry and wet together, and output them
91+
out[i] = val + (delayed * depth);
92+
}
93+
10294
// store them back
103-
unit->mRate = rate;
95+
unit->mModRate = rate;
10496
unit->mAdvance = advance;
10597
unit->mWritepos = writepos;
10698
unit->mReadpos = readpos;
107-
}
99+
}
108100

109101
void Flanger_Dtor(Flanger *unit) {
110-
// check in case RTFree failed in the Ctor
111-
if (unit->mDelayline)
112-
RTFree(unit->mWorld, unit->mDelayline);
113-
}
102+
// NB: it's ok to pass NULL to RTFree()
103+
RTFree(unit->mWorld, unit->mDelayline);
104+
}
114105

115106
PluginLoad(InterfaceTable *inTable) {
116107
ft = inTable;

Ch 29 Writing Unit Generator Plug-ins/Figure_29-10b_Flanger_2 C++ interface.cpp

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class Flanger : public SCUnit {
1313
private:
1414
// it is a convention to use some kind of prefix (or postfix)
1515
// to distinguish member variables from local variables
16-
float mRate;
16+
float mModRate;
1717
float mDelaysize;
1818
float mAdvance;
1919
float mReadpos;
@@ -25,42 +25,34 @@ class Flanger : public SCUnit {
2525
Flanger::Flanger() {
2626
// Here we must initialise *all* state variables in our Flanger struct.
2727
mDelaysize = sampleRate() * 0.02f; // Fixed 20ms max delay
28-
float rate = IN0(1); // initial rate
28+
float rate = in0(1); // initial rate
2929
// Rather than using rate directly, we're going to calculate the size of
3030
// jumps we must make each time to scan through the delayline at "rate"
3131
mAdvance = ((mDelaysize * rate) / sampleRate()) + 1.0f;
32-
mRate = rate;
32+
mModRate = rate;
3333
mWritepos = 0;
3434
mReadpos = 0;
3535

3636
// Allocate the delay line
3737
mDelayline = (float*)RTAlloc(mWorld, (int)mDelaysize * sizeof(float));
3838
// Check the result of RTAlloc because it can fail if the RT pool is too small!
39-
if (!mDelayline)
40-
{
41-
Print("RTAlloc failed!");
42-
// clear outputs, set calc function and set done
43-
ClearUnitOutputs(unit, 1);
44-
SETCALC(ClearUnitOutputs);
45-
mDone = true;
46-
return;
47-
}
39+
auto unit = this;
40+
ClearUnitIfMemFailed(mDelayline);
4841
// Set the delay line to zeros.
4942
memset(mDelayline, 0, mDelaysize * sizeof(float));
5043

5144
// sets the calc function and automatically computes 1 sample.
52-
set_calc_function<&Flanger::next>();
45+
set_calc_function<Flanger, &Flanger::next>();
5346
}
5447

5548
Flanger::~Flanger() {
56-
// check in case RTFree failed in the Ctor
57-
if (mDelayline)
58-
RTFree(mWorld, mDelayline);
49+
// NB: it's ok to pass NULL to RTFree()
50+
RTFree(mWorld, mDelayline);
5951
}
6052

61-
void Flanger::next(int inNumSamples ) {
62-
float *in = in(0);
63-
float *out = out(0);
53+
void Flanger::next(int inNumSamples) {
54+
const float *input = in(0);
55+
float *output = out(0);
6456

6557
// "rate" and "depth" can be modulated at control rate
6658
float currate = in0(1);
@@ -70,7 +62,7 @@ void Flanger::next(int inNumSamples ) {
7062
// to one of our members, so it would have to reload them from
7163
// memory on every loop iteration. To prevent this from happening,
7264
// we temporarily store them in local variables.
73-
float rate = mRate;
65+
float rate = mModRate;
7466
float advance = mAdvance;
7567
float readpos = mReadpos;
7668
int writepos = mWritepos;
@@ -80,11 +72,11 @@ void Flanger::next(int inNumSamples ) {
8072
if (rate != currate) {
8173
// rate input needs updating
8274
rate = currate;
83-
advance = ((delaysize * rate * 2) / SAMPLERATE) + 1.0f;
75+
advance = ((delaysize * rate) / sampleRate()) + 1.0f;
8476
}
8577

8678
for (int i = 0; i < inNumSamples; ++i) {
87-
float val = in[i];
79+
float val = input[i];
8880

8981
// Write to the delay line
9082
delayline[writepos++] = val;
@@ -101,11 +93,11 @@ void Flanger::next(int inNumSamples ) {
10193
readpos += delaysize;
10294

10395
// Mix dry and wet together, and output them
104-
out[i] = val + (delayed * depth);
96+
output[i] = val + (delayed * depth);
10597
}
10698

10799
// store them back
108-
mRate = rate;
100+
mModRate = rate;
109101
mAdvance = advance;
110102
mWritepos = writepos;
111103
mReadpos = readpos;

0 commit comments

Comments
 (0)