https://a.storyblok.com/f/270183/56451/5e3c1521ba/blog_ruby_record-voice-call_1200x600.png

Vonage Voice API WebSocketを使ってRubyで通話を録音する

最終更新日 November 5, 2020

所要時間:2 分

Vonage音声API Vonage Voice API ウェブソケット機能は最近ベータ版から一般版になりました。WebSocketsを使用すると、単一の永続的なTCP接続を介して双方向通信を作成することができます。WebSocketでは、複数のHTTPリクエストとレスポンスを処理する必要はありません。1つのWebSocket接続を開くだけで、テキストとバイナリのデータ通信を継続的に行うことができます。

WebSocketはHTTPレスポンスとリクエストのサイクルを簡素化できるが、アプリケーションを構築するには異なるパラダイムだ。ありがたいことに、一般的に使用されているプログラミング言語にはWebSocketツールがあり、プロセスの複雑さを軽減してくれる。

このチュートリアルでは、RubyでWebSocketを扱う小さなWebサーバーを構築します。このサーバーは、着信する音声通話、WebSocket接続を処理し、HTMLをレンダリングします。使用するのは Rackをウェブインターフェースとして使用し Thinを使用します。このチュートリアルでは、WebSocket の扱いに関する既存の知識は必要ありませんが、Ruby で Web サーバーを扱った軽い経験があることを前提としています。

tl;dr もしスキップしてアプリだけを実行したいのであれば、完全に動作するバージョンを GitHub.

前提条件

このチュートリアルでは、Ruby v2.7以上がマシンにインストールされている必要があります。また、このアプリケーションではいくつかのgemが使用されます。それぞれは後で作成する Gemfileに記載されており、コマンドラインから bundle installを実行することでインストールされます:

これでアプリケーションの実装に取りかかることができる。

Vonage API アカウント

このチュートリアルではバーチャル電話番号も使用します。購入するには 電話番号 > 電話番号の購入 番号を購入するにアクセスし、ニーズに合ったものを検索してください。

APIアカウントを設定する最後のステップは、Voice API Applicationを作成することです。プロビジョニングしたバーチャル電話番号をこのアプリケーションにリンクし、WebhookのURLを設定する。

Vonage API Dashboardから次の場所に移動します。 アプリケーションをクリックし 新しいアプリケーションを作成する.次のページが表示されます:

Dashboard Create ApplicationDashboard Create Application

アプリケーションを作成するために重視すべき点は、紫色で強調表示されています:

  • 名前アプリケーションに任意の名前を付けることができます。

  • 公開鍵と秘密鍵:認証用の公開鍵と秘密鍵のペアを生成します。秘密鍵ファイルがあなたのマシンにダウンロードされます。私たちのアプリケーションは着信音声通話を処理するだけなので、この鍵で何かをする必要はありません。

  • 機能:各アプリケーションは複数の機能を扱うことができる。今回の目的では、以下の機能をオンにするだけです。 Voice.

オプションの入力が終わったら 新規アプリケーションの作成ボタンを押して終了します。

アプリケーションが作成されたので、それを新しくプロビジョニングされた電話番号にリンクし、ウェブフックURLを設定しましょう。

前と同じように アプリケーションに移動し、アプリケーション名の横の楕円をクリックして 編集リンクをクリックします。

社内 機能セクションに以下のオプションが表示されます:

Application Webhook URL settingsApplication Webhook URL settings

を記入する必要があります。 イベントURL回答URL.前者は、Vonageが音声通話のすべてのイベントライフサイクルデータを送信する場所です。後者は、Vonageが新しい音声通話を開始するたびに送信する場所です。ここで提供されるURLは、Vonageが到達できるように、外部からアクセスできる必要があります。つまり localhostを使用することはできません。一般的な開発オプションはngrokで、以下のチュートリアルに従ってください。 チュートリアルを参照してください。

イベントURLとアンサーURLの末尾に /webhooks/event/webhooks/answerで終わることを確認してください。

また、Vonageの電話番号をこのアプリケーションに接続する必要があります。そのためには 番号>自分の番号に移動し、電話番号の横にある鉛筆のアイコンをクリックします。そしてオプションから新しいアプリケーションを選択し、電話番号をリンクさせます。保存 保存をクリックすると、この番号への着信はすべてあなたのアプリケーションに転送されます。

フォルダ構造の作成

Vonage APIアカウントと設定の準備ができたので、アプリケーションのフォルダ構造を作成しましょう。最終的には以下のようになります:

.
+-- recordings/
+-- views/
|   +-- index.html.erb
+-- app.rb
+-- Gemfile

アプリケーションのルートフォルダには app.rbが含まれ、これが音声通話の着信とWebSocket接続を処理するWebサーバーになります。また Gemfileここには依存関係を定義します。さらに2つのフォルダがあります: recordings/views/.と. recordings/フォルダには Voice API WebSockets 接続からの通話記録が保存されます。.フォルダは、アプリケーションの1つのビューを保存する場所です。 views/フォルダは、アプリケーションの1つのビューを保存する場所です。

依存関係の定義

の中に Gemfileに、アプリケーションで使用する以下のgemを追加する:

source 'https://rubygems.org'

gem 'wavefile'
gem 'faye-websocket'
gem 'json'
gem 'rack'
gem 'thin'

それぞれの宝石は特定の機能を果たす:

  • Wavefile:このgemを使って、生のオーディオデータをWAVファイルに変換します。

  • Faye: このgemを使ってWebSockets接続を処理します。

  • JSON: このgemは、Vonage Voice APIに送り返す通話命令をJSONに変換するために使用されます。

  • Rack: ウェブ・フレームワークとして Rack を使用します。

  • 薄い:このgemは、Rackの上にWebサーバを提供します。

コマンドラインから bundle installをコマンドラインから実行することで、これらのgemをすべてアプリケーションで利用できるようになります。

サーバーの構築

依存関係と変数の定義

これでウェブ・サーバーを構築する準備が整った。サーバーの構築で最初にすることは、いくつかの requireincludeステートメントを追加することです:

require 'rack'
require 'erb'
require 'faye/websocket'
require 'json'
require "wavefile"
include Rack
include WaveFile

この時点で、WebSockets接続リクエストの外部アクセス可能なURLと等しい定数変数も定義します。このURLは、Vonage Voice APIに新しいコールを受信したときに送り返す命令で使用します:

EXTERNAL_WS_URL = 'ws://example.com/cable'

上記のスニペットの example.comを外部からアクセス可能なURLに置き換えてください。

ヘルパーメソッド

このアプリケーションでは、2つのヘルパー・メソッドを利用する。今すぐ作成して、定数変数宣言の後に追加すればいい。

最初の方法です、 #create_wav_fileは、WebSocketを通じて受信したバイナリオーディオデータをWAVファイルに変換するプロセスを支援します。Wavefile gemの機能を利用してWAVファイルを作成し、後でアプリケーションで使用するファイル名を返します:

def create_wav_file(data, file_name)
  buffer = Buffer.new(data, Format.new(:mono, :pcm_16, 16000)) 
  puts "Audio Buffer Created..."
  writer = Writer.new(file_name, Format.new(:mono, :pcm_16, 16000))
  puts "New Audio File Created..."
  puts "Writing to the Buffer..."
  writer.write(buffer)
  puts "Closing Buffer Writing..."
  writer.close
  puts "WAV File Created..."
  file_name
end

上記のメソッドは、入力される音声が monoソースではなく stereoソースからのものであることを指定します。違いは、データの配列の配列ではなく、バイナリデータのフラットな配列で定義された単一のオーディオトラックがあることです。オーディオは pcm_16これはソースがリニアPCM 16ビットであることを意味する。最後に、ソースは 16000これはサンプルレートが16KHzであることを意味する。新しいWAVファイルは、オーディオ・バイナリ・ソース・データと同じオーディオ設定で作成されます。

番目のメソッド、 #erbは、ERBテンプレート・ファイルをユーザーにレンダリングするために使う短いメソッドである:

def erb(template)
  path = File.expand_path("#{template}")
  ERB.new(File.read(path)).result(binding)
end

残りのコードは、一連の mapステートメントで構成される。 Rack::Handlerミドルウェアにラップされます。

ルートの定義

アプリケーションのルートは、RackとThinをつなぐRackミドルウェアの内部で定義する必要があります。また Rack::Staticミドルウェアを使います。Faye WebSocketハンドラもここで初期化します:

Rack::Handler::Thin.run(Rack::Builder.new {
  Faye::WebSocket.load_adapter('thin')
  use(Rack::Static, urls: ["/recording"], root: 'recording')

次の4つのルートを構築する必要がある。 mapステートメントを構築する必要があります: /cable, /, /webhooks/answerそして /webhooks/answer.では、それをやってみよう。

最初のルートはWebSocket接続を処理する:

map('/cable') do
  run(->env{
    if Faye::WebSocket.websocket?(env)
      puts "WebSockets connection opened..."
      @call_data = []
      ws = Faye::WebSocket.new(env)
  
      ws.on :message do |event|
        if event.data.is_a?(Array)
          @call_data.append(event.data.pack('c*').unpack('s*'))
        else
          puts event.data
        end
      end
  
      ws.on :close do |event|
        puts 'WebSocket connection closed...'
        create_wav_file(@call_data.flatten, 'recording/recording.wav')
      end
  
      ws.rack_response
    end
  })
end

上記のルートでは、接続リクエストがWebSocketリクエストかどうかをチェックする。もしそうであれば、新しい Faye::WebSocket.WebSocketに送信されるデータには、テキストデータとバイナリデータの2種類があります。後者は常にバイトサイズの整数を配列にした形で送信されます。

このように event.dataが配列オブジェクトかどうかをチェックすることができる。配列であれば、WAVファイルの作成に使用するバイナリ音声データであることがわかる。そうでない場合は、Vonage Voice API からのステータス更新である。この場合は、コンソールにログを記録します。

重要な注意点があります:Faye WebSockets gemは、上で述べたようにバイナリデータをバイトサイズの整数に変換します。しかしこれは、正確に1バイトまたは8ビットサイズの整数に変換することを意味します。Vonage Voice APIは音声のバイナリデータを16ビット整数で送信する。つまり、アプリケーションは8ビットのバイナリ・データを16ビットに変換する必要がある。Ruby標準ライブラリの #pack#unpackインスタンス変数 @call_dataインスタンス変数に追加します。これは、理解しやすい音声を生成するために必要なステップです。

次のルートは indexビューを提供します。ファイルがあるかどうかをチェックし、ファイルがあればインスタンス変数でそれをビューに渡します:

map('/') do
  if File.exist?('recording.wav')
    @call_status = 'Audio Loaded!'
    @file = 'recording.wav'
  end
  run(->env{
    [200, { 'Content-Type' => 'text/html'}, [erb("views/index.html.erb")]]})
end

この /webhooks/answerルートは Vonage Voice API に特化した NCCO (Nexmo Call Control Object) と呼ばれる命令を返します。 NCCO (Nexmo Call Control Object)と呼ばれる特別な命令を返します。JSONとして送り返す命令は、Voice APIにWebSocket接続を開きたいことを伝え、接続を開始するためのWebSocket URLを提供します。また、Voice API には、発信者に短いメッセージを話し、まもなく通話がストリーミングされることを知らせたいことも伝えます:

map('/webhooks/answer') do
  run(->env{
    ncco = [
      {
        "action": "talk",
        "text": "You will be streaming momentarily."
      },
      {
        "action": "connect",
        "endpoint": [
          {
            "type": "websocket",
            "uri": "#{EXTERNAL_WS_URL}",
            "content-type": "audio/l16;rate=16000",
          }
        ]
      }
    ].to_json

    [200, { 'Content-Type' => 'application/json' }, [ncco]]
  })
end

最後に作成するルートは、Voice APIがアプリケーションに送信するコールのイベントライフサイクルデータを処理します。私たちはこのデータに対して何もしたくありません。 200ステータスコード

  map('/webhooks/event') do
  run(->env{
    [200, { 'Content-Type' => 'text/html'}, ['']]
  })
end

最後に Rack::Builderブロックを閉じる:

}, Port: 9292)

アプリケーションを実行する前に作成しなければならない最後の項目は、ビューです。

ビューの作成

このアプリケーションには1つのビューしかありません。このビューにはアプリケーションのルートURL、つまり localhost:9292もしくは 127.0.0.1:9292.このビューは、再生するオーディオを <audio>要素で表示します:

<html>
  <head>
    <title>Ruby Vonage WebSockets Demo</title>
  </head>
  <body>
    <h1>Vonage WebSockets + Ruby == ♥</h1>
    <p>Welcome to the Vonage WebSockets demo in Ruby</p>  
    <h2>Your Audio To Playback</h2>
    <p>Once you have finished your call, your audio will be available to playback from here.</p>
    <div id="audio-status">
      <%= @call_status %>
      <br />
      <% if @file %>
        <audio
          controls
          src="recording/<%= @file %>">
            Your browser does not support the
            <code>audio</code> element.
        </audio>
      <% end %>
    </div>
  </body>
</html>

ビューはルートで作成したインスタンス変数を使って <audio>要素を表示するかどうかを決定します。

これでアプリケーションを実行する準備ができた!

アプリケーションの実行

これでアプリケーションを実行する準備が整いました。実行するには、アプリのルート・フォルダーでコマンドラインから以下を実行する:

bundle exec rackup app.rb

また、ngrokや同様のツールを使って、ウェブサーバーが外部からアクセス可能であることを確認することも忘れずに。

この時点で、Vonageバーチャル電話番号に電話をかけて、アプリケーションに電話をかけます。話し終わったら、電話を切ることができます。ウェブブラウザでアプリにアクセスすると、オーディオプレーヤーが表示され、録音した音声が再生されます。おめでとうございます!

さらに読む

このチュートリアルでは、Vonage Voice API の WebSockets 機能を Ruby で使えるようにするための基本的な機能を紹介しました。この機能でできることはまだまだたくさんあります。Vonage Voice API WebSockets についてもっと知りたい方は、以下をご覧ください:

シェア:

https://a.storyblok.com/f/270183/384x384/e5480d2945/ben-greenberg.png
Ben Greenbergヴォネージの卒業生

ベンはセカンドキャリアの開発者で、以前は成人教育、コミュニティ組織化、非営利団体運営の分野で10年を過ごした。彼はVonageの開発者支援者として働いていた。コミュニティ開発とテクノロジーの交差点について定期的に執筆している。南カリフォルニア出身で、長年ニューヨークに住んでいたが、現在はイスラエルのテルアビブ近郊に在住。