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させるという方法もあるようです。