経緯
私は SC 歴 3 ヶ月程度の者です。
ライブコーディング用途ではなく、「DAW のように曲を作ってファイルに保存したい」という目的で SC を使用しています。
チュートリアルを終えて試しに 1 曲作ろうとした時、 Bus や Group を用いた実践的な NRT ( Non-Real Time )録音についての情報がなさすぎて途方に暮れました。
幸い、 Discord に参加してほとんどの部分を助けていただき、ひとまず望んでいた結果を得ることができました。
私はまだまだ初心者であり、大半の関数に対して「なにが分からないのか分からない」「なぜか動いているからオッケー」という状態です。熟練者の皆様からするとおかしな点ばかりかもしれません。ですが実践的な NRT 録音に関する日本語情報を少しでも増やすため、恥を忍んで投稿します。
コード
(
// SynthDef を配列に入れておくことで、 NRT 用の Score に複数のシンセを登録できる。
~synth = [
// この配列内に、曲の SynthDef を定義する。
SynthDef(\foo, {
| freq = 440, gate = 1, out = 0 |
var signal = Saw.ar(freq: freq, mul: Env.adsr(0.01, 0.01, 0.8, 0.3).kr(doneAction: Done.freeSelf, gate: gate));
signal = Pan2.ar(in: signal);
Out.ar(out, signal);
}),
SynthDef(\effects1, {
| in, out = 0, gate = 1, tempo = 1 |
var env = Env.asr(0.0001, releaseTime: 0.1).kr(gate: gate, doneAction: Done.freeSelf);
var signal;
signal = In.ar(bus: in, numChannels: 2);
// ここにエフェクトを書く。
// (このエフェクトは適用した箇所のみ適用される)
signal = LPF.ar(in: signal);
Out.ar(bus: out, channelsArray: signal * env);
}),
SynthDef(\masterEffects, {
| in, out = 0, gate = 1, tempo = 1 |
var env = Env.asr(0.0001, releaseTime: 0.1).kr(gate: gate, doneAction: Done.freeSelf);
var signal;
signal = In.ar(bus: in, numChannels: 2);
// ここにエフェクトを書く。
// (このエフェクトは曲のはじめから終わりまで適用される)
signal = FreeVerb2.ar(in: signal[0], in2: signal[1]);
Out.ar(bus: out, channelsArray: signal * env);
}),
];
~synth.do({|s| s.add;});
)
// ↑このブロックを実行してから、
// ↓このブロックを実行する。
(
// この structure 変数に曲の楽譜を書く。
// 特別なエフェクトをかけない場合、「out」は「\bus_master」に設定する。
// 特別なエフェクトをかける場合、「out」を当該エフェクト用の Bus に設定する。
var structure = Pseq([
Pbind(*[
instrument: \foo,
tempo: 120/60,
dur: Pseq([1,1,1,1]),
out: Pkey(\bus_master),
group: Pkey(\group_sources),
]),
Pbind(*[
instrument: \foo,
tempo: 180/60,
dur: Pseq([1,1,1,1]),
out: Pkey(\bus_effect1),
group: Pkey(\group_sources),
]),
Pbind(*[
instrument: \foo,
tempo: 150/60,
dur: Pseq([1,1,1,1]),
out: Pkey(\bus_master),
group: Pkey(\group_sources),
]),
Pbind(*[
instrument: \foo,
tempo: 60/60,
dur: Pseq([1,1,1,1]),
out: Pkey(\bus_effect1),
group: Pkey(\group_sources),
]),
]);
// Pproto を使うと Bus や Group をパターン内に閉じ込めることができる(らしい)。
var song = Pproto(
makeFunction: {
// ここで使用する Bus や Group を定義。
// 各 Pattern 内において Pkey で呼び出される。
// 定義順に注意。
~bus_effect1 = (type: \audioBus, channels: 2).yield;
~bus_master = (type: \audioBus, channels: 2).yield;
~group_master = (type: \group).yield;
~group_effects = (type: \group).yield;
~group_sources = (type: \group).yield;
},
pattern: Ppar([
// 「\bus_master」に流れている音に曲全体のエフェクトをかけ、 Bus 0 に流す。
// 「dur」は、「structure」で書いた曲の tempo と dur に依存する。
Pbind(*[
instrument: \masterEffects,
in: Pkey(\bus_master),
out: 0,
dur: Pseq([20], 1),
group: Pkey(\group_master),
]),
// 局所的なエフェクト用の Pattern 。
// エフェクト用のシンセ自体は曲全体を通して開いている。
// この場合、「\bus_effect1」に流れてきた音すべてにエフェクトをかけて「\bus_master」に流している。
// 「dur」は、「structure」で書いた曲の tempo と dur に依存する。
Pbind(*[
instrument: \effects1,
in: Pkey(\bus_effect1),
out: Pkey(\bus_master),
dur: Pseq([20], 1),
group: Pkey(\group_effects),
]),
structure
])
);
var recordNRT = {
| duration = 30, filePath = "", format = "FLAC" |
var score = song.asScore(duration: duration, timeOffset: 0.001);
// 配列に入れておいたシンセをここで全て Score に追加
~synth.do({|s| score.add([0.0, [\d_recv, s.asBytes]]); });
score.sort;
score.recordNRT(
outputFilePath: filePath.standardizePath,
headerFormat: format,
duration: duration,
options: ServerOptions.new
.numOutputBusChannels_(2)
.memSize_(10240)
);
};
// この行↓と最後の行をコメントアウトで切り替えれば、
// 「制作中の試聴」と「最終的なファイル出力」を切り替えることができる。
song.play;
// recordNRT.value(duration: 30, filePath: "ここにファイルパス.flac");
)
参考資料
公式ヘルプ内にある NRT 合成の資料
https://doc.sccode.org/Guides/Non-Realtime-Synthesis.html
SuperCollider の GitHub リポジトリに置かれている、録音についての資料
(ただし、「不幸にも、一番重要な非リアルタイム録音に関しては書きかけです」とあり、 NRT に関してはほぼ記載なし)