SuperColliderで逆再生

PlayBufを使ってサンプルを逆再生する際に、一筋縄では行かなかったのでメモ。

PlayBufには再生レートを指定するrate引数があり、この値をマイナスにすることで逆再生ができます。 その際、再生開始位置(フレーム)を示すstartPos引数を指定しない場合0(すなわちバッファの先頭位置)が指定されるので、逆再生時にはバッファの最後の位置を指定する必要があります。

フレームのindexは0から始まるので、最終フレームはb.bufFrames - 1になります。 (bはBufferとする)

適当なwavファイルを用意し、Buffer.readの第2引数でそのパスとファイル名を指定して読み込みます。

s.boot;
b = Buffer.read(s, "sample.wav");
{PlayBuf.ar(b.numChannels, b.bufnum, -1, startPos: b.numFrames - 1)}.play;

うまく逆再生されますが、これだと再生後にfreeされないため、メモリ上に残ってしまいます。 そのため、DoneActionに2を指定します。

{PlayBuf.ar(b.numChannels, b.bufnum, -1, startPos: b.numFrames - 1, doneAction:2)}.play;

実行はできますが、音は何も聴こえません。

原因について、Eli Fieldsteel氏のTutorialを見ていたら解説がありました。フレームがb.numFrames - 1に到達すると自動的にDoneActionを実行する仕様になっているため、逆再生が始まった瞬間にfreeされてしまっているようです。

解決策として、b.numFrames - 2 から始めるとうまくいきます。

{PlayBuf.ar(b.numChannels, b.bufnum, -1, startPos: b.numFrames - 2, doneAction:2)}.play;

ちなみに、ループ再生にするとdoneActionが無視されるため、b.numFrames - 1から始めても問題ありません。

{PlayBuf.ar(b.numChannels, b.bufnum, -1, startPos: b.numFrames - 1, loop: 1, doneAction:2)}.play;

補足

sc-users MLに該当のQAがありました。

https://sc-users.bham.ac.narkive.com/KGzzUW3A/playbuf-bug

バグではないようです。再生レートなんてLFOとかで変化させたらプラスにもマイナスにもなるわけだし、「マイナスの時でもfreeしたい」場合もあるだろ?というようなことを言っているのかと思いますが、わかったようなわからないような...

DoneActionを0にした上で、ちょうど同じ長さのEnvを使ってfreeさせるという方法もあるようです。