グラフで見るShaper
Shaperは入力信号を大きく変化させてくれるので僕は大好きな、よく使うクラスです。
前回は最初から特殊な使い方をしてしまったので、今回もう一度Shaperを取り上げたいです。
まずエンベロープの書き方をざっくりおさらいしておきます。
*書いたエンベロープをどう使うかは後で書きます。
Env([ポイント], [比率])
こんな感じでEnv()内にポイントを指定して、各ポイント間の比率を指定します。
例えば
・ポイントが3つ、[-1と0.5と1]
・ポイント間の比率が[1対2]
の場合、
Env([-1, 0.5, 1], [1, 2]).plot;
ポイントはいくつ指定してもOKです。
Env([-1, 0.5, 1, -1], [1, 2, 1]).plot;
比率は、もし全部同じにしたいなら数字をひとつ書けばよくて、つまり下記のふたつは同じ意味です。
Env([-1, 0.5, 1, -1], [1, 1, 1]).plot;
Env([-1, 0.5, 1, -1], [1]).plot;
ポイント間をつなぐ線を直線ではなく曲線にしたい場合は3つ目のパラメータを加えます。
Env([-1, 0.5, 1, -1], [1], [-3]).plot;
カーブも個別に指定でき、プラスとマイナスではカーブの向きが逆になります。
Env([-1, 0.5, 1, -1], [1], [-3, 3, -3]).plot;
エンベロープはもちろん、音量やフィルターなどに時間変化を与える目的で使われるものですが、今回はエンベロープを “波形を変化させるツール” として使います。(ここだけ読むと、どゆこと?となるかもですが、通常のエンベロープとは別の使い方をします、ってことです。。)
(エンベロープの書き方についてはこちらも参考にしてください。こちらの回ではEnvで書いた波形をオシレーターとして鳴らす方法を書いていますが、Envというクラスの扱いについては同じです。)
で、Shaper内でのエンベロープの役割とは?
[サイン波など] ->-> [Shaper] ->-> [新しい波形]
という感じで、何か信号をShaperに通すと、全く違う新たな波形を出力します。いわばShaperはエフェクターのような役割をします。
例えばサイン波を例にすると、
サイン波は時間経過にともなって0→-1→0→+1→0→-1→・・・・を繰り返す波形ですが・・・
このサイン波をShaper内で以下のようなエンベロープに通すと・・・
出力波形は以下のように変化します。
どうしてそうなるのか。。
エンベロープのウィンドウは
・横軸:入ってくる値(下図のピンク色の値)
に対して、
・縦軸:最終的に出て行く値(下図の緑色の値)
というふうに見るのですが、
上のエンベロープの場合、入力が1のとき出力が0.5になってます。
そういうわけで振幅1のサイン波を入力すると振幅0.5のサイン波になってしまうんです。ただしそれが起きるのはさっきのエンベロープだと入力値が正のときだけ。(ピンクが-1のときは緑色も-1。)なので0→-1→0→+0.5→0を繰り返す下記のようなサイン波が出力されます。
次に極端な例として、サイン波を以下のようなエンベロープに通したとしましょう。
(注意)SuperColliderのEnvのplotだと横軸が0、500、1000となってしまいます。なので下図のピンク文字のように、-1、0、1と読み替えてください。。
このエンベロープは入力信号(横軸)がゼロのときも1のときも(その間の0.5などのときも)つまり入力信号が正の値のとき、出力(縦軸)がゼロになってます。
なので入力したサイン波も正の値の部分は全部ゼロに変換されます。最終的な出力波形は以下のようなものに。
これは見た目だけでなく、もちろん出音も変わってきます。
Shaperは入力信号に対して変換をかけて別の波形に書き換えてしまうクラス、ということがわかってきたと思います。面白そうですよね。
使い方としてはシンプルで、Shaperをヘルプで調べると下記のようになってます。
bufnumはエンベロープを入れたバッファナンバー
inは入力信号
です。
作ったエンベロープをShaperに入れる用のフォーマットにする
さて、作ったエンベロープをShaperに入れるために、まずはEnvをバッファに入れる必要があります。その方法は、下記のコードです。
[ex1]
//まずブートサーバしておきます。
(
~crv = Env([-1, 0, 1], [1], [0]).asSignal(1024);
~wt = ~crv.asWavetableNoWrap;
~shp = Buffer.loadCollection(s, ~wt);
)
~crv.plot;
このフォーマットのまま覚えて(またはコピペで)使っていくということでよいと思いますが、一応どんなことをやっているか書いておきます。
******************************************
~crv = Env([-1, 0, 1], [1], [0]).asSignal(1024);
asSignalはEnvで作ったものをSuperCollider内部で数値化(数字の羅列に変換)します。
ここでは1024サンプル分の数値に変換しています。この1024の部分は2のX乗である必要あがり、この数字を下げるとエンベロープの解像度が荒くなり、数字を上げると解像度がより細かくなります。
そして数値化したエンベロープを変数~crvに入れました。
~wt = ~crv.asWavetableNoWrap;
数値化したエンベロープをウェーブテーブル化しています。
~shp = Buffer.loadCollection(s, ~wt);
数値化したエンベロープをウェーブテーブル化したものを、最後にバッファに登録(loadColldection)してます。
最終的に~shpという変数に入れました。
******************************************
Shaperのエンベロープは、まず直線を基本にするのが出音をコントロールしやすいので、この例ではエンベロープを直線にしています。
入力値(X軸ピンク色の数値)が-1のときは出力(Y軸)が-1、入力値が1のときは出力が1、というふうに何も変換されないため、Shaperエンベロープが上記のようなが直線のときは、入力した信号がそのまま出力されます。
つまりサイン波を入力するとサイン波がそのまま鳴ることになります。
Pbindで鳴らしてみます。
[ex2]
(
//Synth
SynthDef(\shaperTone, {
arg freq=1, gate=1;
var sig, env, amp=0.5;
env = EnvGen.kr(Env.asr(0.01, 1, 0.1), gate, doneAction:2);
sig = SinOsc.ar(freq);
sig = Shaper.ar(~shp, sig);
sig = Pan2.ar(sig, 0) * env * amp;
Out.ar(0, sig);
}).add;
//Pbind
Pbind(
\instrument, \shaperTone,
\midinote, Pseq([50, 62, 50, 57, 38, 50, 62, 57], inf),
\dur, 0.5,
\sustain, 0.5,
).play(TempoClock(150/60));
)
上記のコードではサイン波をShaperに入れていますが、さきほどバッファ(~shp)に入れたEnvが直線であるため、音色変化は起きずサイン波がそのまま鳴ります。
*メモ*
Shaper.ar(~shp, sig);
の部分の~shpは、本来ならバッファナンバーを入れる箇所です。
実はこれは
Shaper.ar(~shp.bufnum, sig);
の省略形の書き方です。
~shp.bufnumは「ブートサーバして以降何番目のバッファか」を数えたもので、
(ブートサーバするとリセットされて、0番目から数えなおします。)
ちなみに今
~shp.bufnum;
を実行するとpost windowに何番目のバッファかが表示されます。
人によって違うと思いますが、、僕は38と出ました。
なのでShaper.ar(~shp.bufnum, sig);の部分は
Shaper.ar(38, sig);
と書いても同じこと。
でもEnvを書き直してBufferを実行するたび(~shpを新しくするたび)に、この38を書き換えるのは面倒なので
Shaper.ar(~shp.bufnum, sig);
と書いておけば楽々!というわけです。
さらに.bufnumを省略できるので
Shaper.ar(~shp, sig);
となってます。
さてでは、この直線をどんなふうに変化させると、出音はどんなふうに変化するのか、下記のコードを使ってグラフで見ていきます。
[ex3]
(
//Env
~crv = Env([-1, 0, 1], [1], [0]).asSignal(1024);
~wt = ~crv.asWavetableNoWrap;
~shp = Buffer.loadCollection(s, ~wt);
~crv.plot(minval:-1, maxval:1);
{
var sig;
sig = SinOsc.ar(100, pi);
sig = Shaper.ar(~shp, sig);
}.plot(minval:-1, maxval:1);
)
*ウィンドウが2つ重なって表示されるので、マウスでドラッグしてみてください。
plotウィンドウがEnvで作ったエンベロープで
function plotウィンドウが、そのエンベロープにサイン波を通した出力波形です。
(ex3ではサイン波がそのまま出力されていることがわかります。)
[ex3]のコード内の
~crv = Env([-1, 0, 1], [1], [0]).asSignal(1024);
の部分(Envクラスの各パラメータ)を書き換えることで、入力するサイン波がどんな波形に変換されるかをグラフで見ていきます。
*入力するサイン波(実験のためサイン波を使いますが、入力波形は何でもOKです。Oscで作ったオリジナル波形とかでもいいですしフィルター加工したあとの波形などでも。
エンベロープに少しカーブをつけてみます。
~crv = Env([-1, 0, 1], [1], [3, -3]).asSignal(1024);
このカーブだと、入力値が負のときは-1寄りに、入力値が正のときは1寄りになるので、出力されるサイン波が少し矩形波に似た感じになってきます。
もっとカーブをつけると、”ちょっと角のとれた矩形波”という感じにまで変化します。
~crv = Env([-1, 0, 1], [1], [10, -10]).asSignal(1024);
出音も、より矩形波に近い感じになります。
([ex2]のコードでPbindを鳴らしてみることができます。)
カーブを逆にすると。
~crv = Env([-1, 0, 1], [1], [-10, 10]).asSignal(1024);
音色も、また違った感じに。
ちなみに、理解を深めるためにもう一例。
エンベロープを逆の傾きにすると。
~crv = Env([1, 0, -1], [1], [0]).asSignal(1024);
これだと
入力が-1のとき出力が+1
入力が+1のとき出力が-1
となり、プラスとマイナスが逆転します。
つまり最終的な出力は逆相のサイン波に。
位相が入れ替わるだけで音色変化はありません。
入力信号の振幅
入力信号の振幅について注意点を。
例えば先ほどの矩形波っぽかったこのエンベロープの場合、
~crv = Env([-1, 0, 1], [1], [10, -10]).asSignal(1024);
入力信号のサイン波の振幅を小さくすると、Shaperに入れた後の変化があまりありません。ほとんどサイン波のままに見えるくらいです。(下記コードの例では0.1を掛けることで入力信号を小さくしました。つまり入力するサイン波の振幅が1/10になります。)
[ex4]
(
//Env
~crv = Env([-1, 0, 1], [1], [10, -10]).asSignal(1024);
~wt = ~crv.asWavetableNoWrap;
~shp = Buffer.loadCollection(s, ~wt);
~crv.plot(minval:-1, maxval:1);
{
var sig;
sig = SinOsc.ar(100, pi)*0.1;
sig = Shaper.ar(~shp, sig);
}.plot(minval:-1, maxval:1);
)
比較のため、入力サイン波の振幅を小さくしない場合のグラフ
入力信号の振幅を小さくした状態でPbindで鳴らしてみても・・・
[ex5]
(
//Synth
SynthDef(\shaperTone02, {
arg freq=1, gate=1;
var sig, env, amp=0.5;
env = EnvGen.kr(Env.asr(0.01, 1, 0.1), gate, doneAction:2);
sig = SinOsc.ar(freq)*0.1;
sig = Shaper.ar(~shp, sig);
sig = Pan2.ar(sig, 0) * env * amp;
Out.ar(0, sig);
}).add;
//Pbind
Pbind(
\instrument, \shaperTone02,
\midinote, Pseq([50, 62, 50, 57, 38, 50, 62, 57], inf),
\dur, 0.5,
\sustain, 0.5,
).play(TempoClock(150/60));
)
サイン波に近めの音が出ます。
Shaperは入力信号の音量が小さいと波形の変化に至らない、という特性があります。
なので、Shaperによる音色変化をしっかり行いたい場合は、入力信号の振幅をできるだけ大きくしておくとよいと思います。
ただ、この特性を利用して面白い効果も出せます。
例えば、入力信号のサイン波に音量変化を加えて、音量とともに倍音も変化するようにしたり。
[ex6]
(
//Synth
SynthDef(\shaperTone03, {
arg freq=1, gate=1;
var sig, env, amp=0.5, env2;
env = EnvGen.kr(Env.asr(0.01, 1, 0.1), gate, doneAction:2);
env2 = EnvGen.kr(Env.adsr(1.5, 0.5, 0, 0.1, curve:1), gate);
sig = SinOsc.ar(freq) * env2;
sig = Shaper.ar(~shp, sig);
sig = Pan2.ar(sig, 0) * env * amp;
Out.ar(0, sig);
}).add;
//Pbind
Pbind(
\instrument, \shaperTone03,
\midinote, 60,
\dur, 4,
\sustain, 3.5,
).play(TempoClock(150/60));
)
(この例だとちょ〜っと地味に聞こえるかもしれないですが、音量だけの変化とは明らかに違う聞こえ方になりますし、入力する波形やShaperのEnvの形によっては効果大です。)
それと、入力信号のサイン波の音量をノートごとにランダムにしたりできます。下記はその例です。
(Pbind側からランダムの数値Pwhiteを送っています。)
[ex7]
(
//Synth
SynthDef(\shaperTone04, {
arg freq=1, gate=1, randAmp;
var sig, env, amp=0.5;
env = EnvGen.kr(Env.asr(0.01, 1, 0.1), gate, doneAction:2);
sig = SinOsc.ar(freq) * randAmp;
sig = Shaper.ar(~shp, sig);
sig = Pan2.ar(sig, 0) * env * amp;
Out.ar(0, sig);
}).add;
//Pbind
Pbind(
\instrument, \shaperTone04,
\midinote, 55,
\dur, 0.5,
\sustain, 0.3,
\randAmp, Pwhite(0.1, 1).trace,
).play(TempoClock(150/60));
)
wavファイルをShaperに通す
サイン波や矩形波などの基本波形をShaperに通すのも面白いのですが、wavファイルを通すのも楽しいです。
この回で使ったrythm01.wavをShaperに通してみます。
まずrythm01.wavをデスクトップに置いて、下記コードを実行します。
[ex8]
~rythm = Buffer.read(s, "/Users/kuri/Desktop/rythm01.wav");
(これmacのパスですが、パスのユーザーネームの部分(kuriのところ)はご自身のユーザーネームに書き換えてください。わからない場合はrythm01.wavのファイルそのものをSuperColliderのエディタ上にドラッグ&ドロップするとパス全体が表示されます。Windowsも同様です。)
この[ex8]の実行で、~rythmというバッファが使えるようになりました。鳴らしてみます。
[ex9]
~rythm.play;
このまま(まずはShaperを通さずに)Pbindで鳴らしてみます。
[ex10]
(
SynthDef(\wavShaper, {
arg rate=1, gate=1;
var sig, env;
env = EnvGen.kr(Env.asr(0.001, 1.0, 0.001), gate, doneAction:2);
sig = PlayBuf.ar(2, ~rythm, BufRateScale.kr(~rythm)*rate);
Out.ar(0, sig);
}).add;
Pbind(
\instrument, \wavShaper,
\midinote, 60,
\dur, 4,
\sustain, 4
).play(TempoClock(120/60));
)
ではShaper用のEnvを~shp2に入れて・・・
[ex11]
(
~crv = Env([-1, 0, 1], [1], [8, -8]).asSignal(1024);
~wt = ~crv.asWavetableNoWrap;
~shp2 = Buffer.loadCollection(s, ~wt);
)
~crv.plot;
Pbindで鳴らします。
[ex12]
(
SynthDef(\wavShaper, {
arg rate=1, gate=1;
var sig, env;
env = EnvGen.kr(Env.asr(0.001, 1.0, 0.001), gate, doneAction:2);
sig = PlayBuf.ar(2, ~rythm, BufRateScale.kr(~rythm)*rate);
sig = Shaper.ar(~shp2, sig);
Out.ar(0, sig);
}).add;
Pbind(
\instrument, \wavShaper,
\midinote, 60,
\dur, 4,
\sustain, 4
).play(TempoClock(120/60));
)
振幅が増して少しサチュレーションがかかったような音になりました。
さらにエンベロープをいじってみます。
[ex13]
(
~crv = Env([-1, 0.25, -0.25, 1], [1, 0, 1], [0]).asSignal(1024);
~wt = ~crv.asWavetableNoWrap;
~shp2 = Buffer.loadCollection(s, ~wt);
~crv.plot;
)
こんなのはどうでしょう。
このまま[ex12]を実行すると再生されます。
(けっこう大きな音で鳴ります。)
気持ちいい感じの歪みが足されました。
**まめ知識**
Shaperとは関係ない話なのですが、上記[ex12]のような場合、SynthDefとPbindを(カッコ)でくくっているので、(カッコ)内にカーソルを置いてcommand + enterすると、SynthDefとPbindが同時に実行されて便利な反面、そのままPbindを鳴らしながら「SynthDefのパラメータをいじってSynthDefだけを再度実行する」ってことができないですよね。これ、できる方法がありまして・・・。
下図のようにSynthDefの部分だけを選択してshift + enterします。
これで、Pbindを鳴らしっぱなしのままSynthDefだけを何度でも実行可能です。
(Shaperの話に戻ります。)
ところで、エンベロープに「15.ひとつの波形からバリエーションを作る」の回で紹介したメソッドをそのまま使うことも可能です。
例えば以下のような感じです。
[ex14]
(
//Env
~crv = Env([-1, 0.25, -0.25, 1], [1, 0, 1], [0]).asSignal(1024).clip(-0.6, 0.6).cubed;
~wt = ~crv.asWavetableNoWrap;
~shp2 = Buffer.loadCollection(s, ~wt);
~crv.plot;
)
これを実行したあと[ex12]のPbindで鳴らすと(こんどは小さめの音で鳴ります)、また全然違うキャラクターに変化してます。
Shaperは直線のエンベロープを基準にすれば音色変化を想像しやすいですし、逆にそうではないアプローチ(エンベロープのポイントを多くしていくなど)をすれば全く予想できないような音になります。サイン波や三角派などの基本波形にも使えて、wavファイルやSoundInにも使える、もちろん自作ウェーブテーブルにも。いじっているとすぐに時間が過ぎてしまうとても楽しいクラスを紹介しました。
<目次>へ
https://note.com/sc3/n/nb08177c4c01