at backyard

Color my life with the chaos of trouble.

【JavaScript】setIntervalで気軽にポーリング処理

意外と調べても出てこなかったのでメモがてら書いておく。
(検索力がないだけかも)

利用するシーンは限られるかもしれないが、気軽にポーリング処理を書きたいとき setInterval を使って以下のようなポーリング処理を書くことが可能。

const intervalId = setInterval(() => {
    console.log('polling...');
        ・
        ・
        ・
    if (stopPolling) {
        clearInterval(intervalId);
    }
}, 1000);

ここで定義した intervalId は関数内からでも参照できるので、このように setInterval 内で条件を満たしたらポーリングをやめるように処理が行える。

例えば以下のようなコードを実行してみると、10回だけ繰り返し処理が行われているのが分かる。

let count = 0;

const intervalId = setInterval(() => {
    console.log('polling...');

    count++;

    if (count === 10) {
        clearInterval(intervalId);
        console.log('finish');
    }
}, 500);

以上、JavaScript備忘録でした。

【ブラウザでも使える】JavaScriptで文字列から拡張子のみを取得する方法

ググってもあまりパッと出てこなかったので、メモがてら書き残しておくことにした。
(というか Node.js 使ったやり方ばかりが出てくる)

以下のように書けばファイル名の拡張子を取得できる。

"foo.txt".split(".").pop()
// txt

Node.jsでもブラウザでも使える。

まあNode.jsならそれ用のAPIがあるわけだし(と思ったけど、 path.extname.txt という形で返ってくるか)、あくまでスゴークシンプルなやり方の一つということで

※↓はNode.jsの path.extname のドキュメント https://nodejs.org/api/path.html#pathextnamepath

音声認識モデルWhisperを利用して文字起こしをしてくれるMacアプリ、MacWhisperを試してみる

少し前にwhisper.cppについて書いた

shinshin86.hateblo.jp

今回はWhisperを利用して文字起こししてくれるMacアプリが出たらしいので、そちらを試してみる。

目次

MacWhisperのダウンロード方法

App Storeのリンクがこちら

Whisper Transcription

Whisper Transcription

  • Good Snooze
  • ユーティリティ
  • 無料
apps.apple.com

アプリ自体は無料でインストールできるようだが、課金するとMedium/Largeモデルが使えるようになるらしい。
下記はApp Store内の説明

Pro Unlock Whisper Transcription is free and lets you transcribe audio with the Tiny and Base models. They're fast and very accurate, but for the best results you should consider upgrading to Pro to use the Tiny (English), Medium and Large models, for industry leading transcription quality. Depending on your usecase you might want to use the Large version. You can always upgrade to the Pro version later.

このMacWhisper、おそらくインストール時にModelも一緒にダウンロードしてセットアップしているかと思われるので、その分容量がすごい...

4.54GBという大きなサイズ...まあこれは仕方ないかと思われる

MacWhisperを使ってみる

起動時はこんな感じ。

whisper.cppとは異なり、wavだけでなくMP3などにも対応しているのは嬉しいところかも

ファイルを選択するか、直接マイクの音声を文字起こし出来るらしいので、今回もWhisper.cppのときと同様Beaglesのblue blueの歌詞の文字起こしを試してみたいと思う。

[Beaglesの宣伝タイム]

SpotifyApple Musicを始め各種サブスクで配信されているので聴いてみてください

open.spotify.com

Beaglesのblue blueの歌詞の文字起こしを試してみる

Beaglesの楽曲ファイルを食わせてみて早速文字起こしをしてみる。

文字起こしの処理スピードはめちゃくちゃ早い。
すぐに終わる。

Twitterにも書いたが、文字起こし後に音声を再生すると文字起こし内容が追従してくれる。

また以下の画像を見ていただくと分かるが、テキストのコピーや、指定した部分へのジャンプも実装されていてここらへんのUIは良い感じ

Reader機能

アプリの画面上部にはいくつかの機能がある。

Reader と呼ばれるボタンを押すと、以下のように文字起こし下内容を確認できる

クリップボードへの貼り付け

またその右隣にある Copy ボタンを押すとクリップボードに文字起こしし内容を貼り付けることが出来る。

エクスポート機能

またエクスポート機能も兼ね備えており、以下のように複数のフォーマットでエクスポートが可能。

以下はcsvでエクスポートしたものをnumbersで表示させたもの。

こんな感じでタイムスタンプと文字起こし内容がcsv化されている。

無料版での課題

さすがアプリだけあって、最低限の便利な機能は実装されている。

ただ無料版だとtiny / baseモデルしか利用できないため、文字起こしの精度は微妙かも。
(といってもBeaglesの楽曲でしかまだ試していない。ただ、以前 whisper.cpp の small モデルで同じ楽曲を試したときは今回よりも認識の精度は上なので、やはり tiny / baseモデルだと少々辛いのかもしれない。)

とは言え、UIで色々と操作できるので文字起こし関連の作業をサクッと行いたいならこういうアプリはありだと思った

ffmpeg.wasmを使って、音声ファイルの変換をWebUI上で実施する

ffmpeg.wasmについて

以前whisper.cppに関するポストをしたが、whisper.cppへの入力として必要になる16kHzのwavファイル生成を簡単にできないかと考えていた。

shinshin86.hateblo.jp

16kHzへの変換については上のポストでも書いたように ffmege を用いるが、最近wasm周りに興味があるので ffmpeg.wasm 的なものもあるのでは?と思い調べてみたところ、まさにそれがあった。

github.com

WebのフロントエンドやNode.jsなどのwasmが利用できるところであれば、これを用いることでffmpegを利用することができるようになるらしい。

16kHzのwavファイルへ変換する処理を書いてみた

まさにこれだと思い、早速 ffmpeg.wasm を用いて16kHzへの変換機を作成してみたのがこちら。

github.com

git clone してきて、

yarn install
yarn dev

した後、localhost:3000 にアクセスすればWebUIが表示される。

そして変換したい音声ファイルを選択すれば勝手に変換処理が走り、変換後のファイルがダウンロードされる。

とりあえず実現できるかどうかをまずは見たかったので最低限の処理しか実装していないし、デザインもない。

実装する際には公式exampleの以下を実装した。Webフロントエンドで変換するような処理を実装したい場合は、まずはこちらあたりを参考にすると良さそうかも。

https://github.com/ffmpegwasm/ffmpeg.wasm/blob/master/examples/browser/transcode.html

ffmpeg.wasmでハマるかもしれないポイント

いくつかハマるかもしれないポイントがあるので、こちらにも書いておく。
といっても、そこについてはREADMEにも記載があるので、READMEを読んでおけば大丈夫というレベルではある。

SharedArrayBufferを用いるためHTTPヘッダーの設定が必要

ffmpeg.wasm内の処理で SharedArrayBuffer を用いているため、そのままでは SharedArrayBuffer is not defined 的なエラーが出て、処理に失敗する。

developer.mozilla.org

そこについては公式のREADMEや上のMDNにも書かれているように、HTTPヘッダーを設定する必要がある。

サクッとHTTPヘッダーに以下の設定を行うために、私は superstatic を利用してみた。

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

github.com

こちらは初めて利用してみたが、いわゆるローカルサーバを立ち上げるやつで、設定をいじればサクッとHTTPヘッダーを設定できたので、かなり便利だった。

私は superstatic.json というJSONファイルを作成し、以下の設定を加えた。

{
    "headers": [
        {
            "source": "**/*",
            "headers": [
                {
                    "key": "Cross-Origin-Opener-Policy",
                    "value": "same-origin"
                },
                {
                    "key": "Cross-Origin-Embedder-Policy",
                    "value": "require-corp"
                }
            ]
        },
        {
            "source": "/",
            "headers": [
                {
                    "key": "Cross-Origin-Opener-Policy",
                    "value": "same-origin"
                },
                {
                    "key": "Cross-Origin-Embedder-Policy",
                    "value": "require-corp"
                }
            ]
        }
    ]
}

Webに公開する際のパスについて

私が公開したものはローカルで利用する前提なので特に気にしていないが、Webに公開する際はffmpegを読み込みする際のパスを考慮する必要がある。

実際にここらへんは触れていないので詳細は公式ドキュメントを載せるだけにするが、Webフロントエンド触っている人であれば、ああ読み込むパスのことね...という感じで理解していただけるかと思う。

https://github.com/ffmpegwasm/ffmpeg.wasm/blob/master/docs/api.md#ffmpegload-promise

2GBまでの制限あり

これは WebAssembly の制約だが、現在は2GBまでのファイルしか扱えない。今後4GBになるかも?と記載がある。

他にもいくつか注意事項があるが、READMEに記載があるので、後はそちらを読んで頂くのが良い。
Licenseのことや自分で ffmpeg.wasm をビルドする手順なども載っているので、より詳しく知りたい人はそこらへんも見てみると良いのかも。

結論:ffmpeg.wasm、便利

WATでFizz Buzzにチャレンジ

あけましておめでとうございます。

以前WATを触ってみた際の続きとなる。

だいぶ間も空いてしまったが、書初めと称して今回はWATでFizz Buzzを書いてみようと思う。

shinshin86.hateblo.jp

WATのVScodeプラグイン

ちなみに前回はVimで試していたのだが、今回はVSCode環境で書くことにしてみた。

Vim環境では特にWasm関連のプラグインは入れずに試していたが、VSCode環境では下記の拡張機能をインストールしてみることにした。
(とくに前もって何かを調べたわけではない。単に好奇心というだけ)

marketplace.visualstudio.com

リポジトリは下記。

github.com

というわけで、今回はWATでfizz buzzを書いてみる。

目次

WATで足し算をするだけの関数を作ってみる。

まずはWATで文字列を返すだけの関数を作り、これをスタート地点にしようと思う。

基本的にMDNの下記のドキュメントを参考にしていく。

developer.mozilla.org

WATの関数を書く前に、wasmの実行はスタックマシンとして定義されていくということについて気をつけていきたい。

あまりここらへんのことに明るくはないので、ここから先もしかしたら誤った理解をしている箇所もあるかもしれないが、スタックマシンの基本的な考え方としては すべての命令がスタックから特定の数の値をプッシュし、ポップするようにする必要がある

なんのことやら、と自分でも書きながら思ったので、以下のようなTypeScriptで書いた関数を例にして説明する。
(ここでWATで書いたコードを例にすると、自分自身が混乱しそうに思えたのでTSにしといた)

const add = (n1: number, n2: number): number => {
    return n1 + n2;
}

この関数は数値型の引数を2つとり、2つの引数として渡した数値を合算して返す関数となる。

このようなコードをWasm上で実行する場合、挙動としては以下のようなものになる。

まずは引数の引数として n1 がスタックにつまれる。

次に第2の引数の n2 がスタックにつまれる。

スタックにつまれた2つの数値に対して add(合算処理) が実行される。

合算されて一つになった値が一つスタック上に存在している状態となる。
この値をreturnする。

これらの処理を実際にWATで書くと以下のようになる。

(module
  (func $add (param $n1 i32) (param $n2 i32) (result i32)
    local.get $n1
    local.get $n2
    i32.add
  )
  (export "add" (func $add))
)

$n1, $n2 というのが引数で、 result が返り値を表している。
ここではすべての引数と返り値は i32 という32ビット整数である。
上の図でも書いたように、$n1$n2 がスタック上につまれた後 i32.add が実行され、2つの数値が合算される。
そして一つになったスタック上の数値が返される。
なお、返り値の値と最後に残る(つまり返される予定の値)は必ず一致しなければならない。

例えば上のコードで、 i32.add の行を消した状態でwasmにコンパイルしようとすると、 wat2wasm 配下のようなエラーを吐き出す。

(module
  (func $add (param $n1 i32) (param $n2 i32) (result i32)
    local.get $n1
    local.get $n2
  )
  (export "add" (func $add))
)
$wat2wasm add.wat -o add.wasm
add.wat:4:5: error: type mismatch at end of function, expected [] but got [i32]
    local.get $n2
    ^^^^^^^^^

これは関数が終了した状態でのスタックマシンの状態と返り値が一致していないことによるエラーとなる。

例えば i32.addの行を消した状態で正常にコンパイルを完了させるには下記のようにする必要がある。

(module
  (func $add (param $n1 i32) (param $n2 i32) (result i32) (result i32)
    local.get $n1
    local.get $n2
  )
  (export "add" (func $add))
)

これは関数を実行した状態でスタックマシンに2つの i32 の値がつまれた状態となるが、返り値として i32 の返り値が2つであるため、正常にコンパイルされ、実行できる。

ちなみに (export "add" (func $add)) という行は add という関数名で export するということを意味している。

ではこのコードを add.wat という名称で保存して下記のようにコンパイルする。

wat2wasm add.wat -o add.wasm

そして以下のようなHTMLを書き、ローカルサーバを立ち上げて実行するとwasmの実行結果を確認できる。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>WAT sample</title>
  </head>
  <body>
    <script>
      WebAssembly.instantiateStreaming(fetch("add.wasm"))
      .then(obj => {
        const result = obj.instance.exports.add(10,2);
        console.log("result: ", result); // `result: 12` と表示される
      });
    </script>
  </body>
</html>

これでWAT(WASM)がスタックマシンであるという説明は以上となる。

WATで文字列を返す関数を作る

WASMは以下の4つの数値型が用意されている。

またこれとは別に参照型というものがあり、こちらは externref で定義できる。

externref という参照型について

これを使えば一応は文字列を利用できる。
(厳密には文字列型というわけではなく、ただ参照しているだけに過ぎない)

(module
  ;; 返り値を2つ返す場合、(result externref) (result externref)ではなく、下記のようにも書ける
  (func $hello (param $str1 externref) (param $str2 externref) (result externref externref)
    local.get $str1
    local.get $str2
  )
  (export "hello" (func $hello))
)

で、下記のようなコードをJS側から実行する。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>WAT string</title>
  </head>
  <body>
    <script>
      WebAssembly.instantiateStreaming(fetch("hello.wasm"))
      .then(obj => {
        const result = obj.instance.exports.hello("hello", "world");
        console.log(result);
      });
    </script>
  </body>
</html>

結果は ['hello', 'world'] という2つの文字列が格納された配列になる。

できればここで文字列を hello worldという形で連結して表示させたい。

JavaScriptの関数をインポートして使う

WasmではJSの関数をimportして利用することができる。

WATで用意されている機能では文字列連結は難しそうだったので(そもそも文字列型がない)、JavaScriptの関数側から文字列連結をして見るのはどうかと思い立った。

ここでは試しに配列内の文字列を連結して返すだけの関数をJS側で用意し、そちらをWATにimportして文字列連結を実現させるというアプローチを取ることにする。
※ちなみに今更ながらなぜ hello という関数名にしたのかという感じだが、あくまでサンプルということでここはスルーしてほしい...。

watのコードを以下に記載する。
externrefの変数を2つスタックさせた状態で、importした string.join関数を呼ぶというものになっている。
この後に記載するが join関数は2つの参照型の値を連結して返すだけの関数で、externref の値を一つ返す流れとなる。

(module
  (import "string" "join" (func $join (param externref externref) (result externref)))
  (func $hello (param $str1 externref) (param $str2 externref) (result externref)
    local.get $str1
    local.get $str2
    call $join
  )
  (export "hello" (func $hello))

ちなみにWebAssemblyでは2 階層の名前空間のインポート文を持っているようで、今回は string.join という形にしている。
これが1階層だとコンパイル時にエラーになる。

下記は仮に join という1階層のみで定義しようとした場合のエラー。

hello.wat:2:18: error: unexpected token "(", expected a quoted string (e.g. "foo").
  (import "join" (func $join (param externref externref) (result externref)))
                 ^

そしてJS側は以下のようになる。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>WAT string join sample</title>
  </head>
  <body>
    <script>
      const importObject = {
        string: {
          join: function (str1, str2) {
            return str1 + str2;
          }
        }
      };

      WebAssembly.instantiateStreaming(fetch("hello.wasm"), importObject)
      .then(obj => {
        const result = obj.instance.exports.hello("hello", "world");
        console.log(result);
      });
    </script>
  </body>
</html>

これで実行すると、helloworld がコンソール上に表示させる。

WATでif文を使う

MDNの下記のドキュメントが参考になりそうだ

developer.mozilla.org

以下のようにかんたんなサンプルを作ってみる。

;; if.wat
(module
  (import "console" "log" (func $log(param i32)))
  (func (export "ifsample") (param $n1 i32) (result i32)
    (local $result i32)
    (if (i32.eq (i32.const 1)(local.get $n1))
      (then
        (local.set $result (i32.const 100))
      )
      (else
        (local.set $result (i32.const 0))
      )
    )
    
    local.get $result
    return
  )
)

以下のコードが if のブロックとなる。

  (if (i32.eq (i32.const 1)(local.get $n1))
      (then
        (local.set $result (i32.const 100))
      )
      (else
        (local.set $result (i32.const 0))
      )
    )

私はまったくもって詳しくないので間違ったことを書いているかもしれないが、Lispなどで採用されているS式をwatでも採用されており、例えば2つの数字が同じかどうかを判定する箇所も (i32.eq (i32.const 1)(local.get $n1)) といった形で囲われている。

ja.wikipedia.org

今回のコードでは if の前に (local $result i32) でlocal変数を定義してこちらに一度if内での結果を格納している。
また if 内のブロックにおける処理もブロックとして定義する必要があり、

      (then
        (local.set $result (i32.const 100))
      )

という形で (local.set~ 内も囲う必要がある。

最初ここのカッコが抜けていて意図が不明なエラーに悩まされた。

そして、その結果の内容を return する前に local.get $result で一度スタックに積んで、そのスタックに積んだ値を return するという処理の流れになっている。

こちらを以下のようにコンパイルして、

wat2wasm if.wat -o if.wasm

以下のHTMLコードから呼び出せば意図したコードが実行されているのが確認できる。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>WAT if sample</title>
  </head>
  <body>
    <script>
      WebAssembly.instantiateStreaming(fetch("if.wasm"), {console})
      .then(obj => {
        const result = obj.instance.exports.ifsample(1);
        console.log(result);
      });
    </script>
  </body>
</html>

WATでループする

下記のドキュメントが参考になった。

developer.mozilla.org

loop 文でループは可能なようだが、どうやら loop それ自体を書いただけでは意図するループはかけないようだ。

以下は 1~100 までを出力するだけのサンプル。
(内容としては上のMDNないのサンプルとそこまで変わらないものとなった)

(module
  (import "console" "log" (func $log(param i32)))
  (func (export "loopsample")
    ;; 変数として$iを定義する。初期値は0
    (local $i i32)

    (loop $my_loop

      ;; $iに対して1を追加
      local.get $i
      i32.const 1
      i32.add
      local.set $i

      ;; 現在の値をconsole.logに出力する
      local.get $i
      call $log

      ;; もし$iの値が100以下であれば、再びループする
      local.get $i
      i32.const 100
      i32.lt_s
      br_if $my_loop
    )
  )

以下のHTMLをローカルサーバ上で読み出すことでログに1〜100までの値が出力されていることが確認できる。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>WAT loop</title>
  </head>
  <body>
    <script>
      WebAssembly.instantiateStreaming(fetch("loop.wasm"), {console})
      .then(obj => {
        obj.instance.exports.loopsample();
      });
    </script>
  </body>
</html>

WATでFizz Buzz

ここまでで、WATで

  • 関数の作り方
  • 文字列の扱い方
  • if文
  • loop文

の概要を少しだけ理解できた。

ここまでくれば FizzBuzz できるだろう、ということでやってみる。

WATで余りを割り出す

JavaScriptであれば % を用いて余りを計算していくと思うが、watでも同様のことはできる。

developer.mozilla.org

こちらを用いて、以下のような条件分岐を実装していく。

  ;; 15で割った数が0の場合、ifブロック内の処理が実行される
  (if (i32.eq (i32.rem_u (local.get $n)(i32.const 15))(i32.const 0))
      (then
        (local.get $fizzbuzz)
        (call $logStr)
        (local.set $fbFlag (i32.const 1))
      )
    )

WATで書いたFizz Buzzのコード

というわけで、以下がコード。
(もっと良い書き方がありそうだが...疲れたので一旦これで)

(module
  ;; それぞれの型に沿った出力を行いため
  (import "console" "log" (func $logStr(param externref)))
  (import "console" "log" (func $logNum(param i32)))
  (func $display (param $n i32) (param $fizz externref) (param $buzz externref) (param $fizzbuzz externref)
    ;; fizz, buzz, fizzbuzz、いずれにも該当しない場合はフラグを用いて数値を出力するように処理を分岐させる
    (local $fbFlag i32)

    (if (i32.eq (i32.rem_u (local.get $n)(i32.const 15))(i32.const 0))
      (then
        (local.get $fizzbuzz)
        (call $logStr)
        (local.set $fbFlag (i32.const 1))
      )
    )

    (if (i32.eq (i32.rem_u (local.get $n)(i32.const 5))(i32.const 0))
      (then
        (local.get $buzz)
        (call $logStr)
        (local.set $fbFlag (i32.const 1))
      )
    )

    (if (i32.eq (i32.rem_u (local.get $n)(i32.const 3))(i32.const 0))
      (then
        (local.get $fizz)
        (call $logStr)
        (local.set $fbFlag (i32.const 1))
      )
    )

    (local.get $fbFlag)
    (if
      (then)
      (else
        (local.get $n)
        (call $logNum)
      )
    )
  )

  (func $fizzbuzz (param $fizz externref) (param $buzz externref) (param $fizzbuzz externref)
    ;; 変数として$iを定義する。初期値は0
    (local $i i32)

    (loop $my_loop

      ;; $iに対して1を追加
      local.get $i
      i32.const 1
      i32.add
      local.set $i

      ;; display関数に渡すパラメータをスタックに載せてから関数を呼び出す
      local.get $i
      local.get $fizz
      local.get $buzz
      local.get $fizzbuzz
      call $display

      ;; もし$iの値が100以下であれば、再びループする
      local.get $i
      i32.const 100
      i32.lt_s
      br_if $my_loop
    )
  )
  (export "fizzbuzz" (func $fizzbuzz))
)

これを下記のようにコンパイルする。

wat2wasm fizzbuzz.wat -o fizzbuzz.wasm

そしてローカルサーバを起動して以下のHTMLにアクセスする。

<!doctype html>
<html>

<head>
  <meta charset="utf-8">
  <title>WAT fizzbuzz</title>
</head>

<body>
  <script>
    WebAssembly.instantiateStreaming(fetch("fizzbuzz.wasm"), { console })
      .then(obj => {
        obj.instance.exports.fizzbuzz("Fizz", "Buzz", "Fizz Buzz");
      });
  </script>
</body>

</html>

これでコンソールを開くと以下のようなコードが出力されている。

Chromeのコンソール上で確認している

というわけで無事にWATでFizz Buzzできた。

これを今年の書き初めとする。

実際に動くコードはGitHubにも載せました。

github.com

参考にしたドキュメント

developer.mozilla.org

あと最近、オライリーから出ているハンズオンWebAssemblyも読んでいるが、こちらも良さそう。

www.oreilly.co.jp

プログラミング言語は主にC++を取り扱います と書籍の説明欄には書かれているが、C++に特化した内容というよりかは WebAssembly を体系だって理解するような方向性なので、WebAssemblyには興味あるけど別にC++は使わないという人でも楽しく読める。

2022年も今年で終わり

Googleも大晦日仕様

気がつけばあっという間に大晦日である。

2022年の振り返りでも書こうと思い、少しばかりキーボードを叩いていたが、ただやったことを羅列しているだけの気分になりテンションが全く上がらなくなったので辞めた。

育児日記

代わりに子供の成長の記録でもこちらには書いておこうと思う。

もともと子供はどちらかというと意志がかなり強い性格だったが、最近はよりその傾向が顕著に現れ、私に対して反発する機会も増えてきた。

その反発のバリエーションというか、反発するに至る思考を見て以前よりも成長したなと覚えることがある。

今までは「やだ!」なのが、「〇〇だからやだ!」みたいな感じになってきたとでも言えばいいのだろうか、私に対して嫌なことがあり、それを明確に伝えた上で「やだ!」というような発言をするようになり、以前はこういう言い方はしなかったなと思いそういうところにも成長を感じるこの頃である。

この前家電量販店に行った際に、子供が喜々として店頭にディスプレイされているデモ用のChrome bookをいじりだして、キーボードとスワイプを駆使して様々なウィンドウを表示させて喜んでいたが、私としてはPC、強いて言えばプログラミング周りに興味を持ってくれたら嬉しいなと時々思う。

私がプログラミングをするようになって強烈に感じたことに一つにオープンソースの楽しさがある。

驚くほどにクールなプロジェクトが世界にはゴロゴロと転がっていて、それらがコードもソフトウェアに関する議論の内容も公開されているというのは結構衝撃的だった。

オープンソースの海を泳いでいるだけで余裕で一日潰せてしまうし、コードを持ってきてビルドして実際に動いた瞬間の脳汁はやばい。

そんな楽しい世界を子供にも知ってもらえたら、という気持ちが時々生じる。まあ、子供がそういうのにハマるかどうかはわからないし、私もそこまで推すつもりはないが、いつか私がオープンソースとして作ったプロジェクトに子供がPRを送ってくれる所とかを想像すると、それなりにグッと来るなと思った次第。

そんな妄想の中を泳いだような文章で今年は締めくくろうと思う。

来年もよろしくおねがいします。

【動画編集効率化】Final Cut Proを最低限使いこなすための備忘録

私は普段Final Cut Proを用いて動画作成を実施しているが、もうすこし作業効率を上げたいと思い、最近少し調べていた。
今回実際に調べてみて、自身のFinal Cut Pro環境に適用した内容をまとめたのがこのポストとなる。
(この後に書くが「適用した内容をまとめた」といっても以下に記載した動画の内容を参考にしたに過ぎない。最初は色々と取り入れるよりも誰かのマネをするにとどめておいたほうが、焦点が定まって良いと思ったからだ)

主に自分用のまとめとなるが、Final Cut Proで動画作成を行っている人で、より効率的に作業したいと考えている人には有益なものになるかもしれない。

目次

参考にした動画

なお、今回の内容をまとめるにあたって参考にさせていただいた動画は以下となる。 今回書く内容について、より詳細な内容・解説を知りたいと思った方はぜひそちらも参照してみてほしい。
(参考にするにあたって様々な情報を仕入れるよりは、まずは誰かのTIPSだけを参考にして、そこから広げていくほうがより効率的に作業効率化を図れるのではないか?と思い、まずは下記の動画のみを参考にさせていただくことにした)

www.youtube.com

というわけで、Final Cut Pro環境に適用した内容をいかにまとめる。

ショートカット

Final Cut Proのショートカットを見直すことにした。
参考にしたYouTube動画内の設定をほぼほぼ、そのまま反映させている。
(私はズームインとズームアウトを逆にした)

機能 対応するキーバインド
1 ズームアウト
2 ズームイン
Q トリム開始
W ブレード
E トリム終了

以下のようにショートカット内容をドラッグ・アンド・ドロップでキーに持っていくとそれだけで設定できるようになるらしい

このショートカットキーだが、かなり良い。
何よりキーボードを一つ押すだけでサクサクと作業を進められる感じが、かなり作業効率化アップが図れている印象がある。
(ショートカットは Command + なにか とか option + なにか のほうが良いかもしれないと最初は考えていたが、一つのキーボードを押すだけで処理を行えるのはかなり快適だということに気づいた。動画編集はエディターでコードを書くのとは違い、常に全ての英文字が打てる状況でないといけないわけではないので、こういう一文字だけのショートカットというのはかなり良いと思った)

ちなみに以下のようにマウスでも設定はしていたが、キーボードショートカットで設定して手をあまりキーボードから離さずに作業できたほうが良いかと思い、設定した。

shinshin86.hateblo.jp

テロップはプラグインを購入するのが手っ取り早い

テロップなどの文字入れは、作成した動画のスタイルにもよるが、プラグインを購入してしまうのが手っ取り早いことを知った。
たしかにいろいろな動画を見ていて、このテロップのアニメーション作るの大変そうだな、、と思うものを見かけるが全員が全員自作しているわけではなく、プラグインを利用しているということを知った。
餅は餅屋ということか。

たしかにアプリケーション開発などをしていると様々なライブラリを組み合わせて機能を作り上げる場面も多々あるし、そういうのと同じようなものなのだろう。

というわけで、motion VFXというところで以下のプラグインを購入した。

www.motionvfx.com

www.motionvfx.com

www.motionvfx.com

テロップ作業などは更にPasteを導入して効率化を図る

テロップ作業についてはまだ作業の効率化が図れる。

動画作成において、ある程度共通して利用するようなものはクリップボードアプリにコピーしてその中から使いまわしていくのが良いようだ。

おすすめされていたアプリが以下。

Paste - Clipboard Manager

Paste - Clipboard Manager

  • Paste Team
  • 仕事効率化
  • 無料
apps.apple.com

これは有料アプリだが、14日間の無料体験ができる。
無料体験を開始してまだ数時間といったところだが、サブスク購入しようとすぐに決めた。

このアプリを入れて初期設定を済ますと(と言ってもすぐに終わる。)、Command + C でコピーするとPaste内にどんどんコピー履歴が溜まっていく。

そしてデフォルトだと Shift + Command + V でPasteを表示して今までコピーした履歴が確認できる。
その履歴を選択、または数字キーなど対応するショートカットキーで入力することで、即座にペーストできる。
そしてこのPasteにはFinal Cut Pro上のテロップなどもコピーして残しておける。

そのためよく利用するテロップなどはこちらに残しておけば、Shift + Command + V ですぐに使いたいテロップを貼り付けて作業を進められる。

これは便利だ。なお、Pasteではフォルダ管理もできるようで、上のキャプチャにもあるが、私はFinal Cut Pro用のフォルダを作成し、そちらにFinal Cut Pro用のコピー履歴は格納して利用していくつもり。

フォルダへの格納はドラッグ・アンド・ドロップで移動できるようで直感的な点も良い。

またフォルダ間の移動は Command + @ で左、 Command + [ で右、といった形で移動できるので、キーボード内で全て完結できるようになっている。

Pasteでは1Passwordなどのデータもコピーされないのか?

こういうコピペ系アプリを利用する際にパスワードなども履歴として保存されないかが心配になる方もいるかと思うが(私もその一人)、Pasteでは環境設定から以下のように設定できるので、1Passwordなどのパスワード管理アプリを利用されている方は設定しておくのをお忘れなきよう。

なお、私の場合は基本的にFinal Cut Proの作業効率化のために導入した経緯から、他のアプリ、例えばコードを書くときのエディターなども除外している。

Pasteが動いている限り、除外されていないアプリ上でのコピー操作はすべてこちらにも溜まっていくので、そういうのが煩わしい方は除外設定をしておいたほうが良いだろう。
(除外、ではなく、このアプリだけ許可する、みたいな設定もできれば嬉しいのだけど)

音量の調節について

上の動画では説明されていないが動画編集をしていて、とある箇所の音量だけ調節したいというケースはよくある。
そのような場合は Option + クリック を音の波形エリアに対して行うことで、起点?ポイント?(正式な呼び方がわからない)を作成することができる。

これを複数作成することでその部分だけ音量を上げるような事が可能。
(テキストだと説明しにくいが、まあ、実際にやってみればここらへんは分かるかと思う)

こんな感じで一部分の音量のみ上げることが出来る

人物などへのモザイクの自動追尾について

自動追尾に便利なセンサーエフェクト

こちらも上の動画では説明されていない事柄になるが、Final Cut Proではエフェクトのセンサーを用いることで人物など特定のオブジェクトへのモザイクを自動追尾させることが出来る。

このモザイクの自動追尾について私はまだ色々と理解できているわけではないが、少しばかり触ってみて色々と突き詰めてみると時間がかなり溶けそうだということが分かったので、ひとまず簡単に人物へのモザイク自動追尾を行う方法についてだけ書き残しておく。

モザイクの自動追尾を簡単に行う方法

  1. 追尾させたいオブジェクト(人物など)を表示させた状態で、エフェクトからセンサーを呼び出す(検索欄にセンサーと入れる)
  2. センサーエフェクトを自動追尾させたいオブジェクトに直接ドラッグ・アンド・ドロップさせる
  3. ちなみにこのドラッグアンドドロップの際に、すでに対象を認識するような表示になる
  4. あとはプレビュー左上の解析ボタンを押す
  5. これで解析が完了するので、あとはモザイクが自動追尾することを確認する
  6. 自動追尾しているモザイクの範囲的な問題でうまく隠せない場合は AmountRadius の値をいじっていくと良い感じに隠れる

このやり方でだいたいの自動追尾は可能だが、対象のオブジェクトが画面端に切れる瞬間などに認識がなくなる時がある。
(ちなみにこの場合でもトラッカー自体は認識しているようだが、モザイクが切れてしまう。ここは解決するための何かしらの方法がありそうではある)

こういう場合はモザイクが切れる瞬間をそのまま切ってしまってうまく編集したりなどで現在は対応している。
これがなんだかんだで一番時間をかけずに済む方法だったりする。 あとはモザイクが切れる瞬間のみ、別途ピクセル化のモザイクでうまく隠していくのも一つの解決策となる。

まあ、ここらへんは試行錯誤の段階なので、今のところは上記のようなやり方が自身の最適解となっている。

まとめ

これだけの設定で普段の動画制作の作業時間がだいぶ削れた。

参考にしていた動画内でも言及されていたように、動画編集で最も時間のかかる作業の中に 動画のカットテロップの挿入 がある。
今回の環境カスタマイズではこれらの作業効率化に焦点があたっており、実際これは良い結果をもたらしている。

シンプルでやることは少ないものの、効果はかなり大きいので、来年はこれを基本としてどんどん作業効率化を図っていきたいところだ。