令和 3 年度川口市保育所探しのための自宅と駅からの距離算出 Google Apps Script を作成した
ポエム
普段ポエムと趣味を垂れ流しながらコードを書くブログが唐突に現実の話をするのだが、去年第一子が幸いなことに母子ともに無事産まれ妻の職場復帰を目指して保育所探しをすることになった。保育所を探すにあたり妻から保育所のリストを作成せよとの要望があったので、市のホームページから保育所一覧を出し我々の通いやすい保育所を探そうとした。我が家の保育所選びの条件として、そもそもゼロ歳児保育をしているかなどの諸々の条件はあったのが、結局の所近場に通うのが最も良いだろうということで、預けに行くときには家からの距離が迎えに行くときには駅からの距離が肝要という話になり、自宅からと駅からの距離が一定距離より近いことを挙げた。とはいえ 174 箇所もある保育所すべての距離を手で Google 先生に聞くのはそれなりに骨であったので、遊びを兼ねて Google Apps Script を作成し簡単に計算できるようにした。
作ったもの
- 令和 3 年度保育所探し
- 上記スプレッドシートに紐づくような下記の Google Apps Script
// コピペ元:https://it-soudan.com/how-to-get-distance-with-google-map-api/ function calcDistance(src, dest) { var directionFinder = Maps.newDirectionFinder() .setOrigin(src) .setDestination(dest) .setMode(Maps.DirectionFinder.Mode.DRIVING) .setLanguage("ja"); var directions = directionFinder.getDirections(); var route0 = directions.routes[0]; directionFinder.setAvoid(Maps.DirectionFinder.Avoid.HIGHWAYS); directions = directionFinder.getDirections(); var route1 = directions.routes[0]; return [route1.legs[0].distance.value / 1000, route0.legs[0].distance.value / 1000]; } function setUpDistance() { var ss = SpreadsheetApp.getActiveSpreadsheet(); var list = ss.getSheetByName("保育所リスト"); var addresses = ss.getSheetByName("自宅と駅の場所"); var home = addresses.getRange(1, 2).getValue(); var station = addresses.getRange(2, 2).getValue();; var dat = list.getDataRange().getValues(); // 各行を順に処理する for (var i = 1; i < dat.length + 1; i++){ const src = list.getRange(i, 3).getValue().replace("(地図)", ""); if(list.getRange(i, 4).getValue() !== true && !list.getRange(i, 7).getValue()) { list.getRange(i, 6).setValue(calcDistance(src, home)); list.getRange(i, 7).setValue(calcDistance(src, station)); } } }
使い方
「自宅と駅の場所」シートに自宅・駅の住所を書き、上記スクリプトの setUpDistance
関数を実行すると諸々の距離が計算される。ただ、全部の保育所を二回計算すると Google の距離算出 API の呼び出し回数制限に引っかかってしまうので、試す場合は予め保育所を絞った上で実行したほうが良いかもしれない。また、川口市の保育所としているがあくまでリストが川口市のものなだけで、他の地域のリストに変更すれば同様に距離を算出することはできる。中身については参考にした下記の記事を参照されたい。
普段あんまりこういう形でのコードは書かないのでそれなりに新鮮ではあった。ただ、この手のコードを解説するのはあんまりおもしろくなくやる気が全く出ないのがわかったのでもう少し込み入ったコードを書きたい。
Google Home ことはじめ
ポエム
半年前くらいに第一子が産まれたのでプライベートのタスクが爆増していたのだが、ようやく落ち着いてきたので趣味プロ兼実用のために Google Home で使えるアプリを作りたくいろいろ調べていた。というのも、子育てのタスクは絶妙に片手の半分が空く程度にしか手が使えず長く薄く注意を払う必要がある。こういうときにレシピの検索などをしたいが、入出力のデバイスとしてスマートフォンでは集中力が必要すぎるし、そうなると俄然スマートスピーカーによる音声入出力は魅力的に見えてきた。そこで冒頭のように Google Home を制御するにはどうしたらいいのかを調べてみた。 後述するがあまりちゃんとは調べていない。というのも Google Assistant を拡張する方法で Google Home をいじる方法を調べたが最初から実装するにはやや量が多く、手っ取り早く触ってみるに向いていないためぼくのような初心者が手を出すものではないと判断した。
やりたいこと
ぼくのやりたいことはシンプルで「冷蔵庫の中身を保持しておき残りものからよしなな料理とレシピを紹介してくれること」「入出力デバイスは Google Home にする」である。後者をよくわかっていないのでとりあえず何を使えばいいのか知りたい。
結論
Actions on Google だと現段階ではできること&実装することが多すぎるので次は Dialogflow + Cloud Functions の構成を調査する。 Dialogflow + Cloud Functions の構築については下記のようなブログに詳しい。現時点で Dialogflow がどの部分を対応しているのかは把握していない。
調査内容
Actions on Google Smart Home
基本的には下記の公式ページにあたるのが良い。
やること
Actions on Google を使って Google Home などのデバイスからの操作を受け付けるには Fulfillmentと呼ばれるやりとり を通じて下記のように応答を行う必要がある。
この Developer Cloud なのだが Fulfillment だけならともかく、もろもろの実装要件がありなかなかこれがヘビーである。
- OAuth 2.0 の authorization code grant flow の実装
- OAuth 2.0 の authorization header による API の認可
- 認可したユーザーの CRUD 操作
- Smart device の Intent 処理(Fulfillment の実装)
だいたい 1. のあたりからちょっと面倒すぎる。その代わりすべての細かい処理を書ける(=書かなければならない)ので高度なことをやるのであれば対応すればよい。ただ、この辺でぼくの手には負えなさそう& Dialogflow + Cloud Fnctions でお手軽に実装できるという記事を観測していたので一旦調査を打ち切った。
終わりに
音声合成を C, C++ 言語でやっていたりするとよくあるのだが、波形ファイルを読み込もうとするとバイナリの形式の説明が出てくるマッチョイムズとは反対で、 web 系は how は非常にそろっている一方、すぐに検索できる記事では why が欠けてしまって疑問に思ったので調べた記事を残した。中身を詳しく見たわけではないので単品の記事としては微妙な感じだけれども折角調べましたし。
今さら C++ で WORLD のラッパーライブラリを書いてみた
ポエム
現実世界の不具合で出勤時間が消失した分体力が余っているのか、余暇に回す気力がだいぶ戻ってきたので 1000000000 年ぶりくらいに趣味プロをしている。とはいえ別段すごく作りたいものがあるわけではないので、惰性で昔作りたかった WORLD をいい感じに抽象化したライブラリを作ってみた。これ使って何しようかは浮かんではいないのだが、音声の波形を切り貼りするコードはぼくが遊ぶ分にはだいぶ書きやすくなるので一区切りして記事にしておく。
やったこと
WORLD を利用した C++ のラッパーを作った。その際昔 v.Connect 作ってて面倒くさかった周期性・非周期性成分の扱いを抽象化した。と言ってもなんのこっちゃなので下記のコードを見てほしい。
void SpectralEnvelopeEstimation(double *x, int x_length, WorldParameters *world_parameters) { CheapTrickOption option; InitializeCheapTrickOption(world_parameters->fs, &option); // put default parameters into CheapTrickOption. option.q1 = -0.15; option.f0_floor = 71.0; // Parameters setting and memory allocation. world_parameters->fft_size = GetFFTSizeForCheapTrick(world_parameters->fs, &option); world_parameters->spectrogram = new double *[world_parameters->f0_length]; for (int i = 0; i < world_parameters->f0_length; ++i) { world_parameters->spectrogram[i] = new double[world_parameters->fft_size / 2 + 1]; } DWORD elapsed_time = timeGetTime(); CheapTrick(x, x_length, world_parameters->fs, world_parameters->time_axis, world_parameters->f0, world_parameters->f0_length, &option, world_parameters->spectrogram); std::cout << "CheapTrick: " << timeGetTime() - elapsed_time << " [msec]" << std::endl; }
このコードの大本はWORLD のサンプルコードである。元のコードは下記を参照のこと。
音声は、趣味で DX ライブラリ使ってゲームもどき作ったり仕事でウェブアプリを作るのと違いだいぶ扱いが面倒くさく(修羅場P調べ)、 v.Connect で歌声合成するときには時系列の波形データそのものでは扱いづらいため波形データから音の高さ・声の母音の成分・声のそれ以外の成分(雑に書くと正しくないのでこのあとはそれぞれ、F0、周期性成分、非周期性成分と言う)の3つの成分に WORLD を使ってばらしてから再合成していた。このばらした情報は先に挙げたコードだと例えば周期性成分は double **WorldParameters::spectrogram
の中に二次元配列として格納される。この二次元配列はいわゆる音のスペクトルの画像なので、サンプルのコードのように小さいプログラムなら別にこの持ち方でも十分便利ではあり、 v.Connect も結局実装的には本当に楽なので内部的に波形を二次元配列に直しては切り貼りをする処理が書かれている。
しかしこのデータの持ち方、小規模のうちは特に気にならないのだが UTAU 音源を読み込んだりして波形の数が二桁以上になってくると二次元配列だけにバカスカメモリを食い始めて扱いが面倒になってくる。さらによく考えてみると二次元配列で持つかどうかは別段実装都合であって、翻って v.Connect みたいに UTAU 音源使って声を切り貼りするみたいな用途に注目すれば、単純にある時刻の F0、周期性成分、非周期性成分がわかりさえすればいいことに気づく。となると下記のインタフェースだけ実装されていれば音声の切り貼りをする歌声合成器を作るのには十分なのだ。
class Spectrogram { public: /** * Spectrogram#pickUpSpectrumAt picks up an instant spectrum on the specified moment. */ virtual bool pickUpSpectrumAt(Spectrum *destination, double ms) const = 0; /** * Spectrogram#f0At returns f0 value of spectrogram at ms[milli seconds]. */ virtual double f0At(double ms) const = 0; };
裏側に二次元配列を持つかどうかは実装詳細の都合なので何でも良い。今回はバカスカメモリを食うのは嫌だったので、ある時刻の周期性成分と非周期性成分はスペクトルを引っ張ってくる pickUpSpectrumAt
メソッド内で計算する実装に挑戦した。数年前のぼくがどうしても実力不足で作れなかったのだが数年ぶりに書いてみたらなんとかなったので大変うれしい。
できたものと使い方
実装者の環境は下記である。この書き方であってんのか? C++ の事情はよくわからんのでぼくの環境でしか動かないかもしれないが調べる余力はない。
harun@MyComputer MINGW64 /g/Projects/uzume/uzume_dsp $ cmake --version cmake version 3.14.4 CMake suite maintained and supported by Kitware (kitware.com/cmake). harun@MyComputer MINGW64 /g/Projects/uzume/uzume_dsp $ g++ --version g++.exe (Rev2, Built by MSYS2 project) 8.3.0 Copyright (C) 2018 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
とりあえず波形の分析と合成の部分を自作のインタフェースで行えるところまで実装した。オリジナルの WORLD と音質は差がない(個人の感想です)になったので一旦満足。裏側の実装はどうでもいいと言ったが、波形を与えれば二次元配列を裏で確保することなく、その波形の特定の時刻の F0、周期性成分、非周期性成分を計算できるところまで作った。 使い方は CMakeLists.txt に下記のような依存を書く。この書き方が洗練されているかは C++ は普段遣いではないのでよく知らないので参考程度に。
## Dependencies ExternalProject_add(uzume_dsp PREFIX ${CMAKE_CURRENT_BINARY_DIR}/uzume_dsp GIT_REPOSITORY https://github.com/haruneko/uzume_dsp GIT_TAG master INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR} CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}" ) add_dependencies(your_app uzume_dsp) link_directories(your_app PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/lib) target_include_directories(your_app PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) target_link_libraries(your_app PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/lib/libuzume_dsp.a)
んで、例えば /path/to/input.wav
を /path/to/output.wav
に分析・再合成するのであれば下記のようなコードを書けばよい。
#include <algorithm> #include "SynthesizeImpulseResponseWithWORLD.hpp" #include "SynthesizePhraseWithWORLD.hpp" #include "Waveform.hpp" #include "WaveformSpectrogram.hpp" using namespace uzume::dsp; // This is a sample to use dsp directory. int main() { const char *inputPath = "/path/to/input.wav"; const char *outputPath = "/path/to/output.wav"; Waveform *input = Waveform::read(inputPath); Waveform *output = new Waveform(input->length, input->samplingFrequency); WaveformSpectrogram spectrogram(input); SynthesizeImpulseResponseWithWORLD irs(spectrogram.fftSize(), input->samplingFrequency); SynthesizePhraseWithWORLD synthesize(&irs); for(unsigned int i = 0; i < output->length; i++) { output->data[i] = 0.0; } PhraseSignal s(output->data, /* indexMin = */ 0, /* indexMax = */ output->length, output->samplingFrequency); PhraseParameters p(&spectrogram, /* startPhase = */ 0.0, /* startFractionalTimeShift = */ 0.0); synthesize(&s, &p); output->save(outputPath); }
肝は Spectrogram
クラスで、このクラスをとりあえず実装して spectrogram
に突っ込んでおけば上記のようなコードで音声が合成できる。上のコードでは WaveformSpectrogram
という波形データをスペクトルとして扱えるようにするクラスを利用しているが、他の実装に変えて例えば大量の波形データの切り貼りしたスペクトルを返すような実装にしてあげれば、いい感じの切り貼りツールが作れる。
直リンク系ライブラリの CMakeLists.txt の書き方調べた。
ポエム
CMakeLists.txt は便利なのだが、ちゃんと書き方を考えるほど切羽詰まった場面が自分にはなかった。今回は書いていて気になったのでちゃんと書く方法を調べたりしてみた。正直知見がなかったので何でググったっけを書いておくメモみたいなものである。
やりたいこと
- static に依存させるライブラリを作成したい
- 作成したライブラリは
ExternalProject
などで github 上から依存解決をしたい - なるべくならグローバルを汚染しないで依存解決をしたい
要は cmake でのライブラリの書き方がわかれば良い。ただ、静的ライブラリであれば add_subdirectory
すればよくね?という気持ちがないではない。今回は何が何でも静的リンクさせたかった、 cmake 経由でやれたことなかったからね。
やること
ライブラリ側
特殊なことは今回はしないのでこれだけ。target_sources
コマンドでコンパイル対象を指定し、 set_property
コマンドから PUBLIC_HEADER
プロパティを指定してインストール時にインストールすべきヘッダを指定して、 target_include_directories
で自身のヘッダファイルの場所を指定して、 install
コマンドでインストール先を指定している。あとはこの辺の単語でググれば最終的にはリファレンスにぶつかって解決できると思う。
- CMakeLists.txt
project(sample_lib VERSION 0.0.1 LANGUAGES CXX) add_library(sample_lib STATIC) target_sources(sample_lib PRIVATE # .cpp ファイルはここに書く ) set_property(TARGET sample_lib PROPERTY PUBLIC_HEADER # .hpp ファイルはここに書く ) target_include_directories(sample_lib INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> # ライブラリのビルド時に読み込むべき include ディレクトリ $<INSTALL_INTERFACE:include>) # ライブラリのインストール時(依存側で)読み込むべき include ディレクトリ install(TARGETS sample_lib EXPORT libsample_lib ARCHIVE DESTINATION lib LIBRARY DESTINATION lib PUBLIC_HEADER DESTINATION include)
利用する側
ExternalProject_add
で CMakeLists.txt のあるディレクトリやレポジトリを指定すると、 cmake
が走りよしなにビルドしたりインストールしてくれる。 INSTALL_DIR
などでインストール先指定しているけれど、特段指定しなければデフォルトのディレクトリに突っ込まれる。が、 Windows 環境でやっていたのでインストールステップ時に C:\Program Files\
下にライブラリをインストールしようとし、パーミッションが足りずに失敗する罠に引っかかったりするの利用側で指定してしまうのが、別段深い知見はないのだが、良いのではと思った。
- CMakeLists.txt
project(test_app) add_executable(test_app main.cpp) include(ExternalProject) ## Dependencies ExternalProject_add(sample_lib PREFIX ${CMAKE_CURRENT_BINARY_DIR}/sample_lib GIT_REPOSITORY https://path/to/sample_lib GIT_TAG master INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR} CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}" ) add_dependencies(test_app sample_lib) link_directories(test_app PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/lib) target_include_directories(test_app PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) target_link_libraries(test_app PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/lib/libsample_lib.a)
あとがき
別段ベストプラクティスではないと思うので動くもの書いたくらい。 lib
とか .a
とか cmake 側でちゃんと生成して作ったほうがいいし、そも add_subdirectory
と差があるんだっけとか、たぶん改善ポイントはすごく多い。
あと、今回作ってて依存側が依存先のことを超絶細かく知っていてもおかしくないという作りに本能が納得するまですごく時間がかかった。仕事では利用先のライブラリの名前とバージョン書いたら利用できることが多く、ここまでビルドやインストールの中身を意識したのが久々でたいへんつらかった。
コード書いてるもののクラス図を書いておく
ポエム
コード書いてたら何やってるのかわからなくなったのでクラス図を書き残したくなったがノートがないのでブログに書くことにした。
何するの
これのクラス図を適当に書いておく。書かないと 3 日で忘れるし。
クラス図
interface SynthesizePhrase { bool operator()(PhaseSignal*, PhraseParameters*) } class SynthesizePhraseWithWORLD { bool operator()(PhaseSignal*, PhraseParameters*) } SynthesizePhrase <|-u- SynthesizePhraseWithWORLD interface SynthesizeImpulseResponse{ bool operator()(ImpulseResponse *output, const ImpulseResponseParameters *input) } class SynthesizeImpulseResponseWithWORLD { bool operator()(ImpulseResponse *output, const ImpulseResponseParameters *input) const int fftSize const int samplingFrequency } SynthesizeImpulseResponse <|-- SynthesizeImpulseResponseWithWORLD SynthesizePhraseWithWORLD .r.> SynthesizeImpulseResponse class PhraseParameters { Spectrum *spectrogram[1..n] } class PhraseSignal { double *waveform } SynthesizePhrase -l- PhraseParameters SynthesizePhrase -l- PhraseSignal main .d.> PhraseSignal main .d.> SynthesizePhraseWithWORLD main .d.> PhraseParameters main .d.> SynthesizeImpulseResponseWithWORLD
cmake の ExternalProject_add と add_subdirectory を利用して gtest を導入した。
ポエム
しばらく C++ を書いていなかったが趣味は C++ でやりたくなったので gtest を導入してテストを書けるようにしたくなった(テストを書くとは言っていない)。
やりたいこと
googletest を手動インストールしないで cmake 側からよしなに依存を解決したい。
方針
googletest のレポジトリの方針の通り、 add_subdirectory
を利用して関連付けを行う。
googletest/README.md at master · google/googletest · GitHub
README にはこんなアツい思いが書かれている。
すでに CMake を利用しているプロジェクト内で gtest を使うのであれば、 gtest をそのプロジェクトの一部として直接ビルドしてしまうのが、ロバストで柔軟性の高いアプローチだ。このアプローチは、 GoogleTest のソースコードを メインのビルドでアクセス可能にし CMake の
add_subdirectory())
コマンドで GoogleTest を追加することで実現できる。こうすることで、 gtest と自身で書いた残りのプロジェクトとでコンパイラとリンカの設定が同一のものを利用できるようになり、例えば debug/release のような、互換性のないライブラリを利用したことによる問題を回避できるといった素晴らしいメリットを得られる。特に Windows では顕著に有用である。GoogleTest のソースコードをメインのビルドで利用するにはいくつかの方法がある。
GoogleTest のコードを手動でダウンロードし既知の場所に保存する。これは最も柔軟性の低いアプローチであり、 CI などを利用するのを難しくしてしまう可能性がある。
メインのプロジェクトのソースツリーにGoogleTest のコードを直接コピーとして埋め込んでしまう。しばしば見られる最も単純なアプローチではあるが、最新の状態を保つのが最も難しい方法でもある。
git submodule かそれに準ずるもので GoogleTest を追加してしまう。この方法も常に取れるとは限らず適切とは限らない。例えば、 git submodule 自身に長所と欠点があるからだ。
CMake のビルドの configure step の一部として GoogleTest をダウンロードする。この方法はほんの少し複雑ではある、が、他の方法のような制限を受けない。
上にあげた方法の最後の方法を実現するには、分割した小さなファイル、例えば
CMakeLists.txt.in
とする、に、 CMake の stage 中にビルド内にコピーされ、当該のコードがビルドの一部として実行されるよう小さな CMake のスクリプトを書けば良い。
なんかたくさん地雷こと踏んだのかね?ぼくも前回はローカルに直接インストールしたけど頭痛いのでそれはもうやりたくないしこの方法を使ってみる。なお、 cmake のバージョンは 2.8.2
以降が要求されている。
やったこと
README.md に書いたことをそのままやった。
ディレクトリ構成
-+--- ./ +--- CMakeLists.txt +-+- src/ | +- CMakeLists.txt | +- Target.cpp | +- Target.hpp | +-+- test/ +- CMakeLists.txt.in +- CMakeLists.txt +- main.cpp +- TargetText.cpp
./CMakeLists.txt
プロジェクトルートは子ディレクトリを追加する以外何もしない。
project(app_root) add_subdirectory(src) add_subdirectory(test)
./src/CMakeLists.txt
テスト対象となるコードはいつもどおり書けば良い。特段難しいことはしない。
project(app) # add_library(app) なりなんなりいつものように書く
./test/CMakeLists.txt.in
GoogleTest を CMake が作業ディレクトリ内でよしなに展開するようにしている。あんまりポイントはないが、ソースコードの置き場が ${CMAKE_BINARY_DIR}/googletest-src
になり、a
ファイルの置き場が ${CMAKE_BINARY_DIR}/googletest-build
になることだけ気をつけるといい。このコマンドは GoogleTest をダウンロードするがほかはなにもしない。
cmake_minimum_required(VERSION 2.8.2) project(googletest-download NONE) include(ExternalProject) ExternalProject_Add(googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG master SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" TEST_COMMAND "" )
./test/CMakeLists.txt
テストの実行側で GoogleTest に CMake を実行し GoogleTest のダウンロード先のディレクトリを add_subdirectory()
でコンパイル対象に加える。
project(app_test) # Download and unpack googletest at configure time configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt) execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . RESULT_VARIABLE result WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) if(result) message(FATAL_ERROR "CMake step for googletest failed: ${result}") endif() execute_process(COMMAND ${CMAKE_COMMAND} --build . RESULT_VARIABLE result WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download ) if(result) message(FATAL_ERROR "Build step for googletest failed: ${result}") endif() # Prevent overriding the parent project's compiler/linker # settings on Windows set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Add googletest directly to our build. This defines # the gtest and gtest_main targets. add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src ${CMAKE_CURRENT_BINARY_DIR}/googletest-build EXCLUDE_FROM_ALL) # The gtest/gtest_main targets carry header search path # dependencies automatically when using CMake 2.8.11 or # later. Otherwise we have to add them here ourselves. if (CMAKE_VERSION VERSION_LESS 2.8.11) include_directories("${gtest_SOURCE_DIR}/include") endif() # あとは GoogleTest を普通に使うだけでいい。
おわりに
英語読めば一発なんだが頭が弱っているから読むのにも時間かかる…とりあえず GoogleTest に限らず自作のライブラリとかにも応用できそうな気はした。しかしまあ CMake は便利なんだけど、結局ビルドプロセスを頭に入れないとかー、という気持ちにはなった。
UTAU 音源読み込む単純なライブラリ書いた
ポエム
GW に向けて暇だったので作った。いままで Qt だよりにしていたんだけれど、小さい部分はなるべく依存が小さくなるよう人生を悔い改めたので小さく作っている。
作ったもの
#include <iostream> #include "Voicebank.hpp" int main() { const char *voicebankDir = "/path/to/utau/voicebank"; std::cout << uzume::utau::Voicebank::from(voicebankDir).to_string(); return 0; }
信号処理しないので簡単。とりあえず小春音アミさんの17音階連続音読めたしテストないけど動くでしょ。