2017/05/06

SuperColliderのスケールとパターン

SuperColliderの音律とスケール、パターン、テンポについてです。

周波数と音程 cpsmidi, midicps

sclangでは音程にmidiノート番号が使われます。

周波数からmidiノート番号への変換は cpsmidi midiノート番号から周波数への変換は midicps で行います。midiノート番号はfloat値でも指定できます。

440.cpsmidi.postln; // 69
69.midicps.postln;  // 440

12平均律の音程間の比率 pow(2, note / 12) の値を note.midiratio として計算できます。逆に比率から note の値を求めるときは ratio.ratiomidi とします。

1.midiratio.postln;    // 1.0594630943591
pow(2, 1 / 12).postln; // 1.0594630943593

pow(2, 1 / 12).ratiomidi.postln; // 1
1.midiratio.ratiomidi.postln;    // 0.999999999996

midiratioは精度がやや落ちるようです。

音律とスケール

TuningScale で音律とスケールを管理できます。

var size = 9;
var semitones = Array.fill(size, { |index| 12 * sin(pi / 2 * index / size) });
var tuning = Tuning(semitones);
var scale = Scale(#[0, 1, 3, 5, 7], size, tuning);

tuning.semitones.postln;
scale.semitones.postln;

プリセットされた音律とスケールは以下で確認できます。

Tuning.directory;
Scale.directory;

スケールに音律を渡す場合は scale.pitchesPerOctave == tuning.sizetrue となる必要があります。 false の場合は渡した音律が無視されます。以下の例の size の値を変えることで確認できます。

var size = 12;
var semitones = Array.fill(size, { |index| 12 * sin(pi / 2 * index / size) });
var tuning = Tuning(semitones);
var scale = Scale.minor(tuning);

(scale.pitchesPerOctave == tuning.size).postln;

tuning.semitones.postln;
scale.semitones.postln;

パターン

Pbind でパターンを組むことができます。

Pbind(
  \scale, Scale.chromatic,
  \degree, Pseq([0, 12, 27, -2], 8),
  \dur, 0.2
).play;

scale はスケール、 degree はスケール内の度数、 \dur は音の長さです。

PseqPseq(演奏する配列, 繰り返し回数) となっており、呼び出されるたびに配列の値を前から順に取り出します。配列を入れ子にすることでコードを演奏できます。

Pbind(
  \scale, Scale.chromatic,
  \degree, Pseq([[0, 15, 17], 12, -19, -12], 8),
  \dur, 0.2
).play;

SynthDef で作ったシンセを \instrument で指定できます。シンセのパラメータも Pbind に組み込めます。

\scale には関数を指定することもできます。 \scale に指定された関数は performDegreeToKey というメソッドから呼び出されているようです。

SynthDef(\saw,
  { | out, freq, dur, pan, amp |
    var env = Env([1e-6, 1, 0.5, 1e-6], [0.0, dur, 0.4], \exp);
    var osc = Saw.ar(freq, EnvGen.ar(env, doneAction: 2));
    Out.ar(out, Pan2.ar(osc, pan, amp))
  }
).add;

~scaleSin = { | degree, stepsPerOctave, accidental |
  48 * sin(pi / 8 * degree / stepsPerOctave)
};

{ | shift(0) |
  var repeat = 4;
  var chord = [-12, 0, 12, 24];
  Pbind(
    \instrument, \saw,
    \scale, Scale.chromatic(Tuning.just),
    \degree, Pseq([-12, chord, 0, chord, 12, chord, 0] + shift, repeat),
    \dur, Pseq([1, 1, 1, 2, 1, 1, 1] * 0.1, repeat),
    \amp, 0.4,
  ).play;
}.value(-18);

Ppar で複数のパターンを並行して演奏できます。

SynthDef(\sawfilter,
  { | out, freq, dur, pan, amp, cutoff, res |
    var env = Env([1e-6, 1, 0.5, 1e-6], [0.0, dur, 0.4], \exp);
    var osc = Saw.ar(freq, EnvGen.ar(env, doneAction: 2));
    var filter = MoogVCF.ar(osc, cutoff, res);
    Out.ar(out, Pan2.ar(filter, pan, amp))
  }
).add;

{
  var theme = [0, 2, 5, 7, 9];
  var seq = { theme.choose };
  var seqsize = 16;
  var bind = { | shift(0) |
    Pbind(
      \instrument, \sawfilter,
      \scale, Scale.minor(Tuning.mean6),
      \degree, Pseq(Array.fill(seqsize, seq) + shift - 12),
      \dur, 0.25,
      \cutoff, Pseq(Array.fill(seqsize, { rrand(300, 4000.rand) })),
      \res, 0.86,
      \amp, 0.7
    )
  };
  Ppar(Array.fill(theme.size, { |i| bind.value(theme[i]) }), inf).play;
}.value;

テンポ

TempoClock でテンポを指定できます。 TempoClock.default.tempo の値は beats/second なので beats/minute (BPM) で指定するときは一分の秒数である60で割ってやります。

TempoClock.default.tempo = 142 / 60; // 142BPM。

SynthDef(\pulseDetune,
  { | out, freq, dur, pan, amp |
    var env = Env([1, 1e-6], [dur], -4);
    var envGen = EnvGen.ar(env, doneAction: 2);
    var osc1 = Pulse.ar(freq, 0.15);
    var osc2 = Pulse.ar(freq * 1.04, 0.4);
    Out.ar(out, Pan2.ar(osc1 + osc2, pan, amp * envGen))
  }
).add;

Pbind(
  \instrument, \pulseDetune,
  \scale, Scale.suznak,
  \degree, Pn(Pfuncn({24.rand - 14}), inf),
  \dur, 0.25 // 16分。
).play;