Voicy Tech Blog

Voicy公式Techブログ

Voice Techを支える音声処理APIをPython+Falcon+Docker+ECSで開発した話 〜技術選定の苦労話を添えて〜

こんにちは! 11月からVoicyでエンジニアとして働き始めたぱんでぃーです。

前回のエントリーからしばらく間が空きましたが、 ここ数ヶ月でVoicyを取り巻く環境も大きく変わってきました。

具体的には

www.wantedly.com

などがありましたが、今回のエントリーは2つ目のGoogle Homeにも関連するテーマで書きたいと思います。

今回のプロジェクトの背景

現在、Voicyでは下記のように1回の配信の中に複数のチャプターが含まれています。

f:id:voice-tech:20171212203554p:plain

実際の音声ファイルもチャプターごとに存在するのですが、Google HomeやAlexaなどのデバイスから音声コンテンツを配信する際にはひとまとまりのファイルに収まっていた方が都合が良い事があります。 そこで、今後Voicyが様々な声に関するサービスを展開していくための処理基板として、音声処理用のRESTful API(以下、AudioProcessor)を開発する事になりました。

今回はそのプロジェクトの中での技術選定の理由や全体のシステム構成などについて、下記の流れでご紹介していきたいと思います。

主要な技術スタック

まずは主要な技術スタックとその選定理由を紹介します。

音声ファイルの処理エンジン

FFmpeg 音声だけでなく動画ファイルにも対応した、マルチメディアファイル用のエンコーダです。 音楽業界のエンジニアさんも使用している、音声処理ソフトウェアのスタンダードなライブラリの一つです。

FFmpeg(エフエフエムペグ)は動画と音声を記録・変換・再生するためのフリーソフトウェアである[3]。Unixオペレーティングシステム (OS) 生まれであるが現在ではクロスプラットフォームであり、libavcodec(動画/音声のコーデックライブラリ)、libavformat(動画/音声のコンテナライブラリ)、libswscale(色空間・サイズ変換ライブラリ)、libavfilter(動画のフィルタリングライブラリ)などを含む。ライセンスはコンパイル時のオプションによりLGPLGPLに決定される。コマンドラインから使用することができる。対応コーデックが多く、多彩なオプションを使用可能なため、幅広く利用されている。 https://ja.wikipedia.org/wiki/FFmpeg

クロスプラットフォームの音声処理用のソフトウェアとしてはPortAudioも対抗馬として挙がっていましたが、FFmpegは「動画ファイルから音声ファイルだけを抽出する」といったこともできるため、今後のサービス展開も考慮してFFmpegを採用しました。

開発言語・フレームワーク

  • Python (ver. 3.6.3)
  • Pydub Python用に開発された、FFmpegの音声処理機能のみを簡単に扱うためのWrapperライブラリ。(ちなみに上述したPortAudio用のWrapperライブラリとしては、PyAudioがあります。)
  • Falcon RESTful APIに特化した高速で軽量なフレームワーク

VoicyではAPIなどのサーバーサイドの言語はGOに寄せていこうという流れがありますが、GO用の音声処理ライブラリで良さそうなものが見つからなかったため、第二の選択肢としてPythonで探した結果、Pydubというライブラリが良さそうだったため採用に至りました。

社内にはFFmpegに精通したエンジニアがいなかったため、公式ドキュメントの充実さは選定基準として重きを置いていましたが、その点、Pydubは公式のAPIドキュメントが非常に充実しており、FFmpegを使用したことが無い場合でも容易に扱うことができました。

GO用のライブラリとしては、gmfが良さそうでしたが、Beta版である事とドキュメントが少ない点がネックとなりました。

また、API開発用のFalconについては下記の記事などが参考になります。

Pythonフレームワークとしては、DjangoやFlask、Pyramidに比べると新しいですが、公式ドキュメントGithubのWikiが充実しており、RESTful APIに必要なRoutingやValidationなどのロジックをシンプルに書けるので、新規でAPIを開発するのであればFalcon一択と言っていいのではないでしょうか。

今回のプロジェクトとは直接的には関係ありませんが、VoicyではまだPythonで書かれたプロダクトがありませんでした。ただ、今後Voicyがユーザのデモグラ情報やビヘイビア情報を活用したサービスを展開していく上で、Pythonを使ったデータ分析は避けては通れない道となるのでこの機会にPython資産を作りたかったという背景もありました。(自分の"好みの声だけ"に包まれた生活とか、想像しただけでステキじゃないですか!?)

APIの実行環境

DockerとAlpine Linuxについて

まずは何と言ってもDocker。これの採用は外せませんでした。 Pythonと言えば、初学者が環境開発(特に仮想環境の構築)で悩むことで有名ですね。

qiita.com

また、FFmpegも複雑・・・という程ではないですが、インストールにひとクセあるソフトウェアですので、Dockerを使ってImageをPullするだけでサクッと同一の環境を作れるのは非常にありがたいです。

DockerのOSは軽量でDocker Imageには最適な、Alpine Linuxを選んでいます。 実際に採用しているのは公式のAlpine Imageではなく、下記のImageですが、

https://hub.docker.com/r/gliderlabs/alpine/

両者の違いやgliderlabs/alpineを選ぶ理由については下記のサイトで詳しく述べられているため、このエントリーでは割愛します。

また、APIの実行環境を構成するためのDockerfileはこちらです。

# Use Alpine Linux as a parent image
FROM gliderlabs/alpine:3.6

MAINTAINER voicy.jp

# change system timezone to Asia/Tokyo
RUN apk update && \
    apk-install tzdata && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

# setup pyton runtime
RUN apk-install \
        'python3<3.7' \
        'python3-dev<3.7' \
        build-base && \
    echo "Dockerfile"        >> /etc/buidfiles && \
    echo ".onbuild"          >> /etc/buidfiles && \
    echo "requirements.txt"  >> /etc/buidfiles

RUN apk-install 'ffmpeg<3.5'

WORKDIR /app

# Copy the current directory contents into the container at /app
COPY . /app

# Install any needed packages specified in requirements.txt
RUN pip3 install --trusted-host pypi.python.org -r requirements.txt -c constraints.txt

# Make port 8000 available to the world outside this container
EXPOSE 8000

RUN chmod +x /app/entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]

いくつか解説しますと、

# change system timezone to Asia/Tokyo
RUN apk update && \
    apk-install tzdata && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

Alpineのパッケージ管理ツールである、apkコマンド(Ubuntuで言うところのaptコマンド)の更新を行った後、Dockerコンテナ内のOSのタイムゾーンAsia/Tokyoに設定しています。

# Install any needed packages specified in requirements.txt
RUN pip3 install --trusted-host pypi.python.org -r requirements.txt -c constraints.txt

APIの実行に必要な依存パッケージのインストールを行っています。 プログラムが直接依存しているパッケージをrequirements.txtにバージョン指定無しで記載し、

awscli
boto3
falcon
gunicorn
jsonschema
pipdeptree
pydub
python-json-logger
requests

constraints.txtにこれらの依存パッケージが依存しているものも含めた、全ての依存パッケージをバージョン指定と共に記載することで、柔軟にパッケージ管理を行えるようにしています。

appnope==0.1.0
astroid==1.5.3
awscli==1.12.1
.
. (省略)
.
traitlets==4.3.2
urllib3==1.22
wcwidth==0.1.7
wrapt==1.10.11

詳しくは下記で解説されています。

RUN chmod +x /app/entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]

APIサーバーを起動するためのスクリプトに実行権限を付与してから、実行しています。docker buildコマンドでImageを作成する際に、COPY . /appした時のオリジナルファイルに実行権限があれば良いのですが、念のために設定しておいた方が安心です。(私も実際にここで少しハマりました。。。)

entrypoint.shでは単純にPython製のWSGI(Web Server Gateway Interface)サーバであるgunicornを起動しているだけですが、後述のECSの「タスク定義」で環境変数ENVを設定する事により、gunicornの設定ファイルを開発用と本番用で切り替えています。

#!/bin/ash

if [[ "${ENV}" = 'prod' ]]; then
  gunicorn --config ./environments/prod/gunicorn.conf.py 'audio-processor.app:get_app()'
else
  gunicorn --config ./environments/dev/gunicorn.conf.py 'audio-processor.app:get_app()'
fi

gunicornの起動設定以外にも環境変数を使って、下記のような制御を行っています。

  • データベースやS3などの接続先を本番用と開発用で切り替え。
  • 本番環境ではログ分析を行いやすくするためにJSON形式で出力し、開発環境では見やすいプレーンテキストでログ出力。

このへんの設計方針に関しては、The Twelve-Factor App (日本語訳)が非常に参考になりますので、まだご覧になっていない方は是非読んでみてください。

コンテナオーケストレーションとしてのECS

最後に、このプロジェクトを通して最も悩まされたAWSでのコンテナ管理サービスの選定についてです。 本エントリーを執筆中の2017年12月12日時点では選択肢としては下記の3つになるかと思います。

これら3つの違いについてはこの記事がよくまとまっています。

qiita.com

最終的には、採用実績やWeb上のドキュメントが豊富な点でECSを採用しましたが、実際に運用を始めてみると下記のようなデメリットも見えてきたので、AWSでのベストプラクティスについては今後も検討する必要がありそうです。

  • Dockerコンテナに対するロードバランシングとコンテナのホストマシンであるEC2インスタンに対するロードバランシングを2重で管理する必要がある。
  • SwarmやKubernetesといったDockerネイティブのオーケストレーターを使えない。 ※ Kubernetesについては次期バージョンでネイティブサポート。
    [速報]DockerがKubernetesとの統合およびサポートを発表。DockerCon EU 2017 - Publickey
  • クラスター、サービス、タスクといったECS独自の概念を覚える必要があり、これがまた少し複雑。

Docker for AWSAWSではなくDockerコミュニティ製のオーケストレーターですが、CloudWatch Logsなどのサービスとも簡単に連携でき、機能としては最も魅力的に映ったのでもう少しドキュメントが充実してきたら積極的に採用してみたいところです。

アップデートの早いECSでのコンテナデプロイ環境の構築については公式ドキュメントを読み込むのが一番ですが、上述したECS独自の概念があるため、下記も併せて読むのがオススメです。

コンテナ管理サービスを選定中に「正直、コンテナオーケストレーションではKubernetesがデファクトスタンダードになりつつあるのにな。。。。今回はAWS上で構築すると決めたけど、やっぱりGKE使いたい。。。」と憂鬱な思いにもなりましたが、ついに出ましたね!! 先日開催されたAWS re:Invent 2017で更に2つのコンテナ管理サービスが登場し、AWS上でもKubernetesが使えるようになりそうです。

  • EKS(Elastic Container Service for Kubernetes)
  • AWS Fargate

tech.recruit-mp.co.jp

システム構成

最終的にAudioProcessor周りのシステム構成は下記のようになりました。 (今回のエントリーに関わりの少ないサービスは色を薄くしています。)

f:id:voice-tech:20171212212919p:plain

一例として、Google Home用に複数の音声ファイルを結合する処理の流れを解説します。

  1. パーソナリティが録音アプリで登録した複数の音声ファイルがS3のVoicy App Bucketに登録され、URIがRDSに登録される。
  2. 定期実行されているバッチが結合待ちの音声ファイル情報をRDSから取得し、ELBを通しECSのAudioProcessorに音声ファイル結合のリクエストを送る
  3. AudioProcessorはS3のVoicy App Bucketから音声ファイルをダウンロードし、FFmpegのWrapperライブラリを通し音声ファイルを結合。その後、S3のAudioProcessor Bucketにアップロード。
  4. AudioProcessorからのレスポンスで受け取ったURI情報を元に、AudioProcessor Bucketから結合済みのファイルをダウンロードし、Voicy App Bucketへアップロード。その後、Voicy App BucketURI情報をRDSに登録。(AudioProcessorはサービスとは独立した位置づけにしているため、バケット間の直接移動はあえて行っていません)
  5. Google Homeで音声コンテンツが呼び出された際はAPIサーバを介し、結合済みの音声ファイルをS3から配信。

以上がざっくりとした処理の流れです。 今回のスプリント内では達成できなかった今後の課題としては、

  • TravisCIやCircleCIを使った自動テスト・コンテナの自動デプロイ環境の構築
  • ECSでのコンテナオーケストレーションの最適化

などを進めて行きたいと思っています。

長くなりましたが、今回のエントリーが皆さんのDockerやPythonを使った開発プロジェクトの助けになれば幸いです。 また、Voicyではフロントエンド・バックエンドを問わずフルスタックで新しい技術に挑戦できる環境がありますので、「新しい声の世界・サービスを自分で創ってみたい!!」というエンジニアの方はオフィスに遊びに来てもらえると嬉しいです!

次回のエントリーはVoicyに新卒で入った、美人エンジニアの投稿を予定していますので、乞うご期待ください!

Go1.8とAngular4をプロダクションで使ってみた!Voicy WEB版の開発裏話

こんにちは。Voicy CTOの窪田です。

7月18日にVoicyのWeb版をリリースいたしました!さっそく使っていただけたでしょうか?
https://voicy.jp/

f:id:voice-tech:20170725220611p:plain これにより、いままでiPhone/iPadでしか聴けなかったパーソナリティの声を、PCやAndroidのブラウザでもお聴きいただけます!リリース直後には多くの方がSNS上に喜びの声を書き込んでいただいたのを読んで、本当に作ってよかったなぁと感じました。どうもありがとうございます!こういった生の声を直接聴けるのは、toCサービスをやる醍醐味だと思います。

そこで今回はこのWEB版の開発裏話をさせていただきます。

開発言語

言語はサーバーサイドがGo1.8で、クライアントサイドがAngular4を使用しています。それぞれのバージョンは2月と3月にリリースされたばかりの最新で、エンジニアとしては開発していてとても楽しかったです。Goについては以前のブログでも書いているので、よければそちらもご覧ください。

SPAの必要性

WEB版を使っていただくとわかりますが、画面遷移した場合でも、再生プレーヤーが上部に固定で表示されています。動画であれば画面中央にどーんと表示されてユーザの目線はそこに固定されるのですが、音声の良いところは他のページを見ながら聴くという、ながら聴きができるところです。

言い方を変えれば、他のページへ遷移しても音声を止めずに再生し続けないといけないということです。普通であれば他のページへ遷移すると音声も動画も止まってしまいますので、必然的に1つのページ内で表示を切り返るSPA(Single Page Application)で作る必要あり、Angularで開発することにしました。

OGP対応

ありがたいことに、Voicyではパーソナリティを始めとしたユーザーのみなさまが各チャンネルをFacebookTwitterといったSNSで多くのシェアをしていただいています。現在iOSアプリからシェアするとURLが表示されるだけなのですが、WEB版のURLで表示していただくと、このようにシェアした配信を表す写真とテキストが表示されます。(アプリも対応予定です。しばらくお待ちください)

f:id:voice-tech:20170723212347p:plain

この表示を行うために設定しているのがOGP(Open Graph Protocol)です。HTMLのMETAタグに設定するのですが、SPAでは大きな問題があります。それはSPAはシングルページというだけあって、大元のHTMLファイルは1つだけです。つまりMETAタグに設定できるOGPの内容が1つしかありません。そのため、どの配信をシェアしても同じ写真+テキストしか表示されないことになります。

Voicyでは日々多くのパーソナリティの方が配信しており、シェアした時にはその人の写真と配信内容のテキストをどうしても表示したいという強い思いがあり、いろいろと試行錯誤を行いました。

METAタグにflagmentを設定する

AJAXページのSEO対策としてMETAタグに
 <meta name=“fragment” content=“!”>
を設定するとクローラーが良い感じにhogehogeしてくれるというのがあります。(詳細は省略)

OGP対応もそこでできるかなと思いましたが、シェアした場合に各SNSがどこまでクローラーと同じように処理してくれるかが不明で調べている時間もなかったこと、対応するのに思っていたよりも工数がかかりそうだったことの2点を理由に見送りました。

SSRを使用する

Angularは2からSSR(Server Side Rendering)にも対応しています。SSRとはAngularによる描画をサーバーサイドで行い、結果のHTMLをクライアントへ返す方法です。これによりリクエストURLに応じてOGPを設定したMETAタグをクライアントへ返すことができますが、それだと結局画面遷移のたびにHTMLを書き換えることになるため、再生を止めないというSPA採用の目的が果たせなくなってしまいます。

index.htmlを動的に作成する

最終的にはこの方法を採用しました。Angularだけで解決することは諦め、PHPでアクセスURLを元にサーバーから必要な情報を取得し、OGPのMETAタグを動的に生成しています。プログラム自体はそこまで複雑でもないので、その時オフィスに来ていたエンジニアの方にさくっと作っていただきました(笑)

課題

トップ画面パフォーマンス

WEB版のトップ画面では多くの配信情報を取得しているのですが、そのパフォーマンスがあまり良くないという課題があります。ここはDBからの取得部分で改善できそうなポイントがあるので近々対応予定です。

モバイルで連続再生できない

iPhoneAndroidのブラウザでアクセスした場合、1つの記事の再生が終わっても次が自動で再生されません。これはモバイルブラウザの仕様で、プログラムから自動で音声や動画の再生を開始することができないためです。メディアデータはサイズが大きいため、ユーザーが想定していないデータを裏側でダウンロードし、パケット通信量を使いすぎないようにこういった制限が掛かっています。そのため、再生を開始するには画面をタップする等のユーザアクションをトリガーにする必要があるのです。(ブラウザによって多少実装が異なる場合もあります)

実は最初の音声を再生するタイミングで他の音声も全てダウンロードしておけば連続再生できるという裏技も一応あります。実際1パーソナリティの1配信分だけであればそこまでのデータ量ではないのかもしれません。しかし、ユーザーがサイト内を回遊し、いろんな人の配信を少しずつ聴くといった行動をした場合、たいして聴いてもいないのに大量のパケットを消費してしまうことになります。この点ついては、今後の使われ方を見ながらどうすべきか判断させていただきたいと思います。ご不便をおかけして申し訳ありませんが、ご了承ください。

Angular遅延ロード

今はフロントサイドの全てのコードを1つのModuleとして作っています。そのため、最初にアクセスしたタイミングで全てのページのデータをブラウザ側にダウンロードしているのですが、その中には設定画面のような使用頻度の低い画面も含まれています。AngularではModuleを分割して必要になった時にだけダウンロードする遅延ロード(Lazy Loading)というのもできるので、今後画面が増えていくことも考えて早めに対応していきたいと思います。

開発体制

開発は6月からの約1ヶ月半かけて2名で行いました。企画やデザインはもう少し前から取り掛かっていましたが。

サーバーサイドのGoとクライアントサイドのAngularでそれぞれ担当を分け、私は主にクライアントサイドを担当しました。2人ともGo、Angularは今回が初めてで、最近Hello Worldからスタートしたばかりなのですが、そのわりには1ヶ月半でここまで作りきれたのはなかなかではないかと手前味噌ながら思っております。

ちなみにたった2名で作ったのは単純にVoicy社内にそれしかエンジニアがいなかったからです(`・ω・´)キリッ
つまりはエンジニアを100%つぎ込んでWEB版を作っていたので、その間アプリ版の改修が止まってしまい、ユーザーの皆さまには申し訳なく思っております。しかしながら、VoicyではWEB版に限らず他にも新しいサービスをどんどん立ち上げていこうと考えています!改善を止めずに新規サービスを立ち上げていく。そんな無茶を実現するためにも、現在Voicyではエンジニアを大募集中です!本当に立ち上げフェーズのベンチャーで自分の力を試してみたい方、まずは話を聞いてみたいだけでも全然かまいませんので、一度オフィスへ遊びに来て下さい!
https://open.talentio.com/1/c/voicy/requisitions/detail/4803

GYAO!主催エンタメデジコンエンジニア勉強会で弊社伊東が登壇しました!

去る5/17、GYAO!さんが主催のイベントに弊社の伊東が登壇致しました!
デジタルコンテンツの中でも特にエンターテインメントに関するものを取り扱っているITエンジニアが集まり、働き方や技術情報、ノウハウを共有するイベント…ということで、弊社の伊東もVoicy代表としてVoicyを支えるインフラの技術についてお話させていただきました。

f:id:voice-tech:20170618182653j:plain

この記事では、当日のイベント全体の様子を伊東のセッション内容も含めてお伝え致します。

クラウドで実現する動画配信とAIのコラボレーション

f:id:voice-tech:20170618182841j:plain 日本マイクロソフトの畠山さんにAzureが提供する最新のAPIについてご紹介頂きました。
RESTさえ分かれば簡単に使える映像認識のAPIが画期的でした!

たとえば、水泳をしている男性の写真をAPIに投げると
・ポルノ度(裸だけど、スポーツだろうという分析でポルノ度は18%)
・カテゴリ(スイミング)
・性別
・年齢
以上のような情報が解析されて返ってきます。

画像だけではなく、Video indexer APIというAPIを使うと動画も解析できます。
どのシーン(コマ)にどの人が写っているのか、それは合計何分なのか、 音声、顔、文字、感情を検出して検索することもできるという優れもの。是非試してみたいですね。

Voicyシステムリプレースに至るまで

続いて、我らがVoicyのCIO兼ITOこと伊東のセッション!

f:id:voice-tech:20170618183031j:plain

Voicyがシステムをリプレースするに至るまでの経緯や判断基準についてをお話しました。 Voicyではこれまでネイティブアプリのみを提供していましたが、WEB版の開発に伴い既存のシステムをAPI化
する必要が出てきました。そこで既存環境を刷新することになり、現在は以下のような構成になるようにリプレースを進めています。

f:id:voice-tech:20170618190908p:plain

技術選定の最も重要なポイントは、事業の事を第一に考えて選定するということ。
あくまで事業を具現化するための1つの手法として技術があるのであって、先に技術がくるのではない。企画の思考を技術的に無理だから…といって狭めてしまうことがないようにしたい。というアツい想いを語っていました。

ちなみに、言語選定にまつわる詳しい話はコチラの記事にまとまっておりますので是非ごらんください。
VoicyがGoLangとEchoを採択した理由。 - Voicy Tech Blog

気持ちを伝える動画技術

Gyaoでフロントエンドエンジニアをされている浜田さんのセッションでは、浜田さんがハッカソンで作ったものをご紹介頂きました。

f:id:voice-tech:20170618191303j:plain

GoogleのCloud Vision APIとFirebaseを使った感情解析をするアプリの事例です。プレゼンを行う時に、オーディエンスの反応が分かっていれば、自分のプレゼンの仕方ももっと工夫ができるのでは…。そんな思いから、浜田さんはオーディエンスの表情から感情をリアルタイムで解析し、プレゼンターに伝えてあげることで自分のプレゼンの良し悪しを知ることができるアプリを作りました。冒頭のマイクロソフトさんのAPIでも画像解析の話題がありましたが、GoogleIBMなど、各社が色んなCognitive APIを提供しているので、アイディア次第で簡単に面白いアプリが作れますね。

風力と脳波を使ったものづくり

続いてもハッカソンでの制作事例です。Hack Dayという、見て・触って・作って楽しめる、Yahoo! JAPAN主催のクリエイティブイベントで高橋さんが制作したアプリは、なんと風力と脳波でVR上の女の子のスカートをめくるというもの。

f:id:voice-tech:20170618194808j:plain

「技術を使うって楽しい!」「技術を使うってエンターテイメントなんだ!」というのがよく伝わってくるプレゼンテーションでした。

Gyao エンタメテックカンパニーの働き方

Gyaoさんがエンタメテックカンパニーとして実践している人事制度についてご紹介がありました。

1. 大人の学びの法則 大人はどうやって学ぶのか?
→ 7割は仕事の直接の経験から学ぶ。研修より経験。

2. コルブの学習経験モデル
具体的な経験 > 内省的な観察 > 抽象的な概念化(教訓を引き出す) > 積極的な実験
このプロセスを回せる仕組みづくりを。

3. キムの成功循環モデル
関係の質 > 思考の質 > 行動の質 > 結果の質
→【結果から求めるのではなく、関係性に重きをおく】

上のような思想をベースにして、以下の2点を軸に制度設計を行なっているそうです。

人財開発

  • 人財開発カルテ:強み、課題、キャリアプラン、理想の経験を半年に一回更新
  • 人財開発会議:部門における人財開発プランの検討、全社視点でのキャリア形成支援の二回立てで実施

コンディション

健康と関係性 - ななめ会議:隣の部署の上司からヒアリングと議論、FB - 働き方、勤怠管理:36協定をきちんと守る、勤怠実績を経営会議にて報告 - どこでもオフィス・フレックス勤務 - 診療所の併設・メンタルヘルス対策

なかなか制度への落とし込みは大変なことだと思いますが、よく考え実施されているという印象を受けました。

パネルディスカッション

続いて、エンタメ業界のエンジニアとしてのスキルセットや働き方についてのパネルディスカッションが行われました。

f:id:voice-tech:20170618201229j:plain

Q. エンタメサービスでエンジニアは今後どんなスキルが必要でしょうか?

オンプレミスが過去のものとなりつつなる今、何がこれから求められる?

畠山さん:クラウドとAI。アプリケーションはもちろん作れるのは大前提として、クラウドは使えるようになったほうがいい。Paasのサービスは絶対に必要。AIについての最低限の理解、機械学習についても情報収集していくべき。

伊東:機械学習ビッグデータ解析。データが必要になってからデータを取ろうと思っても過去のデータは収集できないので、先を見通してデータを取得しておくべき。

高橋さん: ひとつは、 新しい技術を知っていること。AI、機械学習など今は簡単に扱えるようになってきている。そういった技術があること自体を知っているか知らないかの差は大きい。そして、 開発者自身が楽しんで作っていけるマインドセットも重要。

浜田さん: VODサービスは溢れ、どこも同じような機能や同じようなWEBサイトが多くなってきていると感じる。デザイン、UIがある意味均一化してきている。エンタメを謳うからにはそういった流れの中でフロントのUI/UXの差別化が必要。

AI、クラウド機械学習といったキーワードが目立ちました。
総じていえば「新しい技術にチャレンジしていくこと」へ抵抗感を持たず、前のめりに試していく好奇心と実行力が求めらるということでしょうか。

Q. リモートワークでのチーム開発について。うまくいっている方法や失敗談は?

畠山さん: コアタイムがなくなっている。自由があってグローバルチームの働き方としてはやりやすい。 反面、自由になりすぎると、どこでもいつでも働けてしまうため、過度に働きすぎてしまう人が出てくるのが課題。 自分自身でタイムマネージメントしていかなければならない。

伊東: リモートワークでも、全員が集まる時間は週の中で必ず確保するべき。
コミュニケーションの総量がどうしても少なくなってしまうため、関係性が構築できていない状態でのリモートワークは難しさを感じる。既に関係性が構築できている状態ならリモートも可能だと思う。

高橋さん: リモートワークはよく実施している。Googleハングアウトで常に多拠点間のオフィスとオフィスを繋いだ状態にして仮想的に同じ場所にいるような取り組みも行っている

浜田さん: みんな好きに働いているが、リモートワークはディスカッションには向いていない気がする。そういう場合は顔を合わせたほうがよい。

登壇者が所属するどの企業もリモートワークを取り入れているようでした。リモートワークを実施すると集中して作業ができる、通勤の時間削減などのメリットがありますが、コミュニケーションが必要なシーンでのバランスの取り方が重要ということです。

まとめ

今回は、エンターテインメントに関するものを取り扱っているITエンジニアが集まり、働き方や技術情報、ノウハウを共有するという趣旨のイベントだったからか、Coginitive系のAPIの話が多かったのが印象的でした。Voicyでも今後音声の解析や音声ビッグデータ等の領域で面白いことができるよう、更に事業を成長させて参ります!

声×テクノロジーで新しい文化を作りたい!Voicyでは一緒に働いてくれるエンジニアを募集中です。アットホームなオフィスですので、気になった方はぜひお茶でも飲みにきてください。 www.wantedly.com

スペシャルサンクス

写真を提供してくださった「ヤフー株式会社 公式カメラ隊」の皆様、ありがとうございます!!


Swift3でCore Audioを使用した音声ファイル変換

こんにちは!Voicy CTOの窪田です。

今回はSwift3でCore Audioを使って、WAVファイルをAACに変換する方法についてお話ししようと思います。

経緯

以前、Voicyの録音アプリでは音声を保存する際にはWAVファイルを使用していました。しかし無圧縮のWAVではサイズが大きいため、通信速度が遅い環境でアップロードに時間がかかってしまうという課題がありました。

そこで、録音が終わった後にWAVを圧縮形式のAACに変換すれば、WAVで録音している部分には手を加えなくて済むし良いのではないかと思い今回紹介するプログラムを作成しました。しかし、現実はそんなに甘くはなく、、、

Voicyでは最大10分間の録音ができるのですが、フルに録音したものをiPhone5AACに変換すると2分以上かかってしまったのです。iPhone6以降であれば問題なかったのですが、Voicyで活躍しているパーソナリティにはiPhone5を使用している方もいるので、録音するたびにそんな長時間待たせるわけにはいきません。

というわけで、最終的には録音しながらAACでリアルタイムに保存する対応を行うことになりました。そのため、Voicyのアプリでは今回紹介するフォーマットの変換処理は使っておりません。そして、せっかく作ったのにもったいないからブログのネタにしてやろうとか思ったわけでもありません(`・ω・´)キリッ

ちなみに、WAVファイルで録音していた時でもアップロード後にサーバー側で圧縮していたので、再生アプリでダウンロードする際の通信量は抑えられていました。サーバー側での圧縮処理にはAWSAmazon Elastic Transcoderを使用しています。

Core Audioを使用した変換

Core Audioと言っても処理によって複数のフレームワークやサービスに別れています。今回の変換処理ではAudio Toolboxフレームワークに含まれるExtended Audio File Serviceを使用します。昔のiOSではAACの変換は対応していなかったのですが、iOS 3.1あたりから対応したようです。10まで出ている現在では気にする必要はなさそうですね。ちなみにMP3への変換は未だ未対応です。

余談ですが、先日MP3のライセンスが終了というニュースがありました。未対応の理由がライセンスだったのであればこれを期に対応するのか、それともMP3はもう古いとしてずっと未対応なのかはちょっと気になるところではあります。個人的にAppleは後者ではないかと思っていますが。

WAVファイルを開く

それでは順番に変換処理を説明していきます。まずCore Audioを使用するにはAVFoundationをインポートする必要があります。

import AVFoundation

次に変換元となるWAVファイルを開きます。ファイルはプロジェクト内に元から存在するものとし、Bundle.mainでURLを取得してきています。

// 変換元のinput.wavを指すURL
let inFileUrl = URL(fileURLWithPath: Bundle.main.path(forResource: "input", ofType: "wav")!)

// WAVファイルを開く
var inFile: ExtAudioFileRef?
_ = ExtAudioFileOpenURL(inFileUrl as CFURL, &inFile)

// WAVファイルの情報を取得
var inASBDSize = UInt32(MemoryLayout<AudioStreamBasicDescription>.size)
var inFormat = AudioStreamBasicDescription()
ExtAudioFileGetProperty(inFile!, kExtAudioFileProperty_FileDataFormat, &inASBDSize, &inFormat)

そんなに難しいところは無いと思います。最後の部分では変換元であるWAVファイルの情報を取得しています。サンプリングレートやビットレート、ステレオかモノラルかといった音声ファイルの基本的な情報が含まれており、Core AudioではASBD(Audio Stream Basic Description)と呼ばれる構造体で表されます。

AACファイルを開く

次に変換先となるAACファイルを開きます。ここではテンポラリフォルダにファイルを作成しています。

// 変換先のoutput.aacを指すURL
let toUrl = URL(fileURLWithPath: (NSTemporaryDirectory() as NSString).appendingPathComponent("output.aac"))

// AACファイル情報
var outASBDSize = UInt32(MemoryLayout<AudioStreamBasicDescription>.size)
var aacFormat = AudioStreamBasicDescription()
aacFormat.mFormatID         = kAudioFormatMPEG4AAC
aacFormat.mSampleRate       = 44100.0
aacFormat.mChannelsPerFrame = 1
AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, nil, &outASBDSize, &aacFormat)

// AACファイルを開く
_ = ExtAudioFileCreateWithURL(
    outFileUrl as CFURL,
    kAudioFileM4AType,
    &aacFormat,
    nil,
    AudioFileFlags.eraseFile.rawValue,
    &outFile)

先ほどのWAVファイルでは、ファイルからASBDを取得していましたが、AACの場合はまだファイルが存在しないため、書き込むASBDの値を自分で指定する必要があります。ここではAACであることと、サンプリングレートに44.1kHz、チャンネル数に1(モノラル) を指定しています。

ASBDには他にも情報を指定する必要があるのですが、AudioFormatGetPropertyにkAudioFormatProperty_FormatInfoを指定することでその他の情報を自動的に埋めてくれる便利機能があるのでそれを使用しています。

ファイルを開く際にもkAudioFileM4ATypeでAACであることを指定しています。同じAACを指すものでもASBDに指定していたkAudioFormatMPEG4AACとは違うので間違えないように気をつけてください。AudioFileFlags.eraseFileは既存のファイルがあった場合に上書きすることを表しています。

変換ルールを指定

これでWAV、AACの両ファイルを開けましたが、じゃあどこでデータの変換処理するの?となります。

答えはファイルを読み込む時、もしくは書き込む時で、どちらで行うかは自分で指定する必要があります。ここでは書き込む時に指定してみました。

// 書き込みプロパティ設定
ExtAudioFileSetProperty(
    outFile!,
    kExtAudioFileProperty_ClientDataFormat,
    inASBDSize,
    &inFormat)

AACファイル(outFile)に対して、WAVファイルの情報(inASBDSize, inFormat)を指定しています。これにより、WAVファイルのデータを渡すからAACに変換して保存してねという命令をしていることになります。

WAV -> AAC 変換処理

いよいよメインの処理です。とはいえ先ほど指定した変換ルールのおかげで難しいことは全て自動的にやってくれるため、プログラマーがやることとしては

  1. バッファを作成
  2. WAVファイルを順次読み込み
  3. AACファイル書き込み

これだけです。

まずはバッファを作成します。

// バッファ作成
var readFrameSize: UInt32 = 1024 // 一度に読み込むフレーム数
var bufferSize = readFrameSize * inFormat.mBytesPerPacket
var buffer: UnsafeMutableRawPointer = malloc(Int(bufferSize))
defer { free(buffer) }

var audioBuffer = AudioBufferList()
audioBuffer.mNumberBuffers = 1
audioBuffer.mBuffers.mNumberChannels = inFormat.mChannelsPerFrame
audioBuffer.mBuffers.mDataByteSize = bufferSize
audioBuffer.mBuffers.mData = buffer

var readFrameSize: UInt32 = 1024

フレーム数は必要に応じて変更してください。
ちなみにこの数字を増減しても処理時間はあまり変わりませんでした。

var bufferSize = readFrameSize * inFormat.mBytesPerPacket

WAVファイルの場合、フレーム数とパケット数が同じなので、バッファサイズは フレーム数 × パケットサイズ になります。

var buffer: UnsafeMutableRawPointer = malloc(Int(bufferSize))
defer { free(buffer) }

バッファ領域はmallocで確保しています。mallocで確保した場合は自動的に解放されないため、処理を抜ける時に解放されるようにするため、deferでfreeを行なっています。

バッファを確保できたら、次はファイルの読み込みと書き込みを行ないます。

// ファイルを読み込んで出力
while (true) {
    // WAVファイル読み込み
    ExtAudioFileRead(inFile, &readFrameSize, &audioBuffer)
    // 読み込むデータがなくなれば終了
    if readFrameSize <= 0 { break }
    // AACファイル書き込み
    ExtAudioFileWrite(outFile, readFrameSize, &audioBuffer)
}

バッファにはWAVファイルのデータが読み込まれ、それをExtAudioFileWriteへ渡した際に、先ほど登録した変換ルールが適用されてAACに変換して保存されます。想像つくかもしれませんが、この変換処理が一番重いです。とはいえ最近の端末に限定して良いのであればそこまで気にしなくてもいいのかもしれませんが。

ファイルを閉じる

// ファイルを閉じる
ExtAudioFileDispose(inFile!)
ExtAudioFileDispose(outFile!)

最後にファイルを閉じてオブジェクトを破棄します。Extended Audio File ServiceではExtAudioFileDisposeを呼ぶとファイルのクローズとオブジェクトの破棄を同時に行ってくれます。

終わりに

Core AudioはiPhoneで音声アプリをつくるなら避けては通れないものです。ポインタもあっちこっちで出てくるので最初はとっつきにくいかも知れませんが、今回紹介したExtended Audio File Serviceのように便利な関数も多数用意されており、知れば知るほど楽しくなってきます。VoicyではこれからもCore Audioを使いこなし、音声技術のスペシャリスト集団になっていきたいと思います!

Voicyのメンバーがどういった思いでサービスを作っているのか、ぜひともこちらのインタビューも合わせてお読みください!

Twitterがしゃべる!?Playerカードの作成方法

こんにちは!Voicy CTOの窪田です。

先日Voicyのサイトにチャンネル紹介のページが追加されました! 公式チャンネルや人気のパーソナリティをピックアップして掲載しているのですが、それぞれのページをTwitterでシェアすると、なんとタイムライン上で音声を聴くことができます!

例えばこちらのVoicy公式チャンネルのURLをTwitterでつぶやくと
https://voicy.jp/channel/voicy.html

このように表示されます。

f:id:voice-tech:20170504190928p:plain

そしてツイートをクリックすると再生画面が展開されて、Twitterのタイムライン上で音声を聴くことができます!

f:id:voice-tech:20170504190933p:plain

そこで、今回はこの再生機能の作成方法ついてお話しさせていただきます。

Playerカードについて

タイムライン上での再生機能は、Twitterが提供しているPlayerカードというものを使用しています。正確にはTwitterカードで選べるカードタイプのうちの1つがPlayerカードです。表示される再生画面はHTMLで自作するので、デザインはかなり自由に作ることができ、もちろん音声に限らず動画も再生可能です。

Twitterカード自体は誰でもすぐに使用できるのですが、Playerカードだけはドメインの利用申請が必要になります。また、HTTPSでアクセスできるWebサーバーも必要です。

Playerカードは以下の手順で作成していきます。公式マニュアルも合わせてお読みください。

  1. 再生画面をHTMLで作成
  2. 再生画面をHTTPSでアクセスできるように配置
  3. シェアされるページに専用のMETAタグを設定
  4. シェアされるページのドメインTwitterに申請
  5. Card Validatorで動作確認

Playerカード作成手順

1. 再生画面をHTMLで作成

再生画面を作成する際の注意点が公式マニュアルの"申請時のポイント"に載っているので、それさえ守れば自由にデザインや機能を実装できます。

後から変更することもできるので、Voicyではまず最小限の機能だけを半日くらいで開発して申請を行い、その後正式版として作り直しました。申請時には特に以下の内容を意識して開発しました。

  • iPhoneAndroidTwitterアプリ、twitter.com、mobile.twitter.comなどのすべてのTwitterクライアントでエクスペリエンスをテストしてください。これらすべてのTwitterクライアントで動作しないカードは承認されません。
  • すべてのクライアントで動画コンテンツが表示エリアの横幅いっぱいに表示されるようにしてください。
  • 動画コンテンツとオーディオコンテンツに関する注意
    1. コンテンツが自動的に再生される動画では、初期設定を “消音” にする
    2. 長さが10秒を超えるコンテンツは自動的再生されないようにする
    3. 停止、一時停止、再生ボタンを設置する

2. 再生画面をHTTPSでアクセスできるように配置

再生画面はHTTPSでアクセスできる必要があります。実はVoicyのサイトはこれまでHTTPSに対応していなかったのですが、これを機にサイト全体をHTTPS化しました。

3. シェアされるページに専用のMETAタグを設定

Playerカードを使用するために、シェアされるページのMETAタグで再生画面に関する情報を指定します。この設定が必要なのはシェアされるページであって、再生画面のHTMLではないのでご注意ください。

さきほどのVoicy公式チャンネルページでは以下のように指定しています。

<meta name="twitter:card" content="player" />
<meta name="twitter:title" content="Voicy - Voicy公式チャンネル" />
<meta name="twitter:description" content="毎朝月曜から土曜まで、新鮮なニュースをお伝えするVoicy公式チャンネル。 これさえ聞けば日々の主要ニュースは網羅できるはず。通勤・通学のお供におすすめです。" />
<meta name="twitter:image" content="https://voicy.jp/tw/img/thumb/voicy.png" />
<meta name="twitter:site" content="@voicy_jp" />
<meta name="twitter:player" content="https://voicy.jp/tw/#voicy" />
<meta name="twitter:player:width" content="320" />
<meta name="twitter:player:height" content="450" />
プロパティ 必須 説明
twitter:card 必須 Playerカードを使用する場合は"player"固定です。
twitter:title 必須 タイムラインに表示されるサイトのタイトルです。
twitter:description 必須 タイムラインに表示されるサイトのタイトルです。
twitter:image 必須 タイムラインに表示されるサムネイル画像です。iframe未対応のブラウだでは再生画面の代わりに表示されます。
twitter:site 任意 TwitterのIDです。"twitter:site"もしくは"twitter:site:id"のどちらかが必須です。
twitter:player 必須 再生画面のURLです。HTTPSで指定する必要があります。
twitter:player:width 必須 再生画面の横幅です。
twitter:player:height 必須 再生画面の縦幅です。

プロパティに指定できる値はこの他にもあります。詳細は公式マニュアルをご覧いただきたいのですが、日本語ページと英語ページで説明が異なる部分もあるので、実際に作って動作確認することをオススメします。

4. シェアされるページのドメインTwitterに申請

公式マニュアルでは、「Playerカードを申請する」とあるので、新しいPlayerカードを作成したらその度に申請が必要なのかと思いましたが、実際はドメイン毎の承認になるので、一度承認されれば以降の申請は必要なさそうです。

マニュアルではCard Validatorで動作確認をしてから申請するように書いてあるのですが、Playerカードの場合はドメインの承認前はエラーとなってしまい動作確認ができません。

申請前の画面

f:id:voice-tech:20170504190942p:plain

そのため、多分大丈夫だろうという見込みで申請を行いました(笑) エラー画面に表示されているRequestApprovalのボタンを押して必要事項を入力します。

申請画面

f:id:voice-tech:20170504190938p:plain

Descriptionには簡単なサイトの説明を英語で入力します。"Mark my cards ~“のチェックボックスは基本的にチェック不要です。もしsensitive contentを扱うのであればチェックしてください。その他はデフォルトで入力されていました。

申請が完了するとエラーメッセージが変わります。

f:id:voice-tech:20170504190946p:plain

申請した翌日には承認され、以下のメールが送られてきました。

f:id:voice-tech:20170504191226p:plain

5. Card validatorで動作確認

無事承認されるとCard Validatorで確認することができます。

f:id:voice-tech:20170504190950p:plain

Audioカードについて

今回紹介したPlayerカードの他にも、TwitterにはAudioカードという似たような名前のものがあります。

Audioカードの方がPlayerカードよりもリッチな表現ができるようなのですが、残念ながらこれを使用するにはTwitterと企業提携を行う必要があるそうです。もしTwitterの方がいらしたらぜひ連絡ください(笑)

現在は@SoundCloudのアカウントがよくAudioカードを使用しています。スマホのアプリで見るとわかるのですが、PlayerカードだとWebViewが立ち上がって再生画面が表示されるのに対し、Audioカードではポップアップで表示されるので、ユーザー体験を考えるとAudioカードの方が良いと思います。

終わりに

現在は動画全盛期なので、動画のシェア方法は充実しているのですが、音声や音楽といった音だけのコンテンツに関しては十分とは言えません。Facebookでもタイムライン上で動画を流すことはできますが、音声だけとなると簡単にはできないのが現状です。(Music Storiesというサービスがあるのですが、新規の募集は受け付けていないそうです。。。)

それはさておき、今回のサイトリニューアルで、これまでアプリでしか聴けなかった音声を(一部ですが)ブラウザや、Twitter上でも聴けるようになりました!Voicyでは今後もあらゆる手段で声をお届けできるよう、新しいことへ積極的にチャレンジしていきます!

Voicyのメンバーがどういった思いでサービスを作っているのか、ぜひともこちらのインタビューも合わせてお読みください!

VoicyがGoLangとEchoを採択した理由。

Voicy社 ITO 伊東です。

現在iPhoneアプリを提供しているVoicyですが、WEB版を鋭意製作中であります。
これまではネイティブアプリ開発用の環境を構築しておりましたが、Webに最適化または双方で使うために環境を構築し直す必要が出てきました。

そこで、今回は環境を構築し直すにあたっての言語とフレームワークの選定についてお話します。

結構いろんな言語と比較したんですが、結果的には GoLang × Echoを採択しました。

言語選定の前提となるミッション

今回はAPIを作るのに適した環境を作るという事が一番重要なミッション。
いわゆるマイクロサービスというやつですね。小さい単位でサービスを作ってAPIを通して各機能を呼び出すということ。そうすることで

  • 開発チームがサービスごとに分かれて、得意な言語を利用して各サービスの開発を進めることができる
  • 変更をかけたいときは、システム全体ではなく、その小さなサービスごとに変更をかけられる
  • 小さなサービスで開発単位を進めるため、ビルドやテストの期間が短くなり開発効率が上がる
  • モノリシックなシステムだと何か障害が起きたときに、どこがおかしいのかルート構造をたどるのに時間がかかるが、原因の突き止めが比較的容易

つまり、用途・目的ごとに小さな(マイクロな)サービスを作っておくことで、「変化に強くて柔軟性の高い、アプリケーション開発を行おう」というのがマイクロサービスなのです。

引用元 https://www.salesforce.com/jp/blog/2016/03/microservices.html

はい、僕の言いたいことを全ていい切ってくれてますね(しろめ
コレガヤリタイカラゲンゴカエルンダ、コジンテキナスキルアップデハナインダ・・・

最適な言語はなんなのか?比べてみた

APIを作るのに適した環境を作るというのを念頭に比較してますが、個人の主観も大きく入っておりますのでその点ご了承くださいませ(しろめ

・Java8 & Lagom

Ruby & Rails

PHP & Lumen

・Python3 & Nameko

GoLang & Echo

を検討致しました。
個人的にはRoRが好きです。
今回はORマッパーを使わない想定です(ふるえ
※別に嫌いではないですがAPIという事で1ミリ秒でも早くレスポンスを返したいという事を優先しております。

Java8 & Lagom

速度    ◎
開発効率  △
API化 ◯
採用    ☓

Java8で以前マイクロサービスを開発をしたことがありまして、結構簡単に出来ましたね。(もともとJava使いだからですが)個人的な主観でいってしまいますが 、
選択したくない(キリ
なぜなら別の言語にチャレンジしたいから(しろめ

RubyOnRails

速度    △
開発効率  ◎
API化  △
採用    ◯

Railsはとても好きなフレームワークで ルーティングも簡単に制御できますし、開発効率も高いです。 Viewを使わずActiveRecordのORマッパーも使わないという事ですと、Railsを採用する意味が大きく損なわれてしまいましたね・・

PHP & Lumen

速度    △
開発効率  ◎
API化    ◯
採用    △

LumenはLaravelのマイクロフレームワーク版で同じIlluminateコンポーネントを使用しております。 ルーティング処理もとても評判が良く、とても悩みました。 PHP自体は使えるエンジニアもとても多く、CakeやLaravel等も開発効率が高くサービスリリースを行う上でとても強い武器になると思います。
が、実行速度が遅い(スクリプト言語に求めるなという話ですけどね)事から却下。

Python3 & Nameko

速度    △
開発効率  ◯
API化 ◯
採用    ◯

Python結構いい感じですね、APIを構築するのにとてもあっている気がしますし、永続処理や非同期処理も様々なツールを合わせることで上手く対応できますね。音声ビッグデータ解析等いろいろ行う予定であるVoicyにとってパイソンは通らなくてはならない道ではあります。適材適所の言語を使用すれば良いと私自身は考えますので、その時にパイソンを選択すれば良いかと思います。

GoLang & Echo

速度    ◯
開発効率  ◯
API化  ◯
採用    ◯

GoLang使いました。 APIを作るために存在しているようなくらい簡単に出来てしまいますね。 継承が出来ないので(無理無理できないことはないけど・・・) コーディングするときに少し戸惑いはありますが ルーティング簡単、APIつくるの簡単(というか、それ以外無理ですねこれ)という形でしょうか。

優勝 GoLang

個人的な権限を行使して、面白そうなGoLangに決めました。

GoLang用のフレームワークの選定(すでにEchoとか書いてしまっておりますが・・)

  • Gin
  • Iris
  • Echo の3つを検討致しました。

結論からいうと、Echoを採用致しました。

フレームワークの実行速度で比較

Gin  734
Echo 879
Kami 1237
Goji   1299
Bone 1463
http   1854
Gocraft 1915
Gorilla 3944
Martini 5823
ns/op

参考記事
http://qiita.com/najeira/items/bdc988c4e93b3b5ccf2f

個人的にはGinかEchoの2択に絞られました。

書きやすさで比較

今回はAPIを作るのに適した環境を作るという事を一番の念頭に入れてはおりましたので、その点でどのフレームワークが書きやすそうかという事を考えました。

参考記事
http://blog.iktakahiro.sh/entry/2016/12/10/090000

http://qiita.com/keika299/items/62e806ae42828bb3567a

実際に書いて動作させてみましたがルーティングがとても簡単。

    // ルーティング
    e.Get("/hello", handle.MainPage())
    e.Post("/hello", handle.MainPage())

これで事が足りてしまいます。

まとめ:GoLangAPI作るならEchoが良さそう!

Go自体並列処理が行うのが得意なので、色々とめんどうな処理もは早く処理できそうですし。

Pythonも最後まで捨てがたかったのですが

  1. 学習コスト
  2. 採用
  3. 実行速度(コンパイルする分早いのは当たり前ですけどね)
    (4.個人の趣味)

でしょうか。
RoRからGolangに移行するひとが結構知り合いにも居ましたが気落ちが大分わかりました。

クラウド放送局アプリVoicyの音声収録から配信までのシステム構成

はじめまして。VoicyでCTOをしている窪田です。

さて、今回は最初の技術記事ということで簡単なアプリの紹介を踏まえながらと全体のシステム構成についてお話ししたいと思います。

Voicyというアプリについて

Voicyとは人の声で聞くニュースメディアです。 最近はITで声と言えばAmazonのAlexaや初音ミクに代表される音声認識音声合成を思い浮かべる方も多いと思いますが、Voicyでは機械ではなく人の声でニュースを届けています。

ユーザーは自分の声でコンテンツを読み上げて配信する「パーソナリティ」と「リスナー」に分けられます。 そのためVoicyのアプリも「パーソナリティが使用する録音用」と「一般リスナーが使用する再生用」の二つがあり、並行して開発を進めています。(現在はiPhoneのみ)

f:id:voice-tech:20170401155929p:plain

Voicy [ボイシー] - ニュースは声で聴く時代へを App Store で

Voicy Recorderを App Store で

使い方としては、オーディションに合格したパーソナリティが録音用アプリでニュース記事を読んだり、好きなことについて語ったりしたものを収録し、一般のリスナーが再生用アプリで聞くという流れになります。

システム構成

Voicyのシステム構成を簡単な図にまとめたものがこちらになります。

f:id:voice-tech:20170401155943p:plain

1. 録音アプリ

録音用アプリは誰でもダウンロードして収録することができますが、再生用アプリへ配信して他の人に聞いてもらうためには、定期的に行っているパーソナリティオーディションに応募して合格する必要があります。そのため、アプリ内では配信権限の有無により項目の表示/非表示制御や、権限チェックが必要になります。もちろん不正アクセスを考慮して、サーバーサイドでも同様に権限チェックを行っています。

アプリで収録した音声ファイルはサーバーへ送られ、その後圧縮、フォーマット変換されます。音声ファイルは動画ほどではないとはいえ、通常のWebサービスに比べると大きなサイズのデータを扱うことになるため、サーバーのメモリ使用量は注意しておく必要があります。現在は十分なスペックを確保できていますが、ユーザー数の増加に合わせて当然見直しが必要になってくるため、モニタリングは欠かせません。

2. 再生アプリ

耳から便利にニュースが聞けるといった機能性だけであれば音声合成で十分です。Voicyでは、それに加えてパーソナリティの個性や人間味といった"その人らしさ"を伝えてたいと考えています。そのため、UIではパーソナリティの写真をメインにデザインし、初めてアプリを開いたリスナーが自分好みのチャンネルに出会えるような情報をトップ画面に載せています。

また、アプリを楽しく操作できるための工夫として、フリックや画面遷移したりするタイミングで効果音を鳴らしています。始めにアプリを開発する時は、音声、BGM、効果音といった音のデータをどう扱うかで苦労しました。例えば、マナーモード中にアプリを触ってる時は効果音鳴って欲しくないけど、再生ボタンを押したら音声は流れるようにしたい。そして音声が流れてる時であれば、音が出ている状態なのでマナーモード中であっても効果音鳴らしてもいいんじゃないか、といった具合です。

3. 記事クローラー

パーソナリティが読む記事を収集するためのクローラで、収集された記事はリアルタイムに録音用アプリに表示されます。クローラー毎日新聞スポニチといった提携メディアから記事を取得していますが、取得方法がメディアによって異なるため、提携先が増えるたびにクローラーを開発する必要があります。

とはいえ毎回ゼロから作るわけにもいかないので、クローラーのための共通フレームワークを自作し、面倒な処理のほとんどはフレームワークで行えるようになっています。フレームワークも一度作って終わりではなく、常に改善しているため、当初新しいクローラーを追加するのに丸一日かかっていた作業が、現在は早ければ1時間かからず、長くても3,4時間あれば作れるまでに効率化されました。

4. バッチ処理

上記以外にも、日次レポートの作成やキャッシュデータの更新といったバッチ処理が定期的に実行されています。こちらもクローラー同様に共通フレームワークを自作しているので、新しいバッチを追加する際でも、エンジニアは面倒なことは気にせずにビジネスロジックだけに集中して開発できるようになっています。

最後に

いかがでしたでしょうか。とても簡単ではありましたがVoicyが声を届けるための仕組みを紹介させていただきました。今後はそれぞれのシステムで使用されている技術ついて、もっと詳しく紹介していければと思います。

Voicyのサービスはまだ始まったばかりで、これからどんどん進化していきます!また、まだお話できないような新しいサービスもいろいろと考えております。その実現のためにもVoicyでは現在エンジニアを絶賛募集中です!ちょっと話を聞いてみたい、ベンチャーの雰囲気を感じてみたいといった理由でも構いませんので、ぜひ一度オフィスに遊びに来てください。

www.wantedly.com

パーソナリティをやっている声優やアナウンサーの方々もふらっと遊びに来てくれる、とてもフレンドリーでアットホームなオフィスです! こんなメンバーが働いている現場です。 よろしければインタビューもぜひご覧ください!