百鬼夜行

素性を知られぬままエンジニアになることを目指します

TidalCyclesを使ったライブコーディングをYoutubeでライブ配信する方法

ライブコーディングの実践の場としてライブ配信して世界中の人に見てもらうのが良いのでは?と思い、いろいろ調べてやってみた。ライブ配信のやり方はそこら中に記事があるのでそっちを参照してください。この記事ではあくまで引っかかるポイントだけ載せていきます。

ざっくり流れ

Mac(OS 10.13以上)とTidalCyclesの利用を想定。

  • Youtubeアカウント作成・チャンネル開設
  • OBSのインストールと録音設定
  • 放送開始

Youtubeアカウント作成・チャンネル開設

まずはYoutubeにアカウント作成と自分のチャンネルを開設が必要。方法に関してはガイドに沿って行えばわかるので割愛。

チャンネルを開設したら動画を投稿できるようになりますが、一方ライブ配信はチャンネルを作成すればすぐにできるというものではありません。ライブ配信をするための申請を行わなければならず、申請から24時間後に配信が可能になる。

OBSのインストールと設定

OBSを使ってライブ配信をします。こちらも各種基本設定はいろいろ記事が出ているので、そちらを参照してください。ただし、macOSの場合、デスクトップ音声の出力がデフォルトではできません。仮想音声デバイスのインストールと音声のミキシング用ソフトウェアが必要です。

この音声設定を行うためには2つのステップがあります。

  • (1)必要なツールをインストールする
  • (2)録音するための設定をする

まず(1)ですが、「Soundflower」という仮想音声デバイスと、ミキシングをする「LadioCast」というツールが必要になります。インストールと各種設定に関しては下記のリンクを参照。

参考: https://ch.nicovideo.jp/kenta0644/blomaga/ar537095

Soundflowerのインストール時、エラーが出て先に進めないことがあります。バージョンを変えたりセキュリティとプライバシーから許可したりしてもエラーが出続けてしまう場合は、セーフブートで起動してからインストールしなければなりません。

参考: https://hawksnowlog.blogspot.com/2018/05/install-soundflower-into-high-sierra.html

次に(2)ですが、SuperColliderからSuperflowerに向けて音声を出力させる設定をします。SuperColliderを再起動し、エディタにServerOptions.new.outDevice = “Soundflower (2ch)”;と入力して、実行します。

参考: https://qiita.com/tackey/items/f5034d5d73438bb2845d

LadioCast上で音声出力が行われていることを確認したら、最後にOBS側で「デスクトップ音声デバイス」を追加すれば、ライブ配信の準備は完了です。

配信開始

本配信をする前に、限定公開で動画と音声の確認をしましょう。配信はOBSから「配信開始」ボタンを押せば始まります。

気をつけたいのが、マシンスペックの問題で音がもたついてしまってノイズ音が発生することです。配信中はマシンへの負荷が大きくなるため、エディタとブラウザをそれぞれ行き来している際にブツッとしたノイズ音が生じる可能性があります。あまり耳障り的によくないので、エディタ以外を触らないようにするか、デュアルディスプレイを使ってブラウザとエディタの両方を開くようにしましょう。

ちなみに、こちらが配信時の映像になります。まだまだ練習中。


#1 "Trial" | Live Coding with TidalCycles.

以上がライブコーディングをライブ配信するコツです。ライブコーダーのみなさんも試してみてください。

TidalCyclesの音色と実行パターンに関するメモ

音のパターンを数字や処理で見るのは直感的でないため、ある程度思い通りのリズムにするには実践しながらクセのようなものを体験し、習得しなければいけない。なので、いろいろ試していった所感をメモっていきたいと思う。

今回検証するのはnに関して、音色の操作/*を使う場合に、どのようなサイクルで数値が適用されるのかを公式のサンプルを使って確認した。

n - TidalCycles userbase

d1 $ s "drum [cp hh] odx*2 arpy*2" # n "[1 3 5 0 2]/2"

コードからまずパッとわかるのは、7つの要素で構成され、「drum」「cp」「hh」「odx」「arpy」5つの音色がある。♩ ♫ ♫ ♫というリズムでそれらの音が鳴る。

これにnを使って数値パターンをコントロールパターンに入れ込み、音色を変化させている。別の言い方をすると、コントロールパターンを見ると特に番号の指定がないので、すべての要素の音色が0 0 0 0 0 0 0になっており、この0の並びに対してnを使って数値を代入していく。

数値パターンは/2されることによって、コントロールパターンが2回再生されるまでに入れ込みが終わりきるようになっている。ここが理解しづらい。そもそも7つの要素に対し、5つの数値をどのように入れ込むのか、その数値がさらに/2されるとどう入れ込まれるのか。

検証するために、各音の要素をnumbersに置き換え、数値を読み上げてもらった。結果は以下の通りだった。

# n "[1 3 5 0 2]" の場合

1 3 3 5 0 0 2

# n "[1 3 5 0 2]/2" の場合

1 1 1 3 3 3 5 5 0 0 0 2 2 2

# n "[1 3 5 0 2]*2" の場合

1 5 0 1 3 5 0

先述のように、/2が付いていることでひとつの数値パターンが最後まで入れ込まれるまでにパターンを2回再生することを必要としている。

また数値の重複にも着目したい。/2が付いていない方は、5つの数値を7つの要素に当てはめるため、3と0がひとつ増えている。次に/2の方は、5を中心として左右が3つずつになった。何回再生してもこの通りになったため、ユークリッドシーケンスのように何かしらのルールで不足分を補っているようだった。

上記のパターンをアナログに書き起こすと以下のようになる

d1 $ slow 2 $ s "drum:1 [cp:1 hh:1] [odx:3 odx:3] [arpy:3 arpy:5] drum:5 [cp:0 hh:0] [odx:0 odx:2] [arpy:2 arpy:2]"

*2についても調べてみた。1パターンの間に2回数値が入れ込まれるわけはないので、あまってしまう数値を自動で取り除くようだった。

補填と除去のどちらにせよ、アルゴリズムに則ると利用者がコントロールできるパターンではなくなってくることが今回わかった。これは悪い意味ではなく、ライブコーディングにはこのようなランダム性を表現に取り入れてこその楽しみがあると再認識できた。

Rails の chartkick.js のエラーを解決する

f:id:yagyosan:20181206204137p:plain

開発環境構築中に下記のエラーが表示された。

Asset filtered out and will not be served: add Rails.application.config.assets.precompile += %w( chartkick.js ) to config/initializers/assets.rb and restart your server

config/initializers/assets.rbRails.application.config.assets.precompile += %w( chartkick.js ) を書き加えろとのことなので、そのようにした。

そのあと再起動して、解決しました。

ndenvとnode -vのバージョンが合わない時の解決策

ごちゃごちゃしていたのを整理したのでメモっておく。

$ node -v
v11.2.0
$ ndenv versions
  system
* 8.9.4 (set by /Users/yagyosan/project/.node-version)

最初はこの状態。

nodeを参照している場所を変える

ndenvのPATHなどを .bash_profile をすでに書いてある前提で...。

$ which node
/usr/local/bin/node
$ brew uninstall --ignore-dependencies node
$ exec $SHELL -l
$ which node
/Users/yagyosan/.ndenv/shims/node
% node -v
v8.9.4

ついでにnpmも変える

$ which npm
/usr/local/bin/npm
$ npm uninstall -g npm
$ rm -rf /usr/local/lib/node_modules
$ brew prune
$ which npm
/Users/yagyosan/.ndenv/shims/npm

これですべてndenvの支配下になった。

参考

Nodeのバージョン管理をndenvにしたけどうまくバージョンが切り替わらなかったときのメモ - dackdive's blog

「The Book」で Rust 入門 3 後半

前回のあらすじ

yagyosan.hatenablog.com

関数

関数について見ていく

  • エントリーポイント: プログラム実行時に最初に走る関数
  • fn で新しい関数を作ることができる

関数の引数

  • 引数: 関数のシグニチャ?の一部となる特別な変数
  • 正確には仮引数("parameter")と実引数("argument")がある
  • Rusyでは仮引数の型を宣言しなければならない

関数本体は、文と式を含む

  • 関数本体は、文が並び、最後に式か文を置くという形で形成される
  • Rustは式指向性
  • 文: なんらかの動作をして値を返さない命令
  • 式: 結果値に評価されるもの(数値, 数学演算, 関数呼び出しなど)
  • 文はCとRubyのように代入値を返さない
  • 式はブロックの中で文末に;がつかないもの
fn main() {
    let x = 5;

    let y = {
        let x = 3;
        x + 1
    };

    println!("The value of y is: {}", y);
}

戻り値のある関数

  • 関数を呼び出したコードに値を返せる
  • 関数の戻り値は、関数本体ブロックの最後の式の値と同義
  • returnキーワードで「早期リターン」することもできる
  • 関数名() のあとに -> と型を書いて宣言する
  • エラー文に表示される()はタプルを表す
fn five() -> i32 {
    5
}

fn main() {
    let x = five();

    println!("The value of x is: {}", x);
}

コメント

フロー制御

ifとループ制御について

if式

  • if式の条件式と紐付けられる一連のコードは「アーム」と呼ばれることがある(match式のように)
  • 論理値以外を入れるとコンパイルエラーになる
  • else if と組み合わせることもできるが、コードがめちゃくちゃになることもあるのでmatch式が有用
  • ifは式なのでlet文の右辺に持ってくることも可能
  • 変数は単独の型でなければならないため、各アームの結果になる値は同じ型にする

ループでの繰り返し

loopwhilefor がある。

loopでコードを繰り返す

  • 明示的にやめさせるまで永遠に繰り返す
  • break でやめさせることができる

whileで条件付きループ

  • whileキーワードの後に条件を書く
  • loopifelsebreakを組み合わせても同じようなことができる

forでコレクションを覗き見る

  • 添字を使う場合、長さを間違える可能性もある
  • コレクションの各アイテムに対してコードを実行できる
  • インクリメント/ デクリメントしてwhileを使うよりもforRange型を組み合わせル方が良い
fn main() {
    let a = [10, 20, 30, 40, 50];

    for element in a.iter() {
        // 値は{}です
        println!("the value is: {}", element);
    }
}

Range型の例

fn main() {
    for number in (1..4).rev() {
        println!("{}!", number);
    }
    println!("LIFTOFF!!!");
}

とりあえず3章はこれで終わりで、最後のセクションにあったプログラムを別記事でやってみる。

「The Book」で Rust 入門 3 前半

前回のあらすじ

yagyosan.hatenablog.com

一般的なプログラミングの概念

一般的なプログラミング言語の概念の解説と、それがRustの文脈だとどうなるか。

変数と可変性

Rustでは変数は標準で不変だが、可変にする選択肢もある。なぜそうなっているのか。 cargo new --bin variables でプロジェクトを作って確認してみる。

fn main() {
    let x = 5;
    println!("The value of x is: {}", x);
    x = 6;
    println!("The value of x is: {}", x);
}

ビルドして実行する。

error[E0384]: cannot assign twice to immutable variable `x`
 --> src/main.rs:4:5
  |
2 |     let x = 5;
  |         - first assignment to `x`
3 |     println!("The value of x is: {}", x);
4 |     x = 6;
  |     ^^^^^ cannot assign twice to immutable variable

error: aborting due to previous error
  • 不変な変数に2回も代入できないのと、その該当箇所が表示されている
  • 最初に設定した変数が、のちに変更されてしまっているという見つけづらいバグを防ぐことができる
  • mut をつけることで可変にできる(mutable?)
  • トレードオフとして、バグの予防、パフォーマンスと簡潔性(メモリ)
cargo run
   Compiling variables v0.1.0 (file:///Users/yagyosan/develop/rust_study/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 1.40s
     Running `target/debug/variables`
The value of x is: 5
The value of x is: 6

変数と定数(constants)の違い

定数の特徴は以下

  • mutが使えない(常に不変)
  • const キーワードで宣言し、必ず注釈しないといけない -> 次セクションで説明
  • グローバルスコープ含めてどんなスコープでも定義できる
  • 関数呼び出し結果や実行時に評価される値?にはセットできない
  • (すべて大文字かつアンダースコアで単語区切り)

シャドーイング

前に定義した変数と同じ名前の変数を新しく宣言でき、前の変数を覆い隠す(shadow)こと。「シャドーされた」などという。 mutと上書きの違いは以下の通り。

  • letを使わずに再代入をしようとすればコンパイルエラーになる
  • 実行的には新しい変数を生成することになるため、値の型を変えつつ異なる名前を考える必要がなくなる(ex. 文字列型から数値型への変換)
    • 逆にmutを使おうとすると、型が変更されない(=再代入になる)ためコンパイルエラーになる

データ型

Rustの値はすべてデータ型になる。スカラー型と複合型の2種類を見てみる。

  • 静的型付き言語: コンパイル時にすべての変数の型が判明している必要がある
  • コンパイラ: 値と使用方法に基づいて使用したい型を推論し、複数の方が推論される場合は型注釈をつける必要がある

スカラー

単独の値を示す。主に整数、浮動小数点数、論理値、文字の4つ。

  • 整数型
    • i32型がデフォルトのバリアント
    • iが符号付き(=正負を持つ)整数、uが符号なし、数字がビット数
    • 符号付き数値は「2の補数表現」で保持される 符号付きバリアント:-(2<sup>n-1</sup>)以上2<sup>n-1</sup>-1)以下の数値
      • nはビット数
    • 符号なしバリアント: 0以上2<sup>n</sup>-1以下の数値
    • isizeusizeはプログラムが動作しているコンピュータの種類(64ビットアーキテクチャなど)による
    • 整数リテラル(見たままの値ということ)はバイトリテラル以外は_表記ができる
  • 浮動小数点型
  • 論理値型
    • truefalse
  • 文字型

複合型

  • タプル型
    • 複数の型の何らかの値を一つの複合型にまとめ上げるもの
    • (,)で記述する
    • 変数はタプル全体に束縛されているため、値を取り出すインはパターンマッチングが必要
    • letとパターンを使って個別の変数にすることを「分配」と呼ぶ
    • タプルに.と直後にアクセスしたい値の番号をつければ直接タプルの要素にアクセスできる
  • 配列型
    • タプルとは違い、すべての要素が同じ型でなければならない
    • 他の言語と違い、固定長になっている
    • [,]で記述する
    • ベクタ型はサイズの伸縮が可能なので、どちらを使うべきか迷ったらベクタ型を使う
    • 要素にアクセスするには添字を使う[0]
    • 配列の長さを超える添字を使用した場合はコンパイルエラーになる

「The Book」で Rust 入門 2 後半

前回のあらすじ

yagyosan.hatenablog.com

予想と秘密の数字を比較する

ユーザが入力した値と生成した乱数を比較する。

extern crate rand;

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    
    // snip

    println!("your gessed: {}", guess);

    match guess.cmp(&secret_number) {
        Ordering::Less => println!("Too small!"),
        Ordering::Greater => println!("Too big!"),
        Ordering::Equal => println!("You win!"),
    }
}
  • std::cmp::Ordering: Ordering型もenumで、列挙子はLess, Greater, Equal
  • match guess.cmp(&secret_number): cmpメソッドは2値を比較し、match式で返ってきたOrderingの列挙子に合わせて次の動作を決定
    • match: 複数のアームを持ち、各アームのパターンを順番に照合していき、合わないものは無視される

だが上記の式のままではエラーが出る。

cargo run
   Compiling guessing_game v0.1.0 (file:///Users/yagyosan/develop/rust_study/guessing_game)
error[E0308]: mismatched types
  --> src/main.rs:23:21
   |
23 |     match guess.cmp(&secret_number) {
   |                     ^^^^^^^^^^^^^^ expected struct `std::string::String`, found integral variable
   |
   = note: expected type `&std::string::String`
              found type `&{integer}`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: Could not compile `guessing_game`.

To learn more, run the command again with --verbose.

型の不一致ということなので、下記の行を追加する。

let guess: u32 = guess.trim().parse()
    .expect("Please type a number!");
  • 型推論にも対応しているが、数値型の場合i32がデフォ
  • 文字列と数値型を比較できなかった
  • 新しい値で変数の値を覆い隠す(Shadow)ことが許されるので、型を変えるときはよく使われる
  • trimメソッドが必要なのは、read_lineの入力値が文字列で5\nで改行を含むため
  • u32型は小さな非負整数にぴったり
  • parseメソッドの呼び出しはResult型を返すのでexpectメソッドで扱う

ループで複数回の予想を可能にする

loopキーワードは無限ループを作り出す

extern crate rand;

use std::io;
use std::cmp::Ordering;
use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1, 101);

    println!("The secret number is: {}", secret_number);

    loop {
        println!("Please input your guess.");

        let mut guess = String::new();

        io::stdin().read_line(&mut guess)
            .expect("Failed to read line");

        let guess: u32 = guess.trim().parse()
            .expect("Please type a number!");

        println!("your gessed: {}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => println!("You win!"),
        }
    }
}

これだと無限ループなので変更していく。また文字入力するとクラッシュするが、その内容は下記。

thread 'main' panicked at 'Please type a number!: ParseIntError { kind: InvalidDigit }', libcore/result.rs:945:5
# mainスレッドでは数値を入力してください
note: Run with `RUST_BACKTRACE=1` for a backtrace.
# 注釈: バックトレースを見るには `RUST_BACKTRACE=1` で走らせて

正しい予想をした後に終了する

breakを追加して、勝った時に終了するようにする

// --snip--
        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
        break;
    }
}

main関数の最後にbreakがあるので、これはプログラムを終了することを意味する。

不正な入力を処理する

非数値を入力した時に、クラッシュさせるのではなく数当てを続けられるようにする。

// --snip--
io::stdin().read_line(&mut guess)
    .expect("Failed to read line");

let guess: u32 = match guess.trim().parse() {
    Ok(num) => num,
    Err(_) => continue,
};

println!("You guessed: {}", guess);
// --snip--
  • match式に切り替える
  • _は包括値
  • continueloopの次の段階に移り、再度予想入力を求めるようプログラムに指示する
    • 実質的には、parseメソッドが 遭遇しうる全てのエラーを無視する

最後に秘密の数字を表示している行を削除して完了。