Skip to content

Instantly share code, notes, and snippets.

@illiichi
Last active August 29, 2015 14:14
Show Gist options
  • Save illiichi/5d8baacf60ea5a5b2b08 to your computer and use it in GitHub Desktop.
Save illiichi/5d8baacf60ea5a5b2b08 to your computer and use it in GitHub Desktop.
サイン波のUGenを自力で実装してみる
cmake_minimum_required (VERSION 2.8)
project (MySine)
include_directories(${SC_PATH}/include/plugin_interface)
include_directories(${SC_PATH}/include/common)
include_directories(${SC_PATH}/external_libraries/libsndfile/)
set(CMAKE_SHARED_MODULE_PREFIX "")
if(APPLE OR WIN32)
set(CMAKE_SHARED_MODULE_SUFFIX ".scx")
endif()
add_library(MySine MODULE MySine.cpp)
/***
サイン波Ugenの独自実装(サーバーサイド)
各Ugenは主に下記の3つの定義により作られます。
1. コンストラクタ
2. 波形を作る関数
3. Ugenの情報を受け渡しするための構造体
2.の関数では、一定期間の波形を生成し、それを何度も呼び出すことで全体の波形を生成します。
それぞれの関数の呼び出しが終わる時点で、3.で定義した構造体に次の波形を作るための情報を入れておき、
次に関数が呼ばれた時にその情報を元に次の波形を生成します。
□現在の時刻の考え方
SC_PlugIn.hをインクルードすると定義される定数 SAMPLEDUR はサンプルとサンプルの間の時間幅を表します。
よって、1サンプルごとにSAMPLEDURずつ時刻を増加したものが、現在の時刻になります。(音の開始時点の時刻を0とします)
時刻tについて、周波数fのサイン波は以下の式になります。
f(t) = sin(2π・f・t)
または、サイン波は2πの周期をもっているので、
サイン波の1周期の中のどの位置にいるか(位相)を基準に波形を生成することができます。
位相θについて、周波数fのサイン波は以下の式になります。
f(θ) = sin(f・θ)
時間Δt経過したときの位相の増分Δθは下記の式になります。
Δθ=2・π・Δt
*/
#include "SC_PlugIn.h"
static InterfaceTable *ft;
// 構造体を定義する
// Unit構造体を継承しているため、ここで定義した変数以外もいろいろ入っている。
//
struct MySine : public Unit
{
double phase;
};
static void MySine_next(MySine *unit, int inNumSamples);
static void MySine_Ctor(MySine* unit);
// コンストラクタを定義する
void MySine_Ctor(MySine* unit)
{
SETCALC(MySine_next); // 波形を作る関数を定義する
unit->phase = 0.0; // 構造体の初期値の設定
MySine_next(unit, 1); // 最初の波形生成
}
#define SINE_PERIOD (2.0 * M_PI)
// 波形を作る関数
//
// MySine *unit 波形を作るための情報を持つ構造体
// int inNumSamples この関数一回の呼び出しで何サンプル分の波形を生成するか
void MySine_next(MySine *unit, int inNumSamples)
{
// // 出力波形を入れる配列。引数で何番目のチャネルかを指定する。
float *out = OUT(0);
// Ugenの第一引数の値を取得。IN0(X)はIN(X)[0]と同じ。ここでの引数は何番目の引数かを表す。
float freq = IN0(0);
// 第2引数で指定した数だけ倍音を足せるように拡張
float n = IN0(1);
// 前回の終わりの時点での値を取り出す。
double phase = unit->phase;
// inNumSamples回のループして、out[0] ~ out[inNumSamples - 1]に生成した波形を代入する
for (int i=0; i < inNumSamples; ++i)
{
float total = 0.0;
for(double k=1.0; k <= n; ++k)
{
total += sin(k * freq * phase) / k;
}
// i番目の波形の値を代入
out[i] = total;
// 1サンプルあたりの位相の増分を加算して、次の位相を設定する
phase += SINE_PERIOD * SAMPLEDUR;
}
// 位相を周期の範囲に収める
while(phase >= SINE_PERIOD){
phase -= SINE_PERIOD;
}
// 次に関数を呼び出した時の開始する位相を格納しておく
unit->phase = phase;
}
PluginLoad(MySine)
{
ft = inTable;
DefineSimpleUnit(MySine);
}
/****
□実行例
// 30秒かけてノコギリ波になっていく
{MySine.ar(440, Line.kr(1, 100, 30))}.scope
//Multinewの力でマルチチャンネルでもOK
{MySine.ar([440, 660])}.play
□未対応事項
周波数が変化した時の考慮が実装されていない。
たとえば、SC側で下記のコードを実行するときれいに鳴らない。
{MySine.ar(SinOsc.kr(10, 0, 10) + 440)}.play
*/
/***
サイン波Ugenの独自実装(クライアントサイド)
クライアントサイドでは、SCのコード上でUGenを呼び出すためにどういう引数があるかとそのデフォルト値を決定します。
第一引数は周波数, 第二引数は倍音をいくつ足すかを指定します。第二引数の数を大きくするほどノコギリ波に近くなります。
このコードは、scクライアントの起動時に読み込まれます。
*/
MySine : UGen {
*ar { arg freq = 440.0, n = 1.0, mul = 1.0, add = 0.0;
^this.multiNew('audio', freq, n).madd(mul, add)
}
*kr { arg freq = 440.0, n = 1.0, mul = 1.0, add = 0.0;
^this.multiNew('control', freq, n).madd(mul, add)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment