[Work/Class/サウンドトラック制作基礎/setup]

Studio One Primeのダウンロードとインストール

Studio One Prime

高性能DAWであるStudio Oneの無料版である.

以下の手順のスクリーンショット作成はMacで行っているが,Windowsでも全く同じである.

Studio One Primeのページhttps://www.mi7.co.jp/products/presonus/studioone/prime/へ行く.

My PreSonusへ進む

手順としては,書いてある通りで,MyPreSonusアカウントを作って,そのアカウントを使い0円で購入する形である.My PreSonusへ進む.

My PreSonusへ進む

手順としては,書いてある通りで,MyPreSonusアカウントを作って,そのアカウントを使い0円で購入する形である.My PreSonusへ進む.

アカウントの作成

適切に入力してアカウントを作成する.ソーシャルメディアアカウントで作っても良い.

アクティベーションメールの送信

アクティベーションメールが届いていなければ「アクティベーションメールを再送信」する.

アクティベーションメールの送信

メールが届いていたら「アカウントをアクティベートするためにこちらをクリック」リンクで飛んでアクティベートする.

アカウントがアクティベートされた状態

アカウントがアクティベートされた状態.

Studio One Primeのショップページhttps://shop.presonus.com/products/studio-one-prods/Studio-One-3-Primeへ行く.

ショップページ

ショップページで「Add To Cart」

0円であることを確認

0円であることを確認して「Proceed to Checkout」

購入された状態

購入されたので「閉じる」

購入済みリストに追加されている

Studio One Primeが購入済みリストに追加されているので,「全てのコンテンツをGET」に進む.

プロダクトキーが表示されている

プロダクトキーが表示さていることを確認して,インストーラをダウンロード.

プロダクトキーが表示されている

プロダクトキーが表示さていることを確認して,インストーラをダウンロード.

インストーラを実行

インストーラを実行.(Macの場合は.dmgをマウントして,Applicationsにドラッグドロップする)

インストール完了

インストール完了.Windowsの場合はC:\Program Files,Macの場合は/Applicationsに入る.

使用許諾

起動すると,使用許諾が出てくるので「同意する」

PreSonusアカウントの入力画面

PreSonusアカウントの入力画面が出てくるので,入力して「ログイン」

Studio One Primeがアクティベートされる

認証が成功すると,ソフトウェアがアクティベートされて使用できるようになる.

追加コンテンツのダウンロードとインストール

追加コンテンツのダウンロードとインストールを訊かれたら,よほど容量が厳しくなければインストールしておくことを勧める.

追加コンテンツのインストール中

追加コンテンツのダウンロードとインストール中.

Studio Oneの起動

Studio Oneが起動した画面.


[Work/Class/サウンドトラック制作基礎/setup]

FL Studio Demo Beta版のダウンロードとインストール(Mac用)

FL Studio Mac用Beta版

Macにはまだ正式版がないので,Macで作業したい人はBeta版を用いる.(ユーザ登録などの細かいことについては,自分で行うこと)

http://www.image-line.com/downloads/flstudiodownload.htmlへ行く.

ダウンロードページからBeta版を選択

ダウンロードページからBeta版を選択.

フォーラムへ行く

フォーラムへ行く.(あらかじめユーザ登録は済ませておくこと)

フォーラムの中からトピックを選択

フォーラムの中から,Beta版のトピックを選択.

インストーラをダウンロード

インストーラをダウンロード.

ダウンロードしたインストーラのイメージをダブルクリックするとインストーラパッケージが出てくる

ダウンロードしたイメージ(.img)をダブルクリックしてマウントすると,インストーラパッケージ(.pkg)が出てくるので,これを実行.

インストーラが起動

インストーラが起動するので「続ける」.

インストーラでインストール

「インストール」

インストール中

インストール中

インストール完了

インストールが完了したら「閉じる」.

インストール完了

インストールが完了したら「閉じる」.

インストールされている

/Applicationsの中に「FL Studio Mac Beta」が入っているので,起動.

起動成功

起動成功.


[Work/Class/ES2015で動的Webアプリ/WebSocketを用いた動的Webアプリ]

ES2015でWebSocket - ビデオチャットのように端末側のカメラ画像をWebSocketで送る / 2017-11-02 (木)

WebRTC

JavaScriptからWebカメラを扱う

WebRTCとは,簡単に言えば,端末のメディアデバイス(カメラやマイク)をWebブラウザのJavaScriptから扱うための規格である.Safari&iOSは11から対応している.

Videoに関しては,ChromeとSafari,つまりAndroidとiOSで書法というか扱い方の詳細に違いがあるので,クライアントのWebブラウザをを検出して,分岐させて両方書いておく必要がある.

セキュアHTTP(https://での接続)

WebRTCを使うためには,https://で始まるセキュアHTTPで接続する必要がある.

コード例

どのブラウザでアクセスしているかの文字列UserAgentを最初に取得し,iOSデバイス,Androidデバイス,PCのChrome, MacのSafariをそれぞれ切り分けて分岐処理を行う.

iOSとAndroidはスマホ本体を縦にして撮影している,つまりカメラから取得できる画像が縦長(3:4)であると想定し,PCのChromeとSafariは横長(4:3)であると想定する.

なお,ボタンをスマホブラウザから押しやすくするために,skeletonという軽量CSSフレームワークを利用している.

<!DOCTYPE html>
<!-- WSCameraImage_drawToVideoArea.html -->
<html lang="ja">
<head>
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta charset="utf-8" />
  <title>スマホやPCのカメラをWebブラウザから使う</title>
  <script type="text/javascript" src="./WSCameraImage_drawToVideoArea.js">
  </script>
  <!-- skeltonフレームワークを使う -->
  <link rel="stylesheet" href="./style/normalize.css" />
  <link rel="stylesheet" href="./style/skeleton.css" />
</head>
<body>
  <div id="display_user_agent">
  </div>
  <div>
    <!-- skeltonフレームワークを使った押しやすいボタン.クラスを指定するだけ -->
    <button class="button button=primary" onclick="startVideo()">Start</button><br/>
    <!-- ビデオ表示エリア -->
    <video id="local_video" autoplay playsinline></video>
  </div>
</body>
</html>
// WSCameraImage_drawToCanvas.js
'use strict';
function startVideo() {
    if(navigator.getUserMedia || navigator.webkitGetUserMedia || 
       navigator.mozGetUserMedia || navigator.msGetUserMedia){
	if(window.thisBrowser == 'ios'){
	    // for iOS Devices
	    // スマホを立てて撮影を想定
	    const medias = {audio: false,
			    video: {width: 480,
				    height: 640,
				    facingMode : {exact: "environment"}}};
			    // facingModeというのは,背面カメラと顔カメラのどちらを使うか
			    // 'environment'を指定すると背面カメラ,
			    // 'user'を指定すると顔カメラになる
	    navigator.getUserMedia(medias,
				   (stream) => {
				       window.localVideo.srcObject = stream;
				   },
				   (error) => {
				       alert('navigator.getUserMedia() error:' + error);
				   });
	}
	else if(window.thisBrowser == 'android'){
	    // for Android Chrome (with both Front and Back Camera)
	    // iOSと同じくスマホを立てて撮影しているのを想定
	    const medias = {audio: false,
			    video: {width: 480,
				    wheight: 640,
				    facingMode:{exact: "environment"}}};
	    navigator.mediaDevices.getUserMedia(medias) // ChromeはPCもAndroidもPromiseで記述する
		.then(stream => {
		    window.localVideo.src = window.URL.createObjectURL(stream);
		})
		.catch(error => {
		    console.error('navigator.mediaDvice.getUserMedia() error:', error);
		    return;
		});
	}
	else if(window.thisBrowser == 'pcchrome'){   
	    // PC Chrome (with only 1 Camera for Face)
	    const medias = {audio: false,
			    video: {width: 640,
				    wheight: 480}};
	    navigator.mediaDevices.getUserMedia(medias)
		.then(stream => {
		    window.localVideo.src = window.URL.createObjectURL(stream);
		})
		.catch(error => {
		    console.error('navigator.mediaDvice.getUserMedia() error:', error);
		    return;
		});
	}
	else if(window.thisBrowser == 'pcsafari'){
	    // for Mac Safari (with only 1 Camera for Face)
	    const medias = {audio: false,
			    video: {width:640, 
				    height:480}};
	    navigator.getUserMedia(medias,
				   (stream) => {
				       window.localVideo.srcObject = stream;
				   },
				   (error) => {
				       console.error('navigator.getUserMedia() error:', error);
				   });
	}
    }
}

window.addEventListener('load', () => {
    // ブラウザ情報が入っているUserAgenetを取得し,全部小文字の文字列にする
    window.userAgentInLowerCase = window.navigator.userAgent.toLowerCase();
    // UserAgentを表示
    document.getElementById("display_user_agent").innerHTML = 
	window.userAgentInLowerCase;

    // 各ブラウザで切り分ける
    if(userAgentInLowerCase.indexOf('iphone') != -1 ||
      userAgentInLowerCase.indexOf('ipad') != -1)
	window.thisBrowser = 'ios';
    if(userAgentInLowerCase.indexOf('android') != -1)
	window.thisBrowser = 'android';
    else if(userAgentInLowerCase.indexOf('chrome') != -1)
	window.thisBrowser = 'pcchrome';
    else if(userAgentInLowerCase.indexOf('safari') != -1)
	window.thisBrowser = 'pcsafari';
    else
	alert('Cannot use this program for your browser');

    window.localVideo = document.getElementById('local_video');
}, false);

window.addEventListener('unload', () => {
    
}, false);

DataURL形式

WebSocketで送るために画像を文字列にする

DataURLとは,画像や音などのメディアを,WebSocketのメッセージとして送ることができるような形式,すなわちテキスト情報にエンコードしたものである.

WebRTCから得られたビデオストリームをそのままDataURLにはできない(単に対応していないという問題だけではなく,縦横サイズを統一するために整えたりする必要がある)ので,videoタグの内容をgetImageData()関数で,クロップ(切り抜き)&縮小しながら取得し,そこで得られたImageDataオブジェクトが持つtoDatURL()関数を実行してDataURL形式に変換して取り出す.

受け取り側で復号する

DataURL形式は文字列なので,サーバ側ではこれまでのWebSocketメッセージと同じように扱うが,それを再度受け取ったクライアント側ではデコードする必要がある.この時のヘッダをsplitする文字列に注意する.

セキュアWebSocket(wss://での接続)とTornadoサーバ側の準備

WebRTCを使う場合,必ずhttps://で接続する必要があると前述したが,その結果WebSocketもセキュアWebSocket(wss://)である必要がある.

セキュア接続に必要な証明書(.crtファイルと.keyファイル)はサーバ毎に用意されているので,そのコピーを持って来て読み込める場所(例えば同じフォルダなど)に設置しておき,Tornadoのサーバ生成を定義する段階,つまりtornado.web.Application()関数を実行してインスタンスをm作る時に,ssl_optionで読み込ませる.


[Work/Class/ES2015で動的Webアプリ/WebSocketを用いた動的Webアプリ]

ES2015でWebSocket - Tornadoでマルチクライアント / 2017-10-31 (火)

Tornadoの「1クライアント:1WSサーバー」→マルチクライアント

listでの管理

前項でサーバ側に使ったTornadoは,前項の説明の通り「1クライアント:1サーバ」になっていて,接続してくるクライアントを個別に管理することができる.

個別管理で「1クライアントのメッセージに対して1つの返答をする」というアプリケーションであればそれで問題ないが,同時に複数のクライアントに対して同じ情報を送ることはできない.

複数のクライアントに同じ情報を送るというのは,つまり「まとめて管理する」ということになる.これを実現するためには,グローバル変数としてlistを持ち,そこにWSのセッションがopenしたらそのlistにselfappendcloseしたらそのlistからselfremoveすることで管理する.(Pythonは関数の中でグローバル変数を扱う場合,参照・代入する前にglobal 変数名宣言が必要)

コード例

クライアント側のhtmlとJavaScriptは前項と同じもので大丈夫.

サーバ側.

つまり各イベントハンドラ関数に引数として流れてくるselfが「クライアントとサーバの1ペア(正確にはクライアントの情報を持ったサーバ1つ)」である.

import tornado.ioloop
import tornado.web
import tornado.websocket
import tornado.template

server_client_list = [] # サーバクライアントのペアを保持するためのlistの宣言

class MyWebSocketHandlerMultiClient_Server(tornado.websocket.WebSocketHandler):
    def open(self):
        global server_client_list
        if self not in server_client_list:
            server_client_list.append(self)

    def on_message(self, message):
        global server_client_list
        for a_server_client in server_client_list:
            a_server_client.write_message("The server return anyone's message: " + message)

    def on_close(self):
        global server_client_list
        if self in server_client_list:
            server_client_list.remove(self)

    def check_origin(self, origin):
        return True

if __name__ == '__main__':
    application = tornado.web.Application( \
        [('/ws_multi_client',  \
          MyWebSocketHandlerMultiClient_Server)])
    application.listen(30005)
    tornado.ioloop.IOLoop.instance().start()

任意のサーバクライアントを取り出すための連想配列での保持

dictでの管理

listの代わりにPythonの連想配列dictを用いることで,文字列と関連づけてサーバとクライアントのペアを保持することもできる.

例えば,接続して一番最初に「チャットID」のような文字列を,生成する,もしくはクライアントから送るように要求して(それはon_messageで受け取る),その文字列を使ってselfと文字列をdictに登録すれば,その「チャットID」から任意のサーバとクライアントのペアを取り出すことができる.

これを使うと「1つのクライアントから,接続している別のクライアントを指定して,そのクライアントだけにメッセージを送る」ということも可能になる.

コード例

prefixというのは,定められた意味を持つ文字列を頭に付与することにより,識別を行う,というもの.接続要求を受けると新たなクライアントID文字列を作り,それをkeyにしてサーバクライアントインスタンスがdictに登録される.接続終了するとそのサーバクライアントインスタンスを削除する.

dictオブジェクト.pop(key)は「そのkeyで登録されているオブジェクトを取り出して削除する」という関数である.(この関数はkeyで登録されたオブジェクトを戻り値として返すが,今回はその戻り値は使っていない)

サーバ側.Python3+Tornadoのコード

import tornado.ioloop
import tornado.web
import tornado.websocket

client_prefix_string = "client"
server_client_dict = {}
client_counter = 0

class MyWebSocketHandlerMultiClient_Server(tornado.websocket.WebSocketHandler):
    def open(self):
        global server_client_dict
        global client_counter
        if self not in server_client_dict:
            key = client_prefix_string + str(client_counter)
            server_client_dict[key] = self
            self.write_message('Your ID is: ' + key);
            client_counter += 1

    def on_message(self, message):
        global server_client_dict
        # '送るclientID/メッセージ内容'という形でメッセージが来るとする
        # messageをsplitして,IDとメッセージ内容に分ける
        client_id = message.split('/')[0]
        message_contents = message.split('/')[1]
        if client_id in server_client_dict:
            server_client_dict.get(client_id).write_message(message_contents)

    def on_close(self):
        global server_client_dict
        for key, server_client in server_client_dict.items():
            if server_client == self:
                server_client_dict.pop(key)
                break

    def check_origin(self, origin):
        return True

if __name__ == '__main__':
    application = tornado.web.Application( \
        [('/ws_multi_client_dict',  \
          MyWebSocketHandlerMultiClient_Server)])
    application.listen(30005)
    tornado.ioloop.IOLoop.instance().start()

クライアント側のhtmlコードとJavaScriptコード

<!DOCTYPE html>
<!-- WSTextMessageMultiClient_dictClient.html -->

<html lang="ja">
<head>
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta charset="utf-8" />

  <title>WebSocketでテキストメッセージ - マルチクライアントID付与</title>
  <script type="text/javascript" src="WSTextMessageMultiClient_dictClient.js">
  </script>
  
</head>
<body>
  <div id="controll_field">
    <p>
      <input type="button" value="接続開始" onclick="startConnection()" />
      <input type="button" value="接続終了" onclick="exitConnection()" />
    </p>
    <p>
      「クライアントID/送りたいメッセージ」という形で入力してください.
    </p>
    <form>
      <input type="text" name="text_field" value="ここにクライアントIDとメッセージを入力し,「送信」ボタンを押してください." />
      <input type="button" name="send_button" value="送信" onclick="sendMessage(this.form.elements['text_field'].value)" />
    </form>
  </div>
  <div id="view_field">
    <p>初期テキスト</p>
  </div>
</body>
</html>
// WSTextMessageMultiClient_dictClient.js
'use strict';
window.addEventListener('load', function(){
    window.webSocket = null;
    window.wsServerUrl = 'ws://sfdn-w1.sd.tmu.ac.jp:30005/ws_multi_client_dict';
}, false);

window.addEventListener('unload', function(event){
    window.exitConnection();
}, false);

window.startConnection = function(event){
    window.webSocket = new WebSocket(wsServerUrl);

    window.webSocket.onopen = function(event){
       window.document.getElementById("view_field").innerHTML =
            '<p>Success to connect WebSocket Server!</p>'
    };

    window.webSocket.onmessage = function(event){
	alert(event.data);
	window.document.getElementById("view_field").innerHTML =
	    '<p>Server: ' + event.data + '</p>'
    };

    window.webSocket.onerror = function(error){
	document.getElementById("view_field").innerHTML = 
	    '<p>WebSocket Error: ' + error + '</p>';
    };

    window.webSocket.onclose = function(event){
	if(event.wasClean){ //切断が完全に完了したかどうかを確認
	    document.getElementById("view_field").innerHTML =
	    '<p>切断完了</p><dl><dt>Close code</dt><dd>' + 
		event.Code + 
		'</dd><dt>Close Reason</dt><dd>' + 
		event.reason +
		'</dd></dl>';
	    webSocket = null;
	}
	else
	    document.getElementById("view_field").innerHTML =
	    '<p>切断未完了</p>';
    };

}

window.exitConnection = function(event){
    if(window.webSocket != null)
	window.webSocket.close(1000, '通常終了'); //onclose関数が呼ばれる
}

window.sendMessage = function(sendingMessage){
    if(window.webSocket != null)
	window.webSocket.send(sendingMessage);
}

[Work/Class/ES2015で動的Webアプリ]

ES2015で動的Webアプリ / 2017-10-31 (火)

この授業の目的

Processingで行ったWebSocket通信を使って,ウェブブラウザ上で動くJavaScriptの2015年版(ECMAScript2015, 通称ES2015, ES6)とProcessingのJavaScript用ライブラリP5.jsを用いて,動的なWebアプリケーションを作るための基礎を学ぶ.

コンテンツ

  1. ES2015の基本 - 変数とそのスコープ,関数定義,Promiseによるコールバック
  2. ES2015の基本 - クラスの定義とインスタンス化
  3. ES2015の基本 - PromiseとXMLHttpRequestを使ったAjaxとfetch
  4. ES2015準拠でP5.jsのインスタンスモード
  5. P5.jsで日本語文字列を表示する
  6. ES2015でWebSocketの基本 - テキストメッセージの受け渡し
  7. ES2015でWebSocket - Tornadoでマルチクライアント
  8. ES2015でWebSocket - ビデオチャットのように端末側カメラの画像をWebSocketで送る

授業の下準備

実行環境としてはGoogle Chromeを使うので,最新版を入れておくこと.また描画部分にはP5.js(ProcessingのJavaScript版)を使うので,Processingの描画の復習をしておくこと.

WebSocketサーバ側は,実験用Webサーバ上で実行を行うため,MSYS2やTerminal.appを使ってUnixの基本操作に慣れておくこと.

WebSocketサーバ用のライブラリとしてはPythonのTornadoを使う.TornadoはAnacondaにパッケージがあるが,http用のローカルサーバを立てるのが面倒くさいので,基本的にはこちらで用意したサーバでサーバ側プログラムを動かす.