読者です 読者をやめる 読者になる 読者になる

Folding-expressionを用いてparameter pack長のindex_sequenceを構築する

再帰深度がO(1)に抑えられる.(趣味)

melpon.org

#include <type_traits>

template <int... Ns>
struct V;

template <int N, int... Ns>
struct V<N, Ns...> {
  template <int M> V<N + 1, N, Ns...> operator +(V<M>);
};

template <>
struct V<> {
  template <int M> V<0> operator +(V<M>);
};

template <typename... Types>
struct S {
    template <int... Indices>
    static V<(sizeof...(Indices) - Indices - 1)...> reverse(V<Indices...>);
    
    template <typename> struct dummy {};
        
    using type = decltype(reverse((V<>{} + ... + V<sizeof(dummy<Types>)>{})));
};

struct T;

int main() {
    static_assert(std::is_same<V<0, 1, 2, 3, 4>, typename S<T, T, T, T, T>::type>::value);
}

Traktor + Max/Jitterで楽曲に合わせた映像のポン出しを自動化する

要約

[背景]

  • クラブでは楽曲に合わせてVJさんが映像を出してくれる

[目的]

  • 一人でおうちDJをする際にもアニソンにはOP動画やミュージッククリップといった映像を出したい

[提案]

  • Traktorのbroadcast経由で楽曲情報を持ってきてMax/Jitterで映像を出す

[結論]

  • 出来たけど,再生中の楽曲情報の精度が良くない

詳細

Traktorには直接に再生中の楽曲情報や再生時間を取れる開発者向けのAPIや,プラグインを書くためのSDKが今のところない.しかし,broadcasting機能があり,楽曲名やアーティスト名などかなり限定的な情報のみであればjson形式で取得出来るようなのでこれを用いる.

Traktorのbroadcastingはicecast streaming protocolで配信されるので,icecastのサーバを立てる. Macであればbrewを使えば簡単に入る.

$ brew install icecast

configファイル内のhackmeとなっている初期パスワードを変更する.

$ vim /usr/local/etc/icecast.xml

サーバの立ち上げ

$ icecast -c /usr/local/etc/icecast.xml

Traktorのbroadcasting設定を行う.下記は設定例.Passwordは先程設定したものを入力する.

f:id:fimbul:20160818053857p:plain

設定後,Broadcastingを有効にするには画面右上のアンテナマークを押す.アンテナマークが点滅する場合,icecastサーバが立ち上がっていないか設定に誤りがある.

f:id:fimbul:20160818054009p:plain

アンテナが点灯した状態で楽曲を再生すると,127.0.0.1:8000/status-json.xslにアクセスすることでjson形式で楽曲情報を取得出来る.

最後にMaxパッチの実装.ローカルサーバからjsonファイルを取得して,楽曲情報をパースし適切な映像を再生するパッチを作成する.

おおまかな解説をすると,Maxでは標準でmaxurlというcurlのラッパオブジェクトが使えるのでデータの取得は非常に簡単に出来る.jsonのパースはjsオブジェクトを使えばjavascriptを使えるのでJSON.parseで済む.楽曲タイトルと再生すべき動画のパスの対応表を用意しておき,一定間隔でjsonファイルのポーリングを行い,再生中の楽曲に変更があればjit.qt.movieに読み込むべき動画ファイルのパスを渡し映像を切り替える.

maxpatソースコード (ファイルのパスなどは適宜修正が必要) autovj.js · GitHub

f:id:fimbul:20160818054913p:plain

再生判定の調整

Mixのために次の曲を裏で再生していると,そちらが再生されているとみなされて意に反して動画が切り替わってしまうことがある. Traktorの再生判定は,Preferences -> Transport内のPlay Countの値を再生時間が上回ったら再生とみなす,という仕組みらしく10秒など短く設定しているとこのようなことが起きやすい.

次の記事などを参考にPlay Countを大きめに設定すると少しマシになる. Traktor 再生済みチェックの条件を設定 | Ambient Music for Life

とはいえやはり再生判定の問題は根本的には解決しないし,今度は映像の切り替わりタイミングが遅くなりがちに….

所詮おうちで遊ぶためなのでこれでも良いけれど,真面目に自動化するなら再生中の音データを直接取ってきて直接手元で楽曲判定をする方が良さそう.

coutで多次元配列のdump

何かの役に立つかも

実行結果 http://melpon.org/wandbox/permlink/MJN1EGWMw1ixnvBJ

#include <iostream>

template <typename T, std::size_t N>
std::ostream& operator<< (std::ostream& os, const T(&arr)[N]) {
    os << '{';
    std::size_t i = 0;
    for (const auto& v : arr) {
        os << v;
        if (++i != N) { os << ',' << ' '; }
    }
    os << '}';
    return os;
}

int main() {
    int arr[][2][6] = {{{0, 1, 2, 3, 4, 5}, {0, 1, 2, 3, 4, 5}}, {{0, 1, 2, 3, 4, 5}, {0, 1, 2, 3, 4, 5}}};
    std::cout << arr << std::endl;
}

出力

{{{0, 1, 2, 3, 4, 5}, {0, 1, 2, 3, 4, 5}}, {{0, 1, 2, 3, 4, 5}, {0, 1, 2, 3, 4, 5}}}

MacにTORCSを入れるのが難しい

公式で配布しているtorcs.appは微塵も動く気配がないので実体をfileしたらtorcs-bin: Mach-O executable ppcなどと申すので,化石を発掘した気分になった.

依存ライブラリの類はbrewに揃っていないので自前で落としてきてソースからビルドする必要がある.

片っ端から./configureしてmakeするとやはりメンテされてないのか軒並みコケるので1つずつ対応するも流石に嫌になって結局投げた.(知見: Linux使うべき)

template parameter packのN番目の型を取り出すアプローチ

これはメタプロする事自体を目的にした遊びで,実用という意味で考えればO(N)で線形再帰するべき. 線形再帰ならば誰でも読めるし,数十個ぐらいなら最も速い.もし真面目にテンプレート引数を1000個も渡すケースに陥っているならばそれは多分そこに至る時点で相当設計が悪いはず.

下2つの実装方針のうちN番目の型を取り出すだけなら前者の方が有意に早いです(手元のclangだとN = 10000で3倍くらい速い)

melpon.org

前者はidentity<Ts>*...をvoid*..., T*, ...で受ける方針,後者は多重継承しておいてオーバーロード解決に持ち込む方針. どちらも再帰深度が対数オーダーになるのでNが大きくなってもスケールはする.

そもそも言語仕様のレベルでランダムアクセス出来るようになってないことがイケてないというのはある.

#include <utility>

template <typename T>
struct identity {
    using type = T;
};

template <std::size_t>
using void_p = void*;

template <typename Indices>
struct at_impl;
template <std::size_t... Indices>
struct at_impl<std::index_sequence<Indices...>> {
  template <std::size_t>
  using void_ptr = void*;

  template <typename T>
  static T eval(void_ptr<Indices>..., T*, ...);
};


// split parameter pack 
template <std::size_t N, typename... Ts>
using at = typename decltype(
  at_impl<std::make_index_sequence<N>>::eval(static_cast<identity<Ts>*>(nullptr)...))::type;

template <std::size_t I, typename T>
struct indexed_identity {
  using type = T;
};

template <typename Indices, typename... Ts>
struct at2_impl;
template <std::size_t... Indices, typename... Ts>
struct at2_impl<std::index_sequence<Indices...>, Ts...> : indexed_identity<Indices, Ts>... {
  template <std::size_t N, typename T>
  static identity<T> eval(indexed_identity<N, T>);
};


// overload resolution
template <std::size_t N, typename... Ts>
using at2 = typename decltype(at2_impl<std::make_index_sequence<sizeof...(Ts)>, Ts...>::template eval<N>(
  std::declval<at2_impl<std::make_index_sequence<sizeof...(Ts)>, Ts...>>()))::type;

int main() {
    static_assert(std::is_same<int, at<9, int, int, int, int, int, int, int, int, int, int>>::value, "");
    static_assert(std::is_same<int, at2<9, int, int, int, int, int, int, int, int, int, int>>::value, "");
}

時間を浪費するのやめたい

限界AtoZタイピング

#include <iostream>

int main() {
    // Generate applescript
    std::cout << "on run {input, parameters}\n\tactivate application \"Google Chrome\"" << std::endl;
    for (char i = 'a'; i <= 'z'; ++i) {
        std::cout << "\ttell application \"System Events\" to keystroke " << '"' << i << '"' << std::endl;
    }
    std::cout << "end run" << std::endl;
}
on run {input, parameters}
    activate application "Google Chrome"
    tell application "System Events" to keystroke "a"
    tell application "System Events" to keystroke "b"
    tell application "System Events" to keystroke "c"
    tell application "System Events" to keystroke "d"
    tell application "System Events" to keystroke "e"
    tell application "System Events" to keystroke "f"
    tell application "System Events" to keystroke "g"
    tell application "System Events" to keystroke "h"
    tell application "System Events" to keystroke "i"
    tell application "System Events" to keystroke "j"
    tell application "System Events" to keystroke "k"
    tell application "System Events" to keystroke "l"
    tell application "System Events" to keystroke "m"
    tell application "System Events" to keystroke "n"
    tell application "System Events" to keystroke "o"
    tell application "System Events" to keystroke "p"
    tell application "System Events" to keystroke "q"
    tell application "System Events" to keystroke "r"
    tell application "System Events" to keystroke "s"
    tell application "System Events" to keystroke "t"
    tell application "System Events" to keystroke "u"
    tell application "System Events" to keystroke "v"
    tell application "System Events" to keystroke "w"
    tell application "System Events" to keystroke "x"
    tell application "System Events" to keystroke "y"
    tell application "System Events" to keystroke "z"
end run

dlopenやdlsym辺りの実装を読んでそのうちまとめたい

#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

int main(void) {
  FILE *fp;
  if ((fp = fopen("hello.c", "w")) == NULL) {
    fprintf(stderr, "file cannot open");
    exit(EXIT_FAILURE);
  }
  fprintf(fp,
    "#include <stdio.h>\n"
    "void hello(void) { printf(\"hello dl world!\\n\"); }\n"
  );
  fclose(fp);

  system("gcc -shared hello.c -o hello.so");
  
  void *dl = dlopen("./hello.so", RTLD_LAZY);
  if (dl == NULL) {
    fprintf(stderr, "dl cannot open");
    exit(EXIT_FAILURE);
  }

  void (*f)(void) = dlsym(dl, "hello");
  if (f == NULL) {
    fprintf(stderr, "dl cannot open");
    exit(EXIT_FAILURE);
  }

  f();  // hello dl world!
  dlclose(dl);

  return 0;
}