tuple_for

tupleの要素を順に処理したい場合に、どうするのが簡単なのか悩ましい。

メモ書き。ろくにテストしてないです。

#include <tuple>
#include <type_traits>
#include <utility>

template <int L, int R, int S, std::size_t... Indices>
constexpr auto tuple_for_impl() {
    if constexpr (L + sizeof...(Indices) >= R) {
        return [&](auto&& t, auto&& fn) constexpr {
            if constexpr (std::tuple_size_v<std::remove_reference_t<decltype(t)>> > 0
                       && std::is_same_v<decltype(fn(std::get<0>(t))), void>) {
                return [](auto...) constexpr {return std::tuple<>{}; }((fn(std::get<Indices>(t)), 0)...);
            } else {
                return std::make_tuple(fn(std::get<Indices>(t))...);
            }
        };
    } else {
        return tuple_for_impl<L, R, S, Indices..., L + S * sizeof...(Indices)>();
    }
}

template <int L, int R, int S = 1, std::size_t... Indices>
constexpr auto tuple_for = tuple_for_impl<L, R, S, Indices...>();

int main() {
    constexpr auto t = std::make_tuple(0, 'a', 0.1);
    constexpr auto r = tuple_for<0, 3>(t, [](const auto& arg) { return arg + 1; });
    static_assert(std::get<0>(r) == 1);
    static_assert(std::get<1>(r) == 'b');
    static_assert(std::get<2>(r) == 1.1);
}

https://wandbox.org/permlink/E9hh0AfDUxeej3cM

tuple_element再考

using type_impl<Indices, Types>::value...;がミソ。

[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ

#include <type_traits>
#include <utility>

extern void* enabler;

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

template <std::size_t N, typename T>
struct type_impl {
    template <std::size_t I, std::enable_if_t<I == N>*& = enabler>
    static T value();
};

template <typename... Types>
struct tuple_impl;
template <std::size_t... Indices, typename... Types>
struct tuple_impl<std::index_sequence<Indices...>, Types...> : type_impl<Indices, Types>... {
    using type_impl<Indices, Types>::value...;
    
    template <std::size_t I>
    using type = typename decltype(tuple_impl<std::index_sequence<Indices...>, Types...>::template value<I>())::type;
};

template <typename... Types>
struct tuple : tuple_impl<std::make_index_sequence<sizeof...(Types)>, identity<Types>...> {};

template <std::size_t N, typename T>
struct tuple_element {
    using type = typename T::template type<N>;
};
template <std::size_t N, typename T>
using tuple_element_t = typename tuple_element<N, T>::type;

int main() {
    using t = tuple<const int, char*, void>;
    static_assert(std::is_same_v<const int, tuple_element_t<0, t>>);
    static_assert(std::is_same_v<char*, tuple_element_t<1, t>>);
    static_assert(std::is_same_v<void, tuple_element_t<2, t>>);
}

書き初め

constexpr lambdaが使えるってことは?ということで思いついたネタを。 とりあえずclangさんでは動いてしまった。gccさんは置いてけぼりです。 wandbox.org

proof of conceptなので、必要なオーバーロード全然なかったり勝手にconstついたりする気がするので注意。

#include <iostream>
#include <utility>

static constexpr auto stack = [](auto... xs) constexpr { 
    return [=](auto access) { 
        return access(xs...);
    }; 
};

template <std::size_t N>
struct popper {};

template <typename Type>
struct helper {
    Type data;
    
    template <typename T>
    static constexpr auto make_helper(T&& arg) {
        return helper<std::decay_t<T>>(std::forward<T>(arg));
    }

    static constexpr auto push = [](auto xs, auto x) constexpr {
        return xs([=](auto... z) {
            return [=](auto access) {
                return access(x, z...);
            };
        });
    };

    static constexpr auto pop = [](auto xs) constexpr {
        return xs([=](auto, auto... z) {
            return [=](auto access) {
                return access(z...);
            };
        });
    };

    template <typename T>
    constexpr helper(T&& arg) : data(std::forward<T>(arg)) {};

    template <typename U>
    constexpr auto operator +(const U& rhs) const {
        return helper<Type>::make_helper(push(this->data, rhs.data));
    }
    
    template <typename U>
    constexpr auto operator -(const U&) const {
        return helper<Type>::make_helper(pop(this->data));
    }
};

static constexpr auto top = [](auto xs) constexpr {
    return xs.data([=](auto v, auto...) {
        return v;
    });
};

template <typename T>
    static constexpr auto make_helper(T&& arg) {
        return helper<std::decay_t<T>>(std::forward<T>(arg));
}

template <typename... Types>
struct tuple {
    decltype((make_helper(stack()) + ... + make_helper(std::declval<Types>()))) data;

    template <typename... Ts>
    constexpr tuple(Ts&&... args) : data((make_helper(stack()) + ... + make_helper(std::forward<Ts&&>(args)))) {}
};

template <typename... Types, std::size_t... Indices>
constexpr auto get_impl(const tuple<Types...>& t, std::index_sequence<Indices...>) {
    return (t.data - ... - make_helper(popper<Indices>{}));
}

template <std::size_t N, typename... Types>
constexpr auto get(const tuple<Types...>& t) {
    static_assert(sizeof...(Types) > 0);
    return top(get_impl(t, std::make_index_sequence<(sizeof...(Types) - 1) - N>{}));
}

int main() {
    constexpr tuple<int, char, double, bool> t(1, 'a', 3.14, true);
    static_assert(get<0>(t) == 1);
    static_assert(get<1>(t) == 'a');
    static_assert(get<2>(t) == 3.14);
    static_assert(get<3>(t) == true);
}

入れ子にすればN^2個に展開

#include <iostream>

template <typename... Ts>
int f(Ts... i) {
    int k = 0;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-value"
    (... , (i, (... , (++k, i))));
#pragma GCC diagnostic pop
    return k;
}

int main() {
    std::cout << f(0) << std::endl;
    std::cout << f(0, 0) << std::endl;
    std::cout << f(0, 0, 0) << std::endl;
    std::cout << f(0, 0, 0, 0) << std::endl;
    std::cout << f(0, 0, 0, 0, 0) << std::endl;
}

1
4
9
16
25

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}}}