Ubuntu20.04にAndroidのNDK,SDKをコマンドラインでインストール





Ubuntu20.04にAndroidのNDK,SDKをコマンドラインでインストール

1 概要

  • Android Studioが重いので、コマンドラインでインストールしてみたかった
  • やねうら王や、他のツールをUbuntu上でAndroid用をクロスコンパイルしてみたかった。

2 リンク

3 インストール

3.1 Command line tools onlyのインストール

  • 以下の手順でインストールすると5G弱ディスクスペースを消費した

3.2 以下の操作を行っている動画


3.2.1 Command line tools onlyのインストールの手順

  1. ベースツールのダウンロード
    • Android Studio関係ダウンロードページhttps://developer.android.com/studio?hl=ja の「Command line tools only」のところからLinux用のcommandlinetools-linux-6858069_latest.zipをダウンロード
  2. ダウンロードしたファイルを解凍
    • ここではunarコマンドで行っていますが、他の解凍ツールでもOK
    • 解凍場所はどこでもOK、このファイルは後で使わない
    unar commandlinetools-linux-6858069_latest.zip
    
    • 解凍した実行ファイルのディレクトリに移動
    cd cmdline-tools/bin/
    
  3. インストールするディレクトリを作成
    • ここではホームディレクトリのAndroid/SDKにインストールします
    mkdir -p ${HOME}/Android/SDK
    
  4. sdkmanager helpの確認方法
    ./sdkmanager --sdk_root=${HOME}/Android/SDK/ --help
    
  5. 解凍したコマンドラインツールで、必要パッケージをインストール
    • パッケージリストを確認
    • これでインストールするバージョン選ぶ。以下の私の手順と同じバージョンでなくてもOK
    ./sdkmanager --sdk_root=${HOME}/Android/SDK/ --list
    
    • コマンドラインツールをインストール
    ./sdkmanager --sdk_root=${HOME}/Android/SDK/ --install "cmdline-tools;latest"
    
    • プラットフォームツールと、プラットフォームをインストール。ここでは最新の30の物をインストールした
    ./sdkmanager --sdk_root=${HOME}/Android/SDK/ "platform-tools" "platforms;android-30"
    
    • ndkの最新をインストール
    ./sdkmanager --sdk_root=${HOME}/Android/SDK/ "ndk-bundle"
    
    • build-toolsをインストール
    ./sdkmanager --sdk_root=${HOME}/Android/SDK/ "build-tools;30.0.3"
    
    • 他にもインストールした方が良いものとしては、エミュレーターとかもある。Androidのpkgを作成するには、他にも追加のものをsdkmanagerでインストールする必要あると思う。その場合は上の感じで追加インストールすればOK。
  6. 環境変数のセット
    • SDKを使う場合は、以下の環境変数を設定
    • 毎回設定が面倒なら、.bashrcに以下の2行を追加。追加したばかりだと、これらは設定されてないので、使う前にこれらをシェルで実行
    • set.shというファイル名に入れておいて、source set.sh 等でこれらを実行してもOK
    export PATH=${HOME}/Android/SDK/cmdline-tools/latest/bin:${HOME}/Android/SDK/ndk-bundle/:$PATH
    export ANDROID_SDK_ROOT=${HOME}/Android/SDK/
    
  7. インストールしたパッケージの確認
    sdkmanager --sdk_root=${HOME}/Android/SDK/ --list_installed
    
    • 現在の状態出力
    Installed packages:=====================] 100% Fetch remote repository...       
      Path                 | Version      | Description                             | Location             
      -------              | -------      | -------                                 | -------              
      build-tools;30.0.3   | 30.0.3       | Android SDK Build-Tools 30.0.3          | build-tools/30.0.3/  
      cmdline-tools;latest | 3.0          | Android SDK Command-line Tools (latest) | cmdline-tools/latest/
      emulator             | 30.3.5       | Android Emulator                        | emulator/            
      ndk-bundle           | 22.0.7026061 | NDK                                     | ndk-bundle/          
      patcher;v4           | 1            | SDK Patch Applier v4                    | patcher/v4/          
      platform-tools       | 30.0.5       | Android SDK Platform-Tools              | platform-tools/      
      platforms;android-30 | 3            | Android SDK Platform 30                 | platforms/android-30/
    
  8. updateのやり方
    sdkmanager --sdk_root=${HOME}/Android/SDK/ --update
    

3.3 この章のまとめ

  • コマンドラインツールで、Android SDK,NDKをインストールしてみた。

4 今後

  • 今後も文書追加していきます。

5 この文書のチェンジログ

  • 2021/01/19 初版

著者: NM Max

Created: 2021-01-20 水 08:34

Validate

ANTLR4事始め





ANTLR4ことはじめ

目次

1 概要

  • 機能豊富なパーサジェネレータ
  • インストール
  • 簡易電卓作成(普通のプログラム言語のHello Worldにあたるもの)
  • 簡易プログラム言語作成(計算機能、変数利用と、print命令のみ)

2 この文書作ろうと思ったきっかけ

  • 構文解析をPythonでやろうと色々調べたところ、どうやら、ANTLR4が抜けてるというのをみつけて、これやってみよとなりました。

5 Ubuntu20.04でANTLR4をインストール

  • 公式サイトの文書のやつをやってみる

5.1 以下の操作を行っている動画


5.2 Ubuntu20.04でANTLR4をインストール する手順

sudo apt install antlr4
  • 環境変数をセットしないと、上手く動作しなかった(必要なものが見つからないってエラー)
  • 以下を実行してから実行するのが良い
  • ホームディレクトリの.bashrcとかに追記しておくと、いちいち実行しなくても良くなる
export CLASSPATH=.:/usr/share/java/antlr4.jar:/usr/share/java/antlr4-runtime.jar:/usr/share/java:${CLASSPATH}

5.3 動作確認

5.3.1 ソースファイルHello.g4を用意

5.3.2 ソースファイルHello.g4から生成

antlr4 Hello.g4
  • これで、必要なファイルが生成される
HelloLexer.interp
HelloLexer.java
HelloLexer.tokens
Hello.interp
HelloParser.java
HelloListener.java
HelloBaseListener.java
Hello.tokens

5.3.3 生成したjavaファイルをコンパイル

javac Hello*.java

5.3.4 生成したものを動作確認

/usr/share/antlr4/grun Hello r -tree
or
/usr/share/antlr4/grun Hello r -gui

5.4 この章のまとめ

  • antlr4をUbuntu20.04にインストール
  • 公式サイトのGetting StartedのHello.g4を利用してコンパイル実行してみた

6 簡易電卓の作成

6.1 自然数を解釈する

6.1.1 以下の操作を行っている動画


6.1.2 自然数を解釈する 手順

  1. 0だけを解釈するものを作る
    • 以下の内容でCalc.g4というファイルを作成
    • Parser rule(パーサールール)名は小文字で開始
    • Token(識別子)名は大文字で開始
    grammar Calc;
    expr  : N;
    N     : '0';
    WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
    
    • コンパイル
    export CLASSPATH=.:/usr/share/java/antlr4.jar:/usr/share/java/antlr4-runtime.jar:/usr/share/java:${CLASSPATH}
    antlr4 Calc.g4
    javac Calc*java
    
    • 以下で確認する(色々入力して、最後にCtrl-Dを押す。WindowsならCtrl-Zかも)
    /usr/share/antlr4/grun Calc expr -gui
    
  2. 0と1を解釈するものを作る
    • 以下の内容にCalc.g4を修正
    • 亀の子括弧[]は中身のどれかに該当する一文字にマッチする正規表現
      • 例1 [01]は0か1にマッチ
      • 例2 [012]は0か1か2にマッチ
      • 例3 [abc]はaかbかcにマッチ
    • 正規表現は言語やツールによって多少方言あり
    grammar Calc;
    expr  : N;
    N     : [01];
    WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
    
    • コンパイル
    antlr4 Calc.g4
    javac Calc*java
    
    • 以下で確認する(色々入力して、最後にCtrl-Dを押す。WindowsならCtrl-Zかも)
    /usr/share/antlr4/grun Calc expr -gui
    
  3. 0から9までを解釈するものを作る
    • 以下の内容にCalc.g4を修正
    grammar Calc;
    expr  : N;
    N     : [0123456789];
    WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
    
    • コンパイル
    antlr4 Calc.g4
    javac Calc*java
    
    • 以下で確認する(色々入力して、最後にCtrl-Dを押す。WindowsならCtrl-Zかも)
    /usr/share/antlr4/grun Calc expr -gui
    
  4. 0から9までを解釈するものを作る(その2)
    • 以下の内容にCalc.g4を修正
    • 亀の子括弧[]は中身のどれかに該当する一文字にマッチする正規表現
      • 例1 [0-5]は0か1か2か3か4か5にマッチ
        • (文字もコンピューター内部では数字で処理されていて、この数字ならこの文字の事ということで処理されている。通常半角英数の場合,アスキーコードという文字と数字の対応の規約が使われていて、それだと0から9まで1つずつ増える数字で扱っている)
      • 例2 [0-2]は0か1か2にマッチ
      • 例3 [a-z]はaからzまでの26文字の1文字にマッチ
      • 今回の[0-9]は0,1,2,3,4,5,6,7,8,9の1文字にマッチする正規表現
    grammar Calc;
    expr  : N;
    N     : [0-9];
    WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
    
    • コンパイル
    antlr4 Calc.g4
    javac Calc*java
    
    • 以下で確認する(色々入力して、最後にCtrl-Dを押す。WindowsならCtrl-Zかも)
    /usr/share/antlr4/grun Calc expr -gui
    
  5. 二桁以上の数字に対応(その1)
    • 以下の内容にCalc.g4を修正
    • +は正規表現で、直前のものを1度以上繰り返ししているものにマッチ
      • 例えば 1+なら、1が1文字以上にマッチ、1,11,111,1111,….にマッチする
      • 今回の[0-9]+だと、0から9までの文字が1文字以上繰り返されるものにマッチ
    • *は正規表現で、直前のものを0回以上繰り返ししているものにマッチ
      • 例1 0* なら 文字無し、0,00,000,0000,0000,….にマッチ
      • 例2 [0-9]* なら 数字が0回以上繰り返されるものにマッチ、 文字なし、0,12,359,…等など
    grammar Calc;
    expr  : N;
    N     : [0-9]+;
    WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
    
    • コンパイル
    antlr4 Calc.g4
    javac Calc*java
    
    • 下で確認する(色々入力して、最後にCtrl-Dを押す。WindowsならCtrl-Zかも)
    /usr/share/antlr4/grun Calc expr -gui
    
  6. 二桁以上の数字に対応(その2)
    • 以下の内容にCalc.g4を修正
    grammar Calc;
    expr  : N;
    N     : [0-9]
          | [1-9][0-9]+;
    WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
    
    • [0-9] | [1-9][0-9]+ : 0から9のひと桁の数字あるいは、1から9ではじまる2桁以上の数字にマッチする表現
    • コンパイル
    antlr4 Calc.g4
    javac Calc*java
    
    • 下で確認する(色々入力して、最後にCtrl-Dを押す。WindowsならCtrl-Zかも)
    /usr/share/antlr4/grun Calc expr -gui
    

6.1.3 この節のまとめ

  • 自然数を解釈するパーサーの作成

6.2 足し算表現、引き算表現の解釈処理追加

6.2.1 以下の操作を行っている動画


6.2.2 足し算表現、引き算表現の解釈処理追加 手順

  1. まず足し算表現を追加
    • 前の章で作成したCalc.g4を以下に修正
    • 注意書き、正規表現マッチの多くの場合、特別な意味を持つ文字(+,*,.,?等)はバックスラッシュでエスケープすると、特殊な意味ではなく、その文字自体を意味するものとして扱うことが多いが、ANTLERの文法記述ではこれではなく、半角シングルクオートでかこむようです。
      • 例えば+文字自体を扱いたい時
        • ‘+’と記述
    • 他の主要な正規表現で使う文字
      • .(半角ピリオド文字) : 任意の1文字にマッチ
    • “no viable alternative at input ‘<EOF>'” を防ぐ為に、parseルールを追加
    grammar Calc;
    parse : expr EOF;
    expr  : expr '+' expr
          | N;
    N     : [0-9]
          | [1-9][0-9]+;
    WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
    
    • コンパイル
    export CLASSPATH=.:/usr/share/java/antlr4.jar:/usr/share/java/antlr4-runtime.jar:/usr/share/java:${CLASSPATH}
    antlr4 Calc.g4
    javac Calc*java
    
    • 以下で確認する(色々入力して、最後にCtrl-Dを押す。WindowsならCtrl-Zかも)
    /usr/share/antlr4/grun Calc parse -gui
    
  2. 引き算表現も追加
    • 以下の内容にCalc.g4を修正
    grammar Calc;
    parse : expr EOF;
    expr  : expr  '+' expr
          | expr '-' expr
          | N;
    N     : [0-9]
          | [1-9][0-9]+;
    WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
    
    • コンパイル
    antlr4 Calc.g4
    javac Calc*java
    
    • 以下で確認する(色々入力して、最後にCtrl-Dを押す。WindowsならCtrl-Zかも)
    /usr/share/antlr4/grun Calc parse -gui
    
  3. 数字の前に’-‘がある場合の処理追加
    • 以下の内容にCalc.g4を修正
    grammar Calc;
    parse : expr EOF;
    expr  : expr  '+' expr
          | expr '-' expr
          | N
          | '-' N;
    N     : [0-9]
          | [1-9][0-9]+;
    WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
    
    • コンパイル
    antlr4 Calc.g4
    javac Calc*java
    
    • 以下で確認する(色々入力して、最後にCtrl-Dを押す。WindowsならCtrl-Zかも)
    /usr/share/antlr4/grun Calc parse -gui
    

6.2.3 この節のまとめ

  • 足し算引き算表現も解釈出来るように修正
  • 数字の前のマイナス記号も解釈出来るように修正

6.3 掛け算表現、割り算表現の解釈処理追加

6.3.1 以下の操作を行っている動画


6.3.2 掛け算表現、割り算表現の解釈処理追加 手順

  • 前の章で作成したCalc.g4を以下に修正
grammar Calc;
parse : expr EOF;
expr  : expr '*' expr
      | expr '/' expr
      | expr '+' expr
      | expr '-' expr
      | N
      | '-' N;
N     : [0-9]
      | [1-9][0-9]+;
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
  • コンパイル
export CLASSPATH=.:/usr/share/java/antlr4.jar:/usr/share/java/antlr4-runtime.jar:/usr/share/java:${CLASSPATH}
antlr4 Calc.g4
javac Calc*java
  • 以下で確認する(色々入力して、最後にCtrl-Dを押す。WindowsならCtrl-Zかも)
/usr/share/antlr4/grun Calc parse -gui

6.3.3 この節のまとめ

  • 掛け算、割り算表現も解釈出来るように修正

6.4 括弧でくるんだ部分を優先して処理するように修正

6.4.1 以下の操作を行っている動画


6.4.2 括弧でくるんだ部分を優先して処理するように修正 する手順

  • 前の章で作成したCalc.g4を以下に修正
  • 計算式を複数入れれるように修正
    • 区切り文字としては、EOF,改行,セミコロン(;)
grammar Calc;
parse : expr EOF;
expr  : '(' expr ')'
      | expr '*' expr
      | expr '/' expr
      | expr '+' expr
      | expr '-' expr
      | N
      | '-' N
      | '-' '(' expr ')' ;
N     : [0-9]
      | [1-9][0-9]+;
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
  • コンパイル
export CLASSPATH=.:/usr/share/java/antlr4.jar:/usr/share/java/antlr4-runtime.jar:/usr/share/java:${CLASSPATH}
antlr4 Calc.g4
javac Calc*java
  • 以下で確認する(色々入力して、最後にCtrl-Dを押す。WindowsならCtrl-Zかも)
/usr/share/antlr4/grun Calc parse -gui

6.4.3 この節のまとめ

  • 括弧でくるんだ部分を優先して処理するように修正

6.5 複数数式入力に対応する修正

6.5.1 以下の操作を行っている動画


6.5.2 複数数式入力に対応する修正 する手順

  • 前の章で作成したCalc.g4を以下に修正
  • 計算式を複数入れれるように修正
    • 区切り文字としては、EOF,改行,セミコロン(;)
grammar Calc;
lines:
     | equation
     | lines equation
     | EOF;
equation : expr CR
	 | expr EOF;
expr  : '(' expr ')'
      | expr '*' expr
      | expr '/' expr
      | expr '+' expr
      | expr '-' expr
      | N
      | '-' N
      | '-' '(' expr ')' ;
N     : [0-9]
      | [1-9][0-9]+;
CR  : [\n;];          // newlines, semicolons
WS : [ \t]+ -> skip ; // skip spaces, tabs
  • コンパイル
export CLASSPATH=.:/usr/share/java/antlr4.jar:/usr/share/java/antlr4-runtime.jar:/usr/share/java:${CLASSPATH}
antlr4 Calc.g4
javac Calc*java
  • 以下で確認する(色々入力して、最後にCtrl-Dを押す。WindowsならCtrl-Zかも)
/usr/share/antlr4/grun Calc lines -gui

6.5.3 この節のまとめ

  • 複数数式入力に対応できるようになった

6.6 現時点で最新バージョンのAntlr4をインストール(2021/1/25現在最新の4.9.1をインストール)

  • 色々やっていると以下の不具合があったので、最新のものを入れ直すことにしたhttps://www.antlr.org/download/
    • Ubuntu20.04でPython用のAntlr4パッケージがデフォルトで用意されていない
    • pipでPython用のAntlr4ランタイムを入れると、Ubuntu20.04でパッケージ化されているAntlr4はバージョンがあわない

6.6.1 以下の操作を行っている動画


6.6.2 現時点で最新バージョンのAntlr4をインストール(2021/1/25現在最新の4.9.1をインストール) する手順

  1. antlr-4.9.1-complete.jar ダウンロード
    wget -m -l 1 https://www.antlr.org/download/antlr-4.9.1-complete.jar
    ln -s www.antlr.org/download/antlr-4.9.1-complete.jar .
    
  2. 設定を行う
    • 設定の方法についてはhttps://github.com/antlr/antlr4/blob/master/doc/getting-started.mdのInstallationのUnixのところがわかりやすい。そこの方法を、カレントディレクトリのファイルを使うように、また、バージョンを4.9.1を使うように修正したのが以下となります。
    • これを利用しているシェルで実行すればOK
    export CLASSPATH=".:./antlr-4.9.1-complete.jar:$CLASSPATH"
    alias antlr4='java -Xmx500M -cp "./antlr-4.9.1-complete.jar:$CLASSPATH" org.antlr.v4.Tool'
    alias grun='java -Xmx500M -cp "./antlr-4.9.1-complete.jar:$CLASSPATH" org.antlr.v4.gui.TestRig'
    
    • 私はset.shというファイル名でこの命令を入れておいた。(また使う時にやり方忘れる為)
    • ファイルに入れている場合(set.sh)環境変数や、aliasを設定するには
    . set.sh
    
    • あるいは、
    source set.sh
    
  3. python用のパッケージをインストール
    • デバッグ用のipdbと、ANTLR 4.9.1 runtime for Python 3 をインストール
    • pip3はpipになる環境もある。環境にあわせてPython用のパッケージインストールコマンドを使って下さい。
    • virtualenv下に入れた方が良いかも。私はvirtualenv環境にこれらのパッケージを入れている
    • 例えばvirtualenvの環境名をYoutubeToolsにする場合
    virtualenv YoutubeTools
    
    • 作った環境に入るには
    source YoutubeTools/bin/activate
    
    • その後に以下を実行。pip3ではなく、pipを使うべき環境なら、そちらを使ってください。Ubuntu20.04ならpip3を使います。
    pip3 install ipdb
    pip3 install antlr4-python3-runtime
    

6.6.3 この節のまとめ

  • 現時点で最新バージョンのAntlr4をインストール(2021/1/25現在最新の4.9.1をインストール)
  • Python3で利用するのに必要なパッケージインストール
  • Python3用のデバッガーパッケージインストール

6.7 アクション用のラベル追加, Python用のコード生成

6.7.1 以下の操作を行っている動画


6.7.2 アクション用のラベル追加, Python用のコード生成 する手順

  • 前の章で作成したCalc.g4を以下に修正
grammar Calc;
lines:
     | equation
     | lines equation
     | EOF;
equation : expr CR
	 | expr EOF;
expr  : '(' expr ')' #myBr
      | expr '*' expr #myMul
      | expr '/' expr #myDiv
      | expr '+' expr #myAdd
      | expr '-' expr #mySub
      | N #myNum
      | '-' N #myMinusNum
      | '-' '(' expr ')' #myMinus ;
N     : [0-9]
      | [1-9][0-9]+;
CR  : [\n;];           // newlines, semicolons
WS : [ \t]+ -> skip ;  // skip spaces, tabs
  • コンパイル
antlr4 -Dlanguage=Python3 Calc.g4

6.7.3 この節のまとめ

  • アクション用のラベル追加し、Python3用のコードを自動生成した。

6.8 Python用のコード作成(Listenerメソッドが動くように修正)

6.8.1 関連文書

6.8.2 以下の操作を行っている動画


6.8.3 Python用のコード作成(Listenerメソッドが動くように修正) の手順

  • main.pyを以下の内容で作成
  • ANATLR4 の Python (2 and 3)関連文書 https://github.com/antlr/antlr4/blob/master/doc/python-target.md の “How do I create and run a custom listener?”の部分をベースに、今回作成した構文の名前に変更した
  • ipdbを使って動作確認しやすくなるコードも追加してある
  • ipdbを使わない場合は、ipdbが含まれいている行を削除するか、#でコメントにしておけばOK
import ipdb
import sys
from antlr4 import *
from CalcLexer import CalcLexer
from CalcParser import CalcParser
from CalcListener import CalcListener

class KeyPrinter(CalcListener):
    def exitKey(self, ctx):
        pass

def main(argv):
    input = InputStream(argv[1])
    lexer = CalcLexer(input)
    stream = CommonTokenStream(lexer)
    parser = CalcParser(stream)
    tree = parser.lines()
    #print(tree.getChild(0))
    #print(tree.getChild(0).getText())
    ##print(tree.getChild(0).toStringTree())
    #ipdb.set_trace()
    printer = KeyPrinter()
    walker = ParseTreeWalker()
    walker.walk(printer, tree)

if __name__ == '__main__':
    main(sys.argv)

6.8.4 前の節で自動生成した、CalcListener.pyを以下のように修正し、どこが、どの順で呼び出されるかを確認

  • 主要な解釈のexit部分に、print文追加
  • print(ctx.getText()) で該当の部分を表示
  • 今回は省略していますが、enterの方も表示してみるとよりわかりやすくなります。
# Generated from Calc.g4 by ANTLR 4.9.1
from antlr4 import *
if __name__ is not None and "." in __name__:
    from .CalcParser import CalcParser
else:
    from CalcParser import CalcParser

# This class defines a complete listener for a parse tree produced by CalcParser.
class CalcListener(ParseTreeListener):

    # Enter a parse tree produced by CalcParser#lines.
    def enterLines(self, ctx:CalcParser.LinesContext):
        pass

    # Exit a parse tree produced by CalcParser#lines.
    def exitLines(self, ctx:CalcParser.LinesContext):
        print("exitLines")
        pass


    # Enter a parse tree produced by CalcParser#equation.
    def enterEquation(self, ctx:CalcParser.EquationContext):
        pass

    # Exit a parse tree produced by CalcParser#equation.
    def exitEquation(self, ctx:CalcParser.EquationContext):
        print("exitEquation")
        print(ctx.getText())
        pass


    # Enter a parse tree produced by CalcParser#myMul.
    def enterMyMul(self, ctx:CalcParser.MyMulContext):
        pass

    # Exit a parse tree produced by CalcParser#myMul.
    def exitMyMul(self, ctx:CalcParser.MyMulContext):
        print("exitMyMul")
        print(ctx.getText())
        pass


    # Enter a parse tree produced by CalcParser#myNum.
    def enterMyNum(self, ctx:CalcParser.MyNumContext):
        pass

    # Exit a parse tree produced by CalcParser#myNum.
    def exitMyNum(self, ctx:CalcParser.MyNumContext):
        print("exitMyNum")
        print(ctx.getText())
        pass


    # Enter a parse tree produced by CalcParser#myMinusNum.
    def enterMyMinusNum(self, ctx:CalcParser.MyMinusNumContext):
        pass

    # Exit a parse tree produced by CalcParser#myMinusNum.
    def exitMyMinusNum(self, ctx:CalcParser.MyMinusNumContext):
        print("exitMyMinusNum")
        print(ctx.getText())
        pass


    # Enter a parse tree produced by CalcParser#myMinus.
    def enterMyMinus(self, ctx:CalcParser.MyMinusContext):
        pass

    # Exit a parse tree produced by CalcParser#myMinus.
    def exitMyMinus(self, ctx:CalcParser.MyMinusContext):
        print("exitMyMinus")
        print(ctx.getText())
        pass


    # Enter a parse tree produced by CalcParser#mySub.
    def enterMySub(self, ctx:CalcParser.MySubContext):
        pass

    # Exit a parse tree produced by CalcParser#mySub.
    def exitMySub(self, ctx:CalcParser.MySubContext):
        print("exitMySub")
        print(ctx.getText())
        pass


    # Enter a parse tree produced by CalcParser#myDiv.
    def enterMyDiv(self, ctx:CalcParser.MyDivContext):
        pass

    # Exit a parse tree produced by CalcParser#myDiv.
    def exitMyDiv(self, ctx:CalcParser.MyDivContext):
        print("exitMyDiv")
        print(ctx.getText())
        pass


    # Enter a parse tree produced by CalcParser#myAdd.
    def enterMyAdd(self, ctx:CalcParser.MyAddContext):
        pass

    # Exit a parse tree produced by CalcParser#myAdd.
    def exitMyAdd(self, ctx:CalcParser.MyAddContext):
        print("exitMyAdd")
        print(ctx.getText())
        pass


    # Enter a parse tree produced by CalcParser#myBr.
    def enterMyBr(self, ctx:CalcParser.MyBrContext):
        pass

    # Exit a parse tree produced by CalcParser#myBr.
    def exitMyBr(self, ctx:CalcParser.MyBrContext):
        pass



del CalcParser

6.8.5 テスト

  1. テスト1 100 の場合
    python main.py "100"
    
    • 上記の場合の出力
    exitLines
    exitMyNum
    100
    exitEquation
    100<EOF>
    exitLines
    
  2. テスト2 -100 の場合
    python main.py "-100"
    
    • 上記の場合の出力
    exitLines
    exitMyMinusNum
    -100
    exitEquation
    -100<EOF>
    exitLines
    
  3. テスト3 -100 の場合
    python main.py "-100"
    
    • 上記の場合の出力
    exitLines
    exitMyMinusNum
    -100
    exitEquation
    -100<EOF>
    exitLines
    
  4. テスト3 1+3 の場合
    python main.py "1+3"
    
    • 上記の場合の出力
    exitLines
    exitMyNum
    1
    exitMyNum
    3
    exitMyAdd
    1+3
    exitEquation
    1+3<EOF>
    exitLines
    
  5. テスト3 1+3 の場合
    python main.py "(3-1)*5"
    
    • 上記の場合の出力
    exitLines
    exitMyNum
    3
    exitMyNum
    1
    exitMySub
    3-1
    exitMyNum
    5
    exitMyMul
    (3-1)*5
    exitEquation
    (3-1)*5<EOF>
    exitLines
    

6.8.6 この節のまとめ

  • Python用のコード作成(Listenerメソッドが動くように修正)
  • print文を多量に埋め込むことで、どのように動作しているかを確認

6.9 電卓として動作するように処理を書く(Python向け)

6.9.1 以下の操作を行っている動画


6.9.2 電卓として動作するように処理を書く手順

6.9.3 前の節で自動生成した、CalcListener.pyを以下のように修正

# Generated from Calc.g4 by ANTLR 4.9.1
from antlr4 import *
from collections import deque
if __name__ is not None and "." in __name__:
    from .CalcParser import CalcParser
else:
    from CalcParser import CalcParser

# This class defines a complete listener for a parse tree produced by CalcParser.
class CalcListener(ParseTreeListener):
    s = deque([])

    # Enter a parse tree produced by CalcParser#lines.
    def enterLines(self, ctx:CalcParser.LinesContext):
        pass

    # Exit a parse tree produced by CalcParser#lines.
    def exitLines(self, ctx:CalcParser.LinesContext):
        #print("exitLines")
        pass


    # Enter a parse tree produced by CalcParser#equation.
    def enterEquation(self, ctx:CalcParser.EquationContext):
        pass

    # Exit a parse tree produced by CalcParser#equation.
    def exitEquation(self, ctx:CalcParser.EquationContext):
        print("***exitEquation")
        print(ctx.getText())
        if(len(self.s)>0):
            print(self.s.pop())
        pass


    # Enter a parse tree produced by CalcParser#myMul.
    def enterMyMul(self, ctx:CalcParser.MyMulContext):
        pass

    # Exit a parse tree produced by CalcParser#myMul.
    def exitMyMul(self, ctx:CalcParser.MyMulContext):
        #print("exitMyMul")
        #print(ctx.getText())
        a=self.s.pop()
        b=self.s.pop()
        self.s.append(a*b)
        pass

    # Enter a parse tree produced by CalcParser#myNum.
    def enterMyNum(self, ctx:CalcParser.MyNumContext):
        pass

    # Exit a parse tree produced by CalcParser#myNum.
    def exitMyNum(self, ctx:CalcParser.MyNumContext):
        #print("exitMyNum")
        #print(ctx.getText())
        self.s.append(int(ctx.getText()))
        pass


    # Enter a parse tree produced by CalcParser#myMinusNum.
    def enterMyMinusNum(self, ctx:CalcParser.MyMinusNumContext):
        pass

    # Exit a parse tree produced by CalcParser#myMinusNum.
    def exitMyMinusNum(self, ctx:CalcParser.MyMinusNumContext):
        #print("exitMyMinusNum")
        #print(ctx.getText())
        self.s.append(int(ctx.getText()))
        pass


    # Enter a parse tree produced by CalcParser#myMinus.
    def enterMyMinus(self, ctx:CalcParser.MyMinusContext):
        pass

    # Exit a parse tree produced by CalcParser#myMinus.
    def exitMyMinus(self, ctx:CalcParser.MyMinusContext):
        #print("exitMyMinus")
        #print(ctx.getText())
        a=self.s.pop()
        self.s.append(-a)
        pass


    # Enter a parse tree produced by CalcParser#mySub.
    def enterMySub(self, ctx:CalcParser.MySubContext):
        pass

    # Exit a parse tree produced by CalcParser#mySub.
    def exitMySub(self, ctx:CalcParser.MySubContext):
        #print("exitMySub")
        #print(ctx.getText())
        a=self.s.pop()
        b=self.s.pop()
        self.s.append(b-a)
        pass


    # Enter a parse tree produced by CalcParser#myDiv.
    def enterMyDiv(self, ctx:CalcParser.MyDivContext):
        pass

    # Exit a parse tree produced by CalcParser#myDiv.
    def exitMyDiv(self, ctx:CalcParser.MyDivContext):
        a=self.s.pop()
        b=self.s.pop()
        self.s.append(b/a)
        pass


    # Enter a parse tree produced by CalcParser#myAdd.
    def enterMyAdd(self, ctx:CalcParser.MyAddContext):
        pass

    # Exit a parse tree produced by CalcParser#myAdd.
    def exitMyAdd(self, ctx:CalcParser.MyAddContext):
        #print("exitMyAdd")
        #print(ctx.getText())
        a=self.s.pop()
        b=self.s.pop()
        self.s.append(a+b)
        pass


    # Enter a parse tree produced by CalcParser#myBr.
    def enterMyBr(self, ctx:CalcParser.MyBrContext):
        pass

    # Exit a parse tree produced by CalcParser#myBr.
    def exitMyBr(self, ctx:CalcParser.MyBrContext):
        pass



del CalcParser

6.9.4 動作確認

python main.py "(1+3)*4;"
python main.py "(1+3)*4;5*4;8*3;-1+2*3;1+1;5-3;5/3;-6/3;-(1+3)*5+3;(1+3)/2;-4/(1+1)"

6.9.5 この節のまとめ

  • 文法チェック、エラー処理無しだが、電卓として計算できるところまでもっていった
  • (バグをみつけたら、教えて下さい。お願い致します。)

6.9.6 この節の追記

  • 文法チェックを特別にコーディングしなくても、デフォである程度のエラー管理してくれるようで、文法間違ってたら、メッセージがでた。ただし、文法エラーという表示ではなかった。

7 簡易プログラム言語の作成その1

  • 簡易電卓に以下の機能を追加したプログラム言語を作成する
    • 変数に結果を代入可能
    • 変数をprint命令で表示可能
    • 直接計算結果をprint命令で表示可能
    • 文字列もprint命令で表示可能
    • 計算式にも変数を使える
    • コメント文を入れれる

7.1 parserと、lexerのファイル分離

  • ファイル分離しない方法で、文字列を扱う方法がわからなかったので、ファイルを2つに分ける
  • ファイルを分けると、parser側で、’+’等の記法でトークンを処理できなくなるようだったので、全ての特別な扱いをする記号に名前をつけて、Lexer側に追記した。

7.1.1 以下の操作を行っている動画


7.2 parserと、lexerのファイル分離 する手順

  • 前の章のCalc.g4をベースに MyLangLexer.g4 MyLangParser.g4 を作成する。MyLangLexer.g4 がトークンを抽出するルールで、MyLangParser.g4 がパースするルールを記述している。
  • 色々ためして、数式をパース出来るものが以下
  • 数式の終わりはセミコロンのみに変更(前の章では改行もOKだったが)
  • MyLangLexer.g4 は以下の内容で作成する。作成は使い慣れたテキストエディタでOK
lexer grammar MyLangLexer;

N     : [0-9]
      | [1-9][0-9]+;
ADD   : '+'; 
SUB   : '-'; 
MUL   : '*'; 
DIV   : '/'; 
SB    : '=';
LBR   : '(';
RBR   : ')';
EL : [;];          // semicolons
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
  • MyLangParser.g4 は以下の内容で作成する。同じく、作成は使い慣れたテキストエディタでOK
parser grammar MyLangParser;
options { tokenVocab=MyLangLexer; }
lines:
     | command
     | lines command
     | EOF;
command : expr EL;
expr  : LBR expr RBR #myBr
      | expr MUL expr #myMul
      | expr DIV expr #myDiv
      | expr ADD expr #myAdd
      | expr SUB expr #mySub
      | N #myNum
      | SUB N #myMinusNum
      | SUB LBR expr RBR #myMinus ;

7.2.1 コンパイルして、動作確認

antlr4 MyLangParser.g4 MyLangLexer.g4 
javac MyLang*java
grun MyLang lines -gui

7.2.2 この節のまとめ

  • parserルール記述ファイルと、lexer記述ファイルを分離した
  • 分離したものをコンパイルして、試す方法を説明した

7.3 print命令、変数、文字列も扱えるように修正

  • 関係する本家の文書https://github.com/antlr/antlr4/blob/master/doc/lexer-rules.md の”Lexer Commands”の”mode(), pushMode(), popMode, and more”に文字列を処理する方法の例があり、これをルールに追加している
    • モード切替をおこなわないと、文字列のデータの中まで通常のトークン探す処理をしてしまうので、モード切替を行い、文字列の中は、別処理にする記述をおこなっている

7.3.1 以下の操作を行っている動画


7.4 する手順

  • 前の節の2つのg4ファイルを以下に修正する
  • MyLangLexer.g4 は以下の内容で作成する。作成は使い慣れたテキストエディタでOK
    • ~[ほにゃらら]になっていると、ほにゃららを含まない文字列にマッチ
    • 変数名の自由度を高くしていたが、数式等が変数名と解釈されたりしたので、変数名の制限を強くした
    • 文字列の記述方法が[https://github.com/antlr/antlr4/blob/master/doc/lexer-rules.md]] の”Lexer Commands”の”mode(), pushMode(), popMode, and more”に文字列を処理する方法の例があり、これをそのまま使っている。ただし、このままだと、””でかこんだ中でバックスラッシュでエスケープは出来ない。そのルールにするには、より記述が複雑になるため、今回の例ではやっていない。
lexer grammar MyLangLexer;

N     : [0-9]
      | [1-9][0-9]+;
ADD   : '+'; 
SUB   : '-'; 
MUL   : '*'; 
DIV   : '/'; 
SB    : '=';
LBR   : '(';
RBR   : ')';
PRINT : 'print';
// VARIABLE : [A-Z]~[=; "'.+\-*/()\t\r\n]*;
VARIABLE : [a-zA-Z_][a-zA-Z0-9_]*;
EL : [;];          // semicolons
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines

// String
LQUOTE : '"' -> more, mode(STR) ;
mode STR;
STRING : '"' -> mode(DEFAULT_MODE) ; // token we want parser to see
TEXT : . -> more ; // collect more text for string
  • MyLangParser.g4 は以下の内容で作成する。作成は使い慣れたテキストエディタでOK
    • 変数に数式処理の結果を代入可能にするルール追加
    • 変数に文字列をを代入可能にするルール追加
    • print命令を追加(数式処理結果,文字列,変数の中身を出力可能)
    • 数式中にも変数を使えるようにルール追加
parser grammar MyLangParser;
options { tokenVocab=MyLangLexer; }
lines:
     | command
     | lines command
     | EOF;
command : VARIABLE SB expr EL   #mySubstitutionExpr
	| VARIABLE SB STRING EL #mySubstitutionStr
	| printout              #myPrintout;
printout :PRINT expr EL         #myPrintOutExpr
	 |PRINT STRING EL       #myPrintOutString
	 |PRINT VARIABLE EL     #myPrintOutVariable;
expr  : LBR expr RBR #myBr
      | expr MUL expr #myMul
      | expr DIV expr #myDiv
      | expr ADD expr #myAdd
      | expr SUB expr #mySub
      | N #myNum
      | VARIABLE #myVal
      | SUB N #myMinusNum
      | SUB VARIABLE #myMinusVal
      | SUB LBR expr RBR #myMinus ;

7.4.1 コンパイルして、動作確認

antlr4 MyLangParser.g4 MyLangLexer.g4 
javac MyLang*java
grun MyLang lines -gui

7.4.2 この節のまとめ

7.5 コメント文も扱えるように修正

7.5.1 以下の操作を行っている動画


  • MyLangLexer.g4 を以下の内容で作成する。作成は使い慣れたテキストエディタでOK。 MyLangParser.g4は前の章と同じ
lexer grammar MyLangLexer;

N     : [0-9]
      | [1-9][0-9]+;
ADD   : '+'; 
SUB   : '-'; 
MUL   : '*'; 
DIV   : '/'; 
SB    : '=';
LBR   : '(';
RBR   : ')';
PRINT : 'print';
// VARIABLE : [A-Z]~[=; "'.+\-*/()\t\r\n]*;
VARIABLE : [a-zA-Z_][a-zA-Z0-9_]*;
EL : [;];          // semicolons
BLOCK_COMMENT
	: '/*' .*? '*/' -> channel(HIDDEN)
	;
LINE_COMMENT
	: '//' ~[\r\n]* -> channel(HIDDEN)
	;
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines

// String
LQUOTE : '"' -> more, mode(STR) ;
mode STR;
STRING : '"' -> mode(DEFAULT_MODE) ; // token we want parser to see
TEXT : . -> more ; // collect more text for string

7.5.2 コンパイルして、動作確認

antlr4 MyLangParser.g4 MyLangLexer.g4 
javac MyLang*java
grun MyLang lines -gui

7.5.3 この節のまとめ

7.6 数式計算と、print 計算式; の動作部分のみ、Listenerメソッド追加

  • Pythonでリスナー関数が呼び出されるようにして、インタプリタとして動作するようにコードを追加する
  1. 以下の操作を行っている動画

  2. 数式計算と、print 計算式; の動作部分のみ、Listenerメソッド追加 する手順
    • 前節の MyLangLexer.g4 MyLangParser.g4 利用して、Pythonのコードを自動生成する
    antlr4 -Dlanguage=Python3 MyLangLexer.g4 MyLangParser.g4
    
    • MyLangLexer.py MyLangParser.py MyLangParserListener.py が生成される
    • MyLangParserListener.py に それぞれの処理を追加する。
  3. MyLangParserListener.py の修正例
    • 未作成今後作成予定
    
    
  4. main.py の作成例
    import ipdb
    import sys
    from antlr4 import *
    from MyLangLexer import MyLangLexer
    from MyLangParser import MyLangParser
    from MyLangParserListener import MyLangParserListener
    
    class KeyPrinter(MyLangParserListener):
    	def exitKey(self, ctx):
    		pass
    
    def main(argv):
    	input = InputStream(argv[1])
    	lexer = MyLangLexer(input)
    	stream = CommonTokenStream(lexer)
    	parser = MyLangParser(stream)
    	tree = parser.lines()
    	#print(tree.getChild(0))
    	#print(tree.getChild(0).getText())
    	##print(tree.getChild(0).toStringTree())
    	#ipdb.set_trace()
    	printer = KeyPrinter()
    	walker = ParseTreeWalker()
    	walker.walk(printer, tree)
    
    if __name__ == '__main__':
    	main(sys.argv)
    
    
  5. MyLangParserListener.py を以下に修正
    • 前章の計算する部分と、print 計算式の部分だけコード追加
    # Generated from MyLangParser.g4 by ANTLR 4.9.1
    from antlr4 import *
    from collections import deque
    if __name__ is not None and "." in __name__:
    	from .MyLangParser import MyLangParser
    else:
    	from MyLangParser import MyLangParser
    
    # This class defines a complete listener for a parse tree produced by MyLangParser.
    # Generated from MyLangParser.g4 by ANTLR 4.9.1
    from antlr4 import *
    from collections import deque
    if __name__ is not None and "." in __name__:
    	from .MyLangParser import MyLangParser
    else:
    	from MyLangParser import MyLangParser
    
    # This class defines a complete listener for a parse tree produced by MyLangParser.
    class MyLangParserListener(ParseTreeListener):
    	s = deque([])
    
    	# Enter a parse tree produced by MyLangParser#lines.
    	def enterLines(self, ctx:MyLangParser.LinesContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#lines.
    	def exitLines(self, ctx:MyLangParser.LinesContext):
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#mySubstitutionExpr.
    	def enterMySubstitutionExpr(self, ctx:MyLangParser.MySubstitutionExprContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#mySubstitutionExpr.
    	def exitMySubstitutionExpr(self, ctx:MyLangParser.MySubstitutionExprContext):
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#mySubstitutionStr.
    	def enterMySubstitutionStr(self, ctx:MyLangParser.MySubstitutionStrContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#mySubstitutionStr.
    	def exitMySubstitutionStr(self, ctx:MyLangParser.MySubstitutionStrContext):
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#myPrintout.
    	def enterMyPrintout(self, ctx:MyLangParser.MyPrintoutContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#myPrintout.
    	def exitMyPrintout(self, ctx:MyLangParser.MyPrintoutContext):
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#myPrintOutExpr.
    	def enterMyPrintOutExpr(self, ctx:MyLangParser.MyPrintOutExprContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#myPrintOutExpr.
    	def exitMyPrintOutExpr(self, ctx:MyLangParser.MyPrintOutExprContext):
    	    #print(ctx.getText())
    	    if(len(self.s)>0):
    		    print(self.s.pop())
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#myPrintOutString.
    	def enterMyPrintOutString(self, ctx:MyLangParser.MyPrintOutStringContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#myPrintOutString.
    	def exitMyPrintOutString(self, ctx:MyLangParser.MyPrintOutStringContext):
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#myPrintOutVariable.
    	def enterMyPrintOutVariable(self, ctx:MyLangParser.MyPrintOutVariableContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#myPrintOutVariable.
    	def exitMyPrintOutVariable(self, ctx:MyLangParser.MyPrintOutVariableContext):
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#myMul.
    	def enterMyMul(self, ctx:MyLangParser.MyMulContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#myMul.
    	def exitMyMul(self, ctx:MyLangParser.MyMulContext):
    	    a=self.s.pop()
    	    b=self.s.pop()
    	    self.s.append(a*b)
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#myNum.
    	def enterMyNum(self, ctx:MyLangParser.MyNumContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#myNum.
    	def exitMyNum(self, ctx:MyLangParser.MyNumContext):
    	    self.s.append(int(ctx.getText()))
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#myMinusNum.
    	def enterMyMinusNum(self, ctx:MyLangParser.MyMinusNumContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#myMinusNum.
    	def exitMyMinusNum(self, ctx:MyLangParser.MyMinusNumContext):
    	    self.s.append(int(ctx.getText()))
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#myMinus.
    	def enterMyMinus(self, ctx:MyLangParser.MyMinusContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#myMinus.
    	def exitMyMinus(self, ctx:MyLangParser.MyMinusContext):
    	    a=self.s.pop()
    	    self.s.append(-a)
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#myMinusVal.
    	def enterMyMinusVal(self, ctx:MyLangParser.MyMinusValContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#myMinusVal.
    	def exitMyMinusVal(self, ctx:MyLangParser.MyMinusValContext):
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#mySub.
    	def enterMySub(self, ctx:MyLangParser.MySubContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#mySub.
    	def exitMySub(self, ctx:MyLangParser.MySubContext):
    	    a=self.s.pop()
    	    b=self.s.pop()
    	    self.s.append(b-a)
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#myVal.
    	def enterMyVal(self, ctx:MyLangParser.MyValContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#myVal.
    	def exitMyVal(self, ctx:MyLangParser.MyValContext):
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#myDiv.
    	def enterMyDiv(self, ctx:MyLangParser.MyDivContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#myDiv.
    	def exitMyDiv(self, ctx:MyLangParser.MyDivContext):
    	    a=self.s.pop()
    	    b=self.s.pop()
    	    self.s.append(b/a)
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#myAdd.
    	def enterMyAdd(self, ctx:MyLangParser.MyAddContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#myAdd.
    	def exitMyAdd(self, ctx:MyLangParser.MyAddContext):
    	    a=self.s.pop()
    	    b=self.s.pop()
    	    self.s.append(a+b)
    	    pass
    
    
    	# Enter a parse tree produced by MyLangParser#myBr.
    	def enterMyBr(self, ctx:MyLangParser.MyBrContext):
    	    pass
    
    	# Exit a parse tree produced by MyLangParser#myBr.
    	def exitMyBr(self, ctx:MyLangParser.MyBrContext):
    	    pass
    
    
    
    del MyLangParser
    
    

7.6.1 動作確認

python main.py "print (1+3)*4+5*3;print -(1-5)*3;print 4/2;"
  • 出力
31
12
2.0

7.6.2 この節のまとめ

  • print 計算式; が上手く動作するように修正

7.7 残りのListener部分処理追加

  • 変数処理や、文字列操作等の処理を追加
  • まだ作って動作確認はしてない。作りしだい、文書に追記します。
  • ここまでの復習でやってもらっても良いかも
  1. 以下の操作を行っている動画

7.7.1 MyLangParserListener.py を以下に修正

  • 前章の計算する部分と、print 計算式の部分だけコード追加
  • vdicという連想配列をメンバ変数として用意して、変数関係に利用してます。
  • 内容で重要な部分
    • ctx.getText() でそのリスナー関数が呼ばれたところ全体の文字列を取得できる。
    • ctx.getChild(0).getText() で複数の要素があるところのリスナー関数は、1番目の要素の文字列取り出せる。
    • ctx.getChild(1).getText() で複数の要素があるところのリスナー関数は、2番目の要素の文字列取り出せる。要素数が1の時はエラーになる。
    • ctx.getChild(3).getText() で複数の要素があるところのリスナー関数は、3番目の要素の文字列取り出せる。要素数が2以下の時はエラーになる。
# Generated from MyLangParser.g4 by ANTLR 4.9.1
from antlr4 import *
from collections import deque
if __name__ is not None and "." in __name__:
	from .MyLangParser import MyLangParser
else:
	from MyLangParser import MyLangParser

# This class defines a complete listener for a parse tree produced by MyLangParser.
class MyLangParserListener(ParseTreeListener):
	s = deque([])
	vdic = {}

	# Enter a parse tree produced by MyLangParser#lines.
	def enterLines(self, ctx:MyLangParser.LinesContext):
	    pass

	# Exit a parse tree produced by MyLangParser#lines.
	def exitLines(self, ctx:MyLangParser.LinesContext):
	    pass


	# Enter a parse tree produced by MyLangParser#mySubstitutionExpr.
	def enterMySubstitutionExpr(self, ctx:MyLangParser.MySubstitutionExprContext):
	    pass

	# Exit a parse tree produced by MyLangParser#mySubstitutionExpr.
	def exitMySubstitutionExpr(self, ctx:MyLangParser.MySubstitutionExprContext):
	    #print("exitMySubstitutionExpr")
	    self.vdic[ctx.getChild(0).getText()]=self.s.pop();
	    #print(self.vdic[ctx.getChild(0).getText()])
	    #print(ctx.getText())
	    pass


	# Enter a parse tree produced by MyLangParser#mySubstitutionStr.
	def enterMySubstitutionStr(self, ctx:MyLangParser.MySubstitutionStrContext):
	    pass

	# Exit a parse tree produced by MyLangParser#mySubstitutionStr.
	def exitMySubstitutionStr(self, ctx:MyLangParser.MySubstitutionStrContext):
	    #print("exitMySubstitutionStr")
	    self.vdic[ctx.getChild(0).getText()]=ctx.getChild(2).getText()[1:-1]
	    #print(self.vdic[ctx.getChild(0).getText()])
	    #print(ctx.getText())
	    pass


	# Enter a parse tree produced by MyLangParser#myPrintout.
	def enterMyPrintout(self, ctx:MyLangParser.MyPrintoutContext):
	    pass

	# Exit a parse tree produced by MyLangParser#myPrintout.
	def exitMyPrintout(self, ctx:MyLangParser.MyPrintoutContext):
	    pass


	# Enter a parse tree produced by MyLangParser#myPrintOutExpr.
	def enterMyPrintOutExpr(self, ctx:MyLangParser.MyPrintOutExprContext):
	    pass

	# Exit a parse tree produced by MyLangParser#myPrintOutExpr.
	def exitMyPrintOutExpr(self, ctx:MyLangParser.MyPrintOutExprContext):
	    #print(ctx.getText())
	    if(len(self.s)>0):
	       print(self.s.pop())
	    pass


	# Enter a parse tree produced by MyLangParser#myPrintOutString.
	def enterMyPrintOutString(self, ctx:MyLangParser.MyPrintOutStringContext):
	    pass

	# Exit a parse tree produced by MyLangParser#myPrintOutString.
	def exitMyPrintOutString(self, ctx:MyLangParser.MyPrintOutStringContext):
	    #str=ctx.getText()
	    #print(ctx.getChild(0).getText())
	    #print(ctx.getChild(1).getText())
	    #print(str)
	    #str2=str[6:-2]
	    print(ctx.getChild(1).getText()[1:-1])
	    pass


	# Enter a parse tree produced by MyLangParser#myPrintOutVariable.
	def enterMyPrintOutVariable(self, ctx:MyLangParser.MyPrintOutVariableContext):
	    pass

	# Exit a parse tree produced by MyLangParser#myPrintOutVariable.
	def exitMyPrintOutVariable(self, ctx:MyLangParser.MyPrintOutVariableContext):
	    print(self.vdic[ctx.getChild(1).getText()])
	    pass


	# Enter a parse tree produced by MyLangParser#myMul.
	def enterMyMul(self, ctx:MyLangParser.MyMulContext):
	    pass

	# Exit a parse tree produced by MyLangParser#myMul.
	def exitMyMul(self, ctx:MyLangParser.MyMulContext):
	    a=self.s.pop()
	    b=self.s.pop()
	    self.s.append(a*b)
	    pass


	# Enter a parse tree produced by MyLangParser#myNum.
	def enterMyNum(self, ctx:MyLangParser.MyNumContext):
	    pass

	# Exit a parse tree produced by MyLangParser#myNum.
	def exitMyNum(self, ctx:MyLangParser.MyNumContext):
	    self.s.append(int(ctx.getText()))
	    pass


	# Enter a parse tree produced by MyLangParser#myMinusNum.
	def enterMyMinusNum(self, ctx:MyLangParser.MyMinusNumContext):
	    pass

	# Exit a parse tree produced by MyLangParser#myMinusNum.
	def exitMyMinusNum(self, ctx:MyLangParser.MyMinusNumContext):
	    self.s.append(int(ctx.getText()))
	    pass


	# Enter a parse tree produced by MyLangParser#myMinus.
	def enterMyMinus(self, ctx:MyLangParser.MyMinusContext):
	    pass

	# Exit a parse tree produced by MyLangParser#myMinus.
	def exitMyMinus(self, ctx:MyLangParser.MyMinusContext):
	    a=self.s.pop()
	    self.s.append(-a)
	    pass


	# Enter a parse tree produced by MyLangParser#myMinusVal.
	def enterMyMinusVal(self, ctx:MyLangParser.MyMinusValContext):
	    pass

	# Exit a parse tree produced by MyLangParser#myMinusVal.
	def exitMyMinusVal(self, ctx:MyLangParser.MyMinusValContext):
	    #print("exitMyMinusVal")
	    #print(ctx.getChild(1).getText())
	    self.s.append(-self.vdic[ctx.getChild(1).getText()])
	    pass


	# Enter a parse tree produced by MyLangParser#mySub.
	def enterMySub(self, ctx:MyLangParser.MySubContext):
	    pass

	# Exit a parse tree produced by MyLangParser#mySub.
	def exitMySub(self, ctx:MyLangParser.MySubContext):
	    a=self.s.pop()
	    b=self.s.pop()
	    self.s.append(b-a)
	    pass


	# Enter a parse tree produced by MyLangParser#myVal.
	def enterMyVal(self, ctx:MyLangParser.MyValContext):
	    pass

	# Exit a parse tree produced by MyLangParser#myVal.
	def exitMyVal(self, ctx:MyLangParser.MyValContext):
	    self.s.append(self.vdic[ctx.getText()])
	    pass


	# Enter a parse tree produced by MyLangParser#myDiv.
	def enterMyDiv(self, ctx:MyLangParser.MyDivContext):
	    pass

	# Exit a parse tree produced by MyLangParser#myDiv.
	def exitMyDiv(self, ctx:MyLangParser.MyDivContext):
	    a=self.s.pop()
	    b=self.s.pop()
	    self.s.append(b/a)
	    pass


	# Enter a parse tree produced by MyLangParser#myAdd.
	def enterMyAdd(self, ctx:MyLangParser.MyAddContext):
	    pass

	# Exit a parse tree produced by MyLangParser#myAdd.
	def exitMyAdd(self, ctx:MyLangParser.MyAddContext):
	    a=self.s.pop()
	    b=self.s.pop()
	    self.s.append(a+b)
	    pass


	# Enter a parse tree produced by MyLangParser#myBr.
	def enterMyBr(self, ctx:MyLangParser.MyBrContext):
	    pass

	# Exit a parse tree produced by MyLangParser#myBr.
	def exitMyBr(self, ctx:MyLangParser.MyBrContext):
	    pass



del MyLangParser

7.7.2 動作確認

python main.py "print 1+1;print \"hello\";X=\"sss\";print X;Y=-(5-1)*5;print Y;Z=5*Y;print -Z;print Z;"
  • 出力
2
hello
sss
-20
100
-100

7.7.3 この節のまとめ

  • 変数がらみの処理追加し、変数を利用可能で、文字列も変数に入れて処理できる簡易プログラム言語を作成した
  • ループ、関数定義、if文は現状使えない簡易言語を作れれた。他の機能追加も、多分作れそうな感じまできた。
  • ここまでに利用した機能はANTLR4のほんの一部と思われるが、もっと複雑なプログラム言語、独自の設定ファイルや、変換ツール、フィルターも、ここまでのテクだけ、あるいはちょっとプラスアルファで作れそう。
  • 動作確認はちょっとしか行ってないので、バグやおかしな動作があるかもしれません。みつけたら教えてください。不具合を修正するのも練習に良いと思うので、みつけたら、修正していただいた方が良いと思います。

7.8 ファイル指定して実行できるバージョンのmain2.py作成

  • コマンドラインの引数として命令を直接与えていたけど、ファイルで命令を与えれるようにしたmain2.pyを作成
  1. 以下の操作を行っている動画

7.8.1 main2.py を以下で作成

  • main.pyをベースに作成
import ipdb
import sys
from antlr4 import *
from MyLangLexer import MyLangLexer
from MyLangParser import MyLangParser
from MyLangParserListener import MyLangParserListener

class KeyPrinter(MyLangParserListener):
	def exitKey(self, ctx):
		pass

def main(argv):
	with open(argv[1]) as f:
	  input = InputStream(f.read())
	  lexer = MyLangLexer(input)
	  stream = CommonTokenStream(lexer)
	  parser = MyLangParser(stream)
	  tree = parser.lines()
	  #print(tree.getChild(0))
	  #print(tree.getChild(0).getText())
	  ##print(tree.getChild(0).toStringTree())
	  #ipdb.set_trace()
	  printer = KeyPrinter()
	  walker = ParseTreeWalker()
	  walker.walk(printer, tree)

if __name__ == '__main__':
	main(sys.argv)
  • test01.mylangファイルを以下の内容で作成
/* 
   複数行のコメント
*/  
// 一行のコメント
X=12;
print 3*X;
print -X*5;
S="Hello";
print S;
print "World!";
print -(5-1)*5;
print -(5+1)*5;

7.8.2 動作確認

python3 main2.py test01.mylang 
or 
python main2.py test01.mylang 
  • その時の出力

7.8.3 この節のまとめ

  • ファイルからプログラムを読み込めるmain2.pyを作成

7.9 この章のまとめ

  • 以下の処理を追加して、簡易プログラム言語を作ってみた。
    • 変数処理
    • 文字列処理
    • 計算式に変数を利用可能に改良
    • print命令の実装
  • 今後
    • もしかしたら、ループとか、関数定義、関数利用、if文等の処理を追加した、簡易言語パート2を作るかもしれない。
    • もともと別の用途として、Antler4を使いだしたので、他の用途の簡易言語を作るかもしれない。
    • いちおう簡易言語作れるようになったので、ここで、この文書や、動画シリーズ終わるかも

8 今後

  • 今後も文書追加していきます。

9 この文書のチェンジログ

  • 2021/01/17 初版
  • 2021/01/18 自然数を解釈する の章, 足し算表現、引き算表現の解釈処理追加 の章, 掛け算表現、割り算表現の解釈処理追加, 括弧でくるんだ部分を優先して処理するように修正 追加
  • 2021/01/25 括弧でくるんだ部分を優先して処理するように修正 修正
  • 2021/01/25 複数数式入力に対応する修正, 現時点で最新バージョンのAntlr4をインストール(2021/1/25現在最新の4.9.1をインストール), アクション用のラベル追加, Python用のコード生成, Python用のコード作成(Listenerメソッドが動くように修正), 電卓として動作するように処理を書く(Python向け) 追加
  • 2021/01/27 簡易プログラム言語の作成その1 の章追加
  • 2021/01/28 簡易プログラム言語の作成その1 の章の def exitMyMinusVal(self, ctx:MyLangParser.MyMinusValContext): にバグがあったので修正(デバッグ用print文もコメントアウト)
  • 2021/01/31 簡易プログラム言語の作成その1 の章の 「ファイル指定して実行できるバージョンのmain2.py作成」の節追加

著者: NM Max

Created: 2021-01-31 日 15:11

Validate

SoX(Sound eXchange)の使い方例

SoX(Sound eXchange)の使い方例

1 概要

  • SoX(Sound eXchange), 音声処理のためのスイスアーミーナイフ
  • なんでも出来るがゆえ、使い方難しい

2 この文書作ろうと思ったきっかけ

  • 備忘録かねて作ることに

4 似たツール

  • CSound (音作成関係面では)

5 インストール情報

  • Windows, Mac用は https://sourceforge.net/projects/sox/files/sox/ からダウンロード可能,最新のバージョン番号の該当のファイルをダウンロードして、実行すれば多分インストールされるはず
  • Ubuntu(Debianも)の場合は、パッケージになってるので、以下でOK
sudo apt install sox
  • Linuxの他のディストリビューションも、SoXは必ずパッケージ化されてると思うので、そのディストリビューションのパッケージ管理コマンドでインストール出来る。

6 音の作成

  • 色々な音を簡単に作成して試すことが出来る(効果音とか作りやすい)

6.1 指定時間の無音音声ファイル作成

6.1.1 以下の操作を行っている動画


6.1.2 指定時間の無音音声ファイル作成手順

  • 3秒間の無音
sox -n silence.wav trim 0.0 3.0 
  • オプションの意味は
    • -n : 入力無し
    • silence.wav 出力ファイル名
    • trim 0.0 3.0 :最初の3秒切り出し
  • 以下でもいけた(ゴミ数値はいってるかもしれないけど、聞いても無音)
sox -n silence.wav synth 3 sine 100 vol 0 
  • オプションの意味は
    • -n : 入力無し
    • silence.wav 出力ファイル名
    • synth 3 sine 100 : 音声合成3秒間sine波形で、周波数100Hz
    • vol 0 : ボリューム0

6.1.3 この節のまとめ

  • 指定時間の無音ファイルの生成

6.2 指定時間の指定周波数のサイン波作成

6.2.1 以下の操作を行っている動画


6.2.2 指定時間の指定周波数のサイン波ファイル作成 手順

  • 周波数500Hzの3秒の波形を生成して、音を聞いてみます。
play -n synth 3 sine 500
  • オプションの意味は
    • -n : 入力無し
    • synth 3 sine 500 : 音声合成3秒間sine波形で、周波数500Hz
  • 今の音を 500.wav というファイルに保存する場合は以下で出来ます。
sox -n 500.wav synth 3 sine 500
  • オプションの意味は
    • -n : 入力無し
    • 500.wav : 出力ファイル名
    • synth 3 sine 500 : 音声合成3秒間sine波形で、周波数500Hz

6.2.3 この節のまとめ

  • playコマンドの使い方の例(playコマンドで試して、上手くいったものをsoxコマンドでファイル出力するのがオススメ)
  • 指定時間の指定周波数のサイン波ファイル作成をおこなった
  • (周波数を指定しない場合440Hzがデフォで使われる)http://sox.sourceforge.net/sox.html(でsynthで検索)

6.3 指定時間の色々な波形の音の作成

6.3.1 以下の操作を行っている動画


6.3.2 指定時間の指定周波数の色々な波形の作成 手順

  • マニュアルのsynth部分を確認すると以下の波形が指定可能です(デフォルトはsine) http://sox.sourceforge.net/sox.html(でwhiteあるいはsynthで検索)
  • Linux系なら以下でマニュアルみれる
man sox
  • sine
  • square
  • triangle
  • sawtooth
  • trapezium
  • exp
  • [white]noise
  • tpdfnoise
  • pinknoise
  • brownnoise
  • pluck
  1. square
    • 周波数500Hzの3秒の波形を生成して、音を聞いてみます。
    play -n synth 3 square 500
    
    • オプションの意味は
      • -n : 入力無し
      • 500.wav : 出力ファイル名
      • synth 3 square 500 : 音声合成3秒間square波形で、周波数500Hz
  2. triangle
    • 周波数500Hzの3秒の波形を生成して、音を聞いてみます。
    play -n synth 3 triangle 500
    
    • オプションの意味は
      • -n : 入力無し
      • 500.wav : 出力ファイル名
      • synth 3 triangle 500 : 音声合成3秒間triangle波形で、周波数500Hz
  3. sawtooth
    • 周波数500Hzの3秒の波形を生成して、音を聞いてみます。
    play -n synth 3 sawtooth 500
    
    • オプションの意味は
      • -n : 入力無し
      • 500.wav : 出力ファイル名
      • synth 3 sawtooth 500 : 音声合成3秒間sawtooth波形で、周波数500Hz
  4. trapezium
    • 周波数500Hzの3秒の波形を生成して、音を聞いてみます。
    play -n synth 3 trapezium 500
    
    • オプションの意味は
      • -n : 入力無し
      • 500.wav : 出力ファイル名
      • synth 3 trapezium 500 : 音声合成3秒間trapezium波形で、周波数500Hz
  5. exp
    • 周波数500Hzの3秒の波形を生成して、音を聞いてみます。
    play -n synth 3 exp 500
    
    • オプションの意味は
      • -n : 入力無し
      • 500.wav : 出力ファイル名
      • synth 3 exp 500 : 音声合成3秒間exp波形で、周波数500Hz
  6. pluck
    • 周波数500Hzの3秒の波形を生成して、音を聞いてみます。
    play -n synth 3 pluck 500
    
    • オプションの意味は
      • -n : 入力無し
      • 500.wav : 出力ファイル名
      • synth 3 pluck 500 : 音声合成3秒間pluck波形で、周波数500Hz

6.3.3 この節のまとめ

  • sine以外の波形を生成して聴いてみた
  • noise系は不快な音になるので、各自お試しください。(ノイズ系では周波数指定は意味なし)

6.4 プッシュフォンの音をSoXで作ってみる

6.4.1 以下の操作を行っている動画


6.4.2 プッシュフォンの音をSoXで作ってみるの手順

  • 「プッシュホン 周波数」でググると、使っている周波数を調べることが出来る
    • 1209Hz,1336Hz,1447Hz,1633Hzと 697Hz,770Hz,852Hz,941Hzの組み合わせ
    • 9,5,1なら1447Hz+852Hz, 1336Hz+770Hz, 1209Hz+697Hz
play -n synth 0.5 sine 1447 sine 852  
play -n synth 0.5 sine 1336 sine 770
play -n synth 0.5 sine 1209 sine 697
  • オプションの意味は
    • -n : 入力無し
    • synth 0.5 sine 1447 sine 852: 音声合成0.5秒間sine波形で、周波数1447Hzと周波数852Hzの和音
  • 一度にやると
play -n synth 0.5 sine 1447 sine 852  ; play -n synth 0.5 sine 1336 sine 770 ; play -n synth 0.5 sine 1209 sine 697

6.4.3 この節のまとめ

  • 今までの知識を元に、プッシュフォンの音を合成してみた

6.5 周波数を変化させてみる1

6.5.1 以下の操作を行っている動画


6.5.2 周波数を変化させてみる1

  • 100Hzから1000Hzまで
play -n synth 0.5 sine 100-1000
  • 1000Hzから100Hzまで
play -n synth 0.5 sine 1000-100
  • マニュアルで確認すると、用意されている変化の方法として以下がある。
:      Linear: the tone will change by a fixed number of hertz per second.

+      Square: a second-order function is used to change the tone.

/      Exponential: the tone will change by a fixed number of semitones per second.

-      Exponential: as `/', but initial phase always zero, and stepped (less smooth) frequency changes.
  • :をやってみる
play -n synth 0.5 sine 100:1000
  • +をやってみる
play -n synth 0.5 sine 100+1000
  • /をやってみる
play -n synth 0.5 sine 100/1000

6.5.3 この節のまとめ

  • 周波数を変化させてみた

7 今後

  • 今後も文書追加していきます。

8 この文書のチェンジログ

  • 2021/01/17 初版
  • 2021/01/19 周波数を変化させてみる1 の節追加

著者: NM Max

Created: 2021-01-24 日 07:34

Validate

FFmpegの基本的な使い方





FFmpeg使い方例

目次

1 概要

  • FFmpegで、動画から音声のみ抜き出すとか、一部切り出しを良く行っていた
  • 今日、将棋の動画を撮っていて、途中で通信エラーになった部分を除いて(対戦中2度発生)みて、その手順を動画にしてみようと思い、このシリーズ作りました。

2 関連文書

3 動画の情報確認

3.1 以下の操作を行っている動画


3.2 動画の情報確認

ffmpeg -i 調査対象ファイル

3.3 この章のまとめ

4 動画の情報をFFmpegを利用して調べる方法の紹介

5 サポートしているコーデックの確認

  • コンパイル方法により、サポートしているコーデックが異るので、確認する必要があったりする

5.1 以下の操作を行っている動画


5.2 サポートしているコーデックの確認手順

ffmpeg -codecs 
  • Dがついているもの Demuxing supported(読み込み)
  • Eがついているもの Muxing supported(書き込み)
  • 以下もマニュアルにある確認コマンド(他にも色々あり、マニュアル確認) https://ffmpeg.org/ffmpeg.html#Stream-specifiers-1
ffmpeg -formats

5.3 この章のまとめ

  • 使っているffmpegのサポートコーデックの確認方法について

6 動画からの音声の抜き出し(劣化無し)

6.1 以下の操作を行っている動画


6.2 音声だけの抜き出し手順

ffmpeg -i 元動画ファイル名 -vn -c copy 出力ファイル名
ffmpeg -i 元動画ファイル名 -c copy 出力ファイル名
  • -vn ビデオ出力オフオプション(私の環境だと無くてもいけた)
  • -c copyが無変換で、そのまま抜き出す(無劣化)
  • 元ファイルをtest.mp4で、音声
ffmpeg -i test.mp4 -c copy test.aac

6.3 時間指定での抜き出し(開始時間と、時間の長さ指定)

  • 開始時間9秒から3秒間抜き出す場合
  • -ssオプションは前と後で動作が異るらしい。後につけると時間がより正確で、前だと速度はやくなるという情報があった
  • 正確な時間ではなく、最も近いseekポイントを起点として処理されるらしい
  • https://ffmpeg.org/ffmpeg.html#Main-options
  • 以下だと上手く動作している(ファイルの長さが異るので、動作内容は異ると思われる)
ffmpeg -i test.mp4 -ss 00:09 -t 3 -c copy test2.aac
ffmpeg -ss 00:09 -i test.mp4 -t 3 -c copy test3.aac

6.4 時間指定での抜き出し(開始時間と、終了時間指定)

  • 開始時間が9秒から、終了時間が12秒
  • 以下だと上手く動作している(ファイルの長さが異るので、動作内容は異ると思われる)
ffmpeg -i test.mp4 -ss 00:09 -to 00:12 -c copy test4.aac
ffmpeg -ss 00:09 -to 00:12 -i test.mp4  -c copy test5.aac
  • test2.aac test4.aac が同じファイルだった
  • test3.aac test5.aac が同じファイルだった
  • これだと、長さが12秒の音声となった
ffmpeg -ss 00:09 -i test.mp4 -to 00:12 -c copy test6.aac

6.5 この章のまとめ

  • 動画から音を抜き出す手順について

7 動画からの動画部分のみの切り出し(音削除)(劣化無し)

7.1 以下の操作を行っている動画


7.2 動画からの動画部分のみの切り出し(音削除)手順

ffmpeg -i 元動画ファイル名 -an -c copy 出力ファイル名
  • -an audio部分を除いて出力(音無しに)
  • -c copyが無変換で、そのまま抜き出す(無劣化)
  • 元ファイルをtest.mp4で、音声
ffmpeg -i test.mp4 -an -c copy test-ov.mp4

7.3 時間指定での抜き出し(開始時間と、時間の長さ指定)

  • 開始時間9秒から3秒間抜き出す場合
  • -ssオプションは前と後で動作が異るらしい。後につけると時間がより正確で、前だと速度はやくなるという情報があった
  • 正確な時間ではなく、最も近いseekポイントを起点として処理されるらしい
  • https://ffmpeg.org/ffmpeg.html#Main-options
  • 以下だと上手く動作している(ファイルの長さが異るので、動作内容は異ると思われる)
ffmpeg -i test.mp4 -ss 00:09 -t 3 -an -c copy test2-vo.mp4
ffmpeg -ss 00:09 -i test.mp4 -t 3 -an -c copy test3-vo.mp4

7.4 時間指定での抜き出し(開始時間と、終了時間指定)

  • 開始時間が9秒から、終了時間が12秒
  • 以下だと上手く動作している(ファイルの長さが異るので、動作内容は異ると思われる)
ffmpeg -i test.mp4 -ss 00:09 -to 00:12 -an -c copy test4-vo.mp4
ffmpeg -ss 00:09 -to 00:12 -i test.mp4 -an -c copy test5-vo.mp4
  • test2-vo.mp4 test4-vo.mp4 が同じファイルだった
  • test3-vo.mp4 test5-vo.mp4 が同じファイルだった

7.5 この章のまとめ

  • 動画から音抜きの動画を抜き出す方法について

8 分離した音なし動画と音声の再合成(劣化無し)

8.1 以下の操作を行っている動画


8.2 分離した音なし動画と音声の再合成手順

ffmpeg -i 音無し動画ファイル名 -i 音声ファイル -c copy 出力ファイル名
  • -c copyが無変換で、そのまま抜き出す(無劣化)
  • 元ファイルをtest-vo.mp4で、音声ファイルをtest.aac
ffmpeg -i test3-vo.mp4 -i test3.aac -c copy test3-new.mp4

8.3 この章のまとめ

  • 音あるいは画像に加工して再合成する時に使ってる手順の紹介

9 動画から一部切り出し

9.1 以下の操作を行っている動画


9.2 動画から切り出し手順

ffmpeg -ss 開始時間 -i 元動画ファイル名 -t 秒単位で切り出す動画の時間幅 -c copy 出力ファイル名
ffmpeg -ss 開始時間 -to 終了時間 -i 元動画ファイル名 -c copy 出力ファイル名

9.3 時間指定での抜き出し(開始時間と、時間の長さ指定)

  • 開始時間9秒から3秒間抜き出す場合
  • -ssオプションは前と後で動作が異るらしい。後につけると時間がより正確で、前だと速度はやくなるという情報があった
  • 正確な時間ではなく、最も近いseekポイントを起点として処理されるらしい
  • https://ffmpeg.org/ffmpeg.html#Main-options
  • 以下だと上手く動作している(ファイルの長さが異るので、動作内容は異ると思われる)
ffmpeg -i test.mp4 -ss 00:09 -t 3 -c copy out2.mp4
ffmpeg -ss 00:09 -i test.mp4 -t 3 -c copy out3.mp4

9.4 時間指定での抜き出し(開始時間と、終了時間指定)

  • 開始時間が9秒から、終了時間が12秒
  • 以下だと上手く動作している(ファイルの長さが異るので、動作内容は異ると思われる)
ffmpeg -i test.mp4 -ss 00:09 -to 00:12 -c copy out4.mp4
ffmpeg -ss 00:09 -to 00:12 -i test.mp4  -c copy out5.mp4

9.5 オプション場所による違い

  • out2.mp4 out4.mp4 が同じファイルだった
  • out3.mp4 out5.mp4 が同じファイルだった

10 動画や音声のフォーマット変換(劣化あるときも)

10.1 以下の操作を行っている動画


10.2 動画フォーマット変換手順

ffmpeg -i 元動画ファイル名 出力ファイル名
  • 例 aacをwavフォーマットに変換
ffmpeg -i test.aac test.wav
  • 例 mp4をmkvフォーマットに変換
ffmpeg -i test.mp4 test.mkv

10.3 この章について

  • 動画や、音声ファイルのフォーマット変換について

11 複数の同じエンコード動画の無劣化結合

11.1 以下の操作を行っている動画


11.2 複数の同じエンコード動画の無劣化結合手順

11.2.1 元動画(ここではtest.mp4)から3つの動画を無劣化で切り出し

  • 復習かねて、昔の章でやった手順で3つのファイルを切り出し。
ffmpeg -ss 開始時間 -to 終了時間 -i 元動画ファイル名 -c copy 出力ファイル名
  • 最初の3秒切り出し
ffmpeg -to 00:03 -i test.mp4 -c copy x01.mp4
  • 9秒から12秒まで切り出し
ffmpeg -ss 00:09 -to 00:12 -i test.mp4 -c copy x02.mp4
  • ファイルの最初からの時間ではなく、終わりを基準に指定するのにsseofオプションがある。以下だと終わりの時間を指定して、最後までを切り出し
ffmpeg -sseof ファイル末端からの時間で開始時間指定 -i 元動画ファイル名 -c copy 出力ファイル名
  • 動画の最後の3秒切り出し
ffmpeg -sseof -00:03 -i test.mp4 -c copy x03.mp4
  • 以上の手順でx01.mp4,x02.mp4,x03.mp4を用意できた。

11.2.2 上で作成した3つの動画を結合するのに必要なファイル作成

  • 結合するファイルを書式にのっとって記述したファイルを用意
  • 今回は filelist.txt というファイル名で以下の内容で作成します。
file 'x01.mp4'
file 'x02.mp4'
file 'x03.mp4'

11.2.3 上で作成した3つの動画を結合して1つの動画を作成する

ffmpeg -f concat -safe 0 -i 結合したいファイルを規約にしたがって記述したファイル名 -c copy 出力ファイル名
  • 今回だと
ffmpeg -f concat -safe 0 -i filelist.txt -c copy xout.mp4

11.3 この章について

  • 複数の同じエンコードの動画を無劣化で結合する手順について記述してみました。

12 音量調節

12.1 以下の操作を行っている動画


12.2 音量調節手順

12.2.1 ボリューム変更

  1. ボリュームを3割に
    • 入力ファイル test.mp4 出力ファイルを test_a_3.mp4 とした場合
    ffmpeg -i test.mp4 -filter:a "volume=0.3" test_a_3.mp4
    
    • 増やす場合は0.3を1より大きな数にする。
    • dBでやるには、0.3の部分を増やすならプラスで、減らすならマイナスの値+dBで指令するらしい(ex. -10dB)
  2. ラウドネスの正規化(これが良いらしい)
    • 公式サイトの文書 https://ffmpeg.org/ffmpeg-filters.html#loudnorm
    • 音量の変化がわかりやすいように、先程ボリューム下げた動画を元に正規化してみます。(通常であれば、元の動画を変換した方が劣化が少なくてすみます。)
    ffmpeg -i test_a_3.mp4 -filter:a loudnorm test_loudnorm.mp4
    
  3. ピークや、平均で規格化(normalize)
    1. 元ファイルの音を分析
      ffmpeg -i test_a_3.mp4 -filter:a volumedetect -f null /dev/null
      
      • 以下のような出力が得られる
      .... 省略
      video:589kB audio:1716kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
      [Parsed_volumedetect_0 @ 0x5595cad71400] n_samples: 878592
      [Parsed_volumedetect_0 @ 0x5595cad71400] mean_volume: -29.6 dB
      [Parsed_volumedetect_0 @ 0x5595cad71400] max_volume: -13.4 dB
      [Parsed_volumedetect_0 @ 0x5595cad71400] histogram_13db: 4
      [Parsed_volumedetect_0 @ 0x5595cad71400] histogram_14db: 86
      [Parsed_volumedetect_0 @ 0x5595cad71400] histogram_15db: 1705
      
      • 平均は(mean_volume)でわかる -29.6db
      [Parsed_volumedetect_0 @ 0x5595cad71400] mean_volume: -29.6 dB
      
      • 最大は(max_volume)でわかる -13.4db
      [Parsed_volumedetect_0 @ 0x5595cad71400] max_volume: -13.4 dB
      
    2. 調べた数値を元にボリューム変更
      • 平均を元に規格化するなら
      ffmpeg -i test_a_3.mp4 -filter:a "volume=29.6dB" test_norm_mean.mp4
      
      • マックス値を元に規格化するなら
      ffmpeg -i test_a_3.mp4 -filter:a "volume=13.4dB" test_norm_max.mp4
      
  4. ffmpeg-normalizeによる処理
    pip3 install ffmpeg-normalize
    

    or

    pip install ffmpeg-normalize
    
    • 今回利用しているtest.mp4の場合は以下でいけた
    ffmpeg-normalize test_a_3.mp4 -c:a aac -o test_norm.mp4
    
    • -c:a aac オプションは通常のオーディオファイルなら不要みたいだが、mp4の場合自動検出してくれないようなので、自分で調べて指定した。動画の中身はffmpeg -i 調べたい動画ファイルで調べることが可能
    • vorbisの場合の例
    ffmpeg-normalize ~/Videos/simplescreenrecorder.mkv -ar 48000 -c:a libvorbis -f -o ~/Videos/simplescreenrecorder_norm.mkv
    

12.3 この章のまとめ

  • 音量調節方法について
  • 各種normalizeの方法について
    • 一番簡単なのはffmpegのみでloudnormを利用するのが楽っぽい

13 公式文書のフィルターの紹介の例の最初をやってみた。

  • この公式文書の最初の例の、画像の上半分を反転させたものをした半分に処理する処理をやってみることで、フィルターの使い方の基礎を知る。

13.1 関連文書

13.2 以下の操作を行っている動画


13.3 フィルター基礎をやってみる

13.4 関係文書

ffmpeg -i test.mp4 -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" test_filter01.mp4
  • オプションの意味
    • -vf 画像関係のフィルター
      • 5.5 Video Options https://ffmpeg.org/ffmpeg.html#Video-Options の下の方に記述あり
      • -af はオーディオフィルター指定用のオプション。
      • -filter_complex は画像、音両方にフィルターかけれるオプション。この例では利用してない
    • split [main][tmp] : 元の動画の動画のストリームから、main と tmp という2つのストリームを作成
    • [tmp] crop=iw:ih/2:0:0, vflip [flip] : さきほどsplitで分裂した片方のストリームから、上半分を切り取り(crop)、vflipで上下反転処理させ、結果をflipという名称のストリームに流し込む
    • [main][flip] overlay=0:H/2 : 元の画像に、上で作成した上半分の反転画像を下半分に重ね書き
  • 今回の例では、入力の動画のストリームが一つしかないので、入力ストリームの記述が省略されている。ffmpeg -iで調べたストリームの番号を利用出来る。記述方法が柔軟で省略も出来るため色々な記述方法があるが、説明がややこしくなるので、ここでは省略

13.5 この章のまとめ

  • 公式文書のフィルターの紹介の例の最初をやってみた。これにより、フィルター記述の基礎を理解。
  • この例で出てきた、各種フィルターを公式文書で確認した。
    • split
    • crop
    • vflip
    • overlay

14 2つの音を、時間をずらして合成

  • 動画の音声を加工してYoutubeにアップロードしたいと考えた場合。この処理が必要

14.1 関係文書

14.2 以下の操作を行っている動画


14.3 2つの音を、オフセットを指定して合成 する手順

14.3.1 元ファイル1 silence03.wav の作成

  • SoXというツールで作成
  • 無音の3秒の silence03.wav 作成
sox -n silence03.wav synth 3 sine vol 0

14.3.2 元ファイル2 sine440_1.wav の作成

  • 1秒間440Hzのサイン波の sine440_1.wav 作成
sox -n sine440_1.wav synth 1 sine 440

14.3.3 音をずらして合成

  • sine440_1.wav を1秒ずらして合成し、出力ファイルを out.wav というファイルに出力
ffmpeg -i silence03.wav -i sine440_1.wav -filter_complex "[1] adelay=1000 [d];[0][d]amix" out.wav

14.4 この章のまとめ

  • 前の章のやり方を元に、2つの音を、時間をずらして合成してみた。
  • -filter_complex を使ってみた

15 動画から複数の画像に分離

15.1 関係文書

15.2 以下の操作を行っている動画


15.3 動画から複数の画像に分離 する手順

ffmpeg -ss 00:10 -to 00:11 -i test.mp4 test_images%05d.png
  • オプションの意味
    • -ssオプションは開始時間指定 今回は10秒
    • -toオプションは終了時間指定 今回は11秒
    • -i test.mp4 オプションは入力ファイルの指定。test_images00001.png,test_images00002.png….で作成される。
    • test_images%05d.png は出力ファイル名。 test_images

15.4 この章のまとめ

  • 動画から複数の画像に分離してみた。

16 複数の静止画像から動画作成

16.1 関係文書

16.2 以下の操作を行っている動画


16.3 複数の静止画像から指定長さの動画作成 する手順

  • 元となる画像は、一つ前の章で作成したものを利用
  • これで作る画像ではなく、別の画像を用意いただいてもOK
ffmpeg -framerate 56.2 -i test_images%05d.png -c:v libx264 -vf fps=56.2 -pix_fmt yuv420p  test_images.mp4
  • オプションの意味
  • 入力と出力のフレームレートが同じなら以下でもOK(今回のケースなら下でいける)
ffmpeg -framerate 56.2 -i test_images%05d.png -c:v libx264 -pix_fmt yuv420p  test_images.mp4

16.4 この章のまとめ

  • 複数の静止画を元に指定時間の動画を作成してみた。

17 1枚の静止画像から指定長さの動画作成

17.1 関係文書

17.2 以下の操作を行っている動画


17.3 1枚の静止画像から指定長さの動画作成 する手順

17.3.1 ベースとなる画像作成

convert -size 720x1280 xc:red -fill blue -stroke green -strokewidth 5  -draw "roundrectangle 50,50 670,670 30,30" my01.png

17.3.2 この画像の指定長さの動画作成

  • https://trac.ffmpeg.org/wiki/Slideshow の Single image にある手順で作成
  • 元画像のファイル名を my01.png, 出力ファイル名を my01.mp4 に
  • 作成する動画の長さは3秒に
ffmpeg -loop 1 -i my01.png -c:v libx264 -t 3 -pix_fmt yuv420p my01.mp4
  • オプションの意味
    • -loop 1 で入力画像の無限入力を指示しています。
    • -i my01.pngで、入力画像の指示をおこなっています。
    • -c:v libx264 はエンコーダーの指示
    • -t 3 で時間指定
    • -pix_fmt yuv420p はピクセルフォーマットの指示
    • my01.mp4 生成する動画ファイル名を与えています。

17.4 この章のまとめ

  • 1枚の静止画を元に指定時間の動画を作成してみた。

18 1枚の静止画像から用意した音ファイルと同じ長さの動画作成

18.1 関係文書

18.2 以下の操作を行っている動画


18.3 1枚の静止画像から用意した音ファイルと同じ長さの動画作成 する手順

  • 画像は一つ前の章で利用したものを利用 my01.png
  • 音楽は Akatsuki JAPAN (ファイル名akatsuki-japan.mp3 http://shw.in/ から入手可能) を利用させていただきます。
  • 他の音ファイルでもOK。その場合は利用する音楽ファイル名にしてください。
  • 出力ファイル名を my01.mp4 に
  • 作成する動画の長さは音楽にあわせる
  • 最初の方はaacコーデックでエンコード、後の命令の場合は、音はそのままのコーデックで流し込む(無劣化)
ffmpeg -loop 1 -i my01.png -i akatsuki-japan.mp3 -c:v libx264 -pix_fmt yuv420p -c:a aac -b:a 192k -shortest my01.mp4
or
ffmpeg -loop 1 -i my01.png -i akatsuki-japan.mp3 -c:v libx264 -pix_fmt yuv420p -c:a copy -shortest my01.mp4
  • オプションの意味
    • -loop 1 で入力画像の無限入力を指示しています。
    • -i my01.pngで、入力画像の指示をおこなっています。
    • -i akatsuki-japan.mp3で、音声をながしこんでます。
    • -c:v libx264 は動画のエンコーダーの指示
    • -pix_fmt yuv420p はピクセルフォーマットの指示
    • -c:a aac は音のエンコーダーの指示
    • -b:a 192k は音のビッツ/秒の指定
    • – shortest は入力の一番短いものに出力をあわせるオプション,これをつけないと、無限に大きなファイルになってく(Finish encoding when the shortest input stream ends.)
    • my01.mp4 生成する動画ファイル名を与えています。

最後の my01.mp4 生成する動画ファイル名を与えています。

18.4 この章のまとめ

  • 1枚の静止画を元に、音楽の長さにあわせた動画を作成してみた。

19 同じ動画をN回繰り返す動画の作成

19.1 関係文書

19.2 以下の操作を行っている動画


19.3 同じ動画をN回繰り返す動画の作成 する手順

ffmpeg -stream_loop N-1 -i test.mp4 -c copy testN.mp4
  • オプションの意味
    • -stream_loop N-1 で入力の繰り返す回数指定、0で1回1で2回….N-1でN回
    • -i test.mp4で、入力画像の指示をおこなっています。
    • -c copy : ストリームはコピー
    • testN.mp4 生成する動画ファイル名を与えています。
  • 2回繰り返す場合は以下のコマンドでOK
ffmpeg -stream_loop 1 -i test.mp4 -c copy test2.mp4
  • オプションの意味
    • -stream_loop 1 で入力の繰り返す回数指定、0で1回1で2回….N-1でN回
    • -i test.mp4で、入力画像の指示をおこなっています。
    • -c copy : ストリームはコピー
    • test2.mp4 生成する動画ファイル名を与えています。

19.4 この章のまとめ

  • 同じ動画をN回繰り返す動画の作成してみた

20 バックグラウンドミュージックをループで無音動画に追加

  • 私の動画は現状この必要ないけど、やりたくなった時用にやってみた。

20.1 関係文書

20.2 以下の操作を行っている動画


20.3 バックグラウンドミュージックをループで動画に追加 する手順

20.3.1 長い、音無しの動画作成

  • 動画で利用している動画は長さが20秒なので、20回繰り返して、400秒の無音動画を作成します。
ffmpeg -stream_loop 19 -i test.mp4 -an -c copy test20.mp4
  • オプションの意味
    • -stream_loop 19 : (20回入力を繰り返す指示)
    • -i test.mp4 : 入力ファイルを test.mp4 に指定
    • -an : オーディオのストリームは流さない
    • -c copy : 画像は無劣化で出力
    • test20.mp4 : 出力ファイル名

20.3.2 用意した無料で利用可能な音楽ファイルを追加

  • 今回は Akatsuki JAPAN (ファイル名akatsuki-japan.mp3 http://shw.in/ から入手可能) を利用していきます
  • 他のお好みの音楽で試してOK
ffmpeg -i test20.mp4 -stream_loop -1 -i akatsuki-japan.mp3 -c copy -shortest test20_with_audio.mp4
  • オプションの意味
    • -i test20.mp4 : 入力ファイルを test20.mp4 に指定
    • -stream_loop -1 -i akatsuki-japan.mp3 : 無限繰り返しで akatsuki-japan.mp3 を入力
    • -c copy : 音も、画像も無変換
    • -shortest : これをつけないと、無限に大きなファイルになってく(Finish encoding when the shortest input stream ends.)
    • test20_with_audio.mp4 : 出力ファイル名

20.4 この章のまとめ

  • バックグラウンドミュージックをループで無音動画に追加

21 バックグラウンドミュージックをボリューム調節して、ループで音あり動画に追加

  • 私の動画は現状この必要ないけど、やりたくなった時用にやってみた。

21.1 関係文書

21.2 以下の操作を行っている動画


21.3 バックグラウンドミュージックをループで動画に追加 する手順

21.3.1 長い、音あり動画作成

  • 動画で利用している動画は長さが20秒なので、20回繰り返して、400秒の動画を作成します。
ffmpeg -stream_loop 19 -i test.mp4 -c copy test_20.mp4
  • オプションの意味
    • -stream_loop 19 : (20回入力を繰り返す指示)
    • -i test.mp4 : 入力ファイルを test.mp4 に指定
    • -c copy : 画像は無劣化で出力
    • test_20.mp4 : 出力ファイル名

21.3.2 用意した無料で利用可能な音楽ファイルを追加

  • 今回は Akatsuki JAPAN (ファイル名akatsuki-japan.mp3 http://shw.in/ から入手可能) を利用していきます
  • 他のお好みの音楽で試してOK
ffmpeg -i test_20.mp4 -stream_loop -1 -i akatsuki-japan.mp3 -filter_complex "[1] volume=0.3 [bgm];[0][bgm] amix" -c:v copy -c:a aac -shortest test_20_with_bgm.mp4
  • オプションの意味
    • -i test_20.mp4 : 入力ファイルを test_20.mp4 に指定
    • -stream_loop -1 -i akatsuki-japan.mp3 : 無限繰り返しで akatsuki-japan.mp3 を入力
    • -filter_complex :フィルター処理
      • [1] volume=0.3 [bgm] : 2番目のストリームここでは akatsuki-japan.mp3 のボリュームを下げたものをbgmと名付けたストリームに流す https://ffmpeg.org/ffmpeg-filters.html#volume
      • [0][bgm] amix : 1番目のストリームと、bgmストリームの音を合成
    • -c:v copy : 画像も無変換
    • -c:c aac : 音はaacで変換
    • -shortest : これをつけないと、無限に大きなファイルになってく(Finish encoding when the shortest input stream ends.)
    • test_20_with_bgm.mp4 : 出力ファイル名

21.4 この章のまとめ

  • バックグラウンドミュージックをボリューム調節して、ループで音あり動画に追加してみた。

22 画像サイズを同じ比率で縮小

  • 最近良く使ってるテク
  • 動画サイズをガンと減らせます

22.1 関係文書

22.2 以下の操作を行っている動画


22.3 画像サイズを同じ比率で縮小 する方法

  • 縦を360にするケース
    • エラーになる場合数値を変更すると上手くいくことがあります。(例:360が駄目なら300とか)
ffmpeg -i 入力動画 -vf scale=-1:360 出力動画
  • 音はコピーするなら
ffmpeg -i 入力動画 -vf scale=-1:360 -c:a copy 出力動画
  • 横幅を指定したいなら(例800)
ffmpeg -i 入力動画 -vf scale=800:-1 -c:a copy 出力動画
  • 動画では以下でやってます。
    • 入力動画ファイル in.mp4
    • 出力動画ファイル out.mp4
  • -c:a copy はオーディオのストリームをコピーするというオプション
ffmpeg -i in.mp4 -vf scale=-1:360 -c:a copy out.mp4

22.4 この章のまとめ

  • 画像サイズを同じ比率で縮小する方法を紹介しました。

23 動画を範囲指定で切り抜き

  • 最近良く使ってるテク
  • 動画サイズを減らせます

23.1 関係文書

23.2 以下の操作を行っている動画


23.3 動画を範囲指定で切り抜き する方法

  • 切り抜く四角の左上の座標が(x0,y0)
  • 切り抜く四角幅をwidth,高さがheight
  • -c:a copy はオーディオのストリームをコピーするというオプション
ffmpeg -i 入力動画 -vf crop=width:height:x0:y0 -c:a copy 出力動画
  • 動画では以下でやってます。
    • 切り抜く四角の左上の座標(10,5)
    • 切り抜く四角の幅100,高さ50
ffmpeg -i in.mp4 -vf crop=100:50:10:5 -c:a copy out.mp4

23.4 この章のまとめ

  • 画像を範囲指定で切り抜き方法の紹介

24 出力動画のフレームレート(FPS)を指定する

  • 最近良く使ってるテク
  • 複数の作り方の異る動画の結合の時に、フレームレートをあわせないと上手くいかなかったので、このテク使ってます。

24.1 関係文書

24.2 以下の操作を行っている動画


24.3 FPSの指定

  • 出力FPSの指定を24にする場合
  • -c:a copy はオーディオのストリームをコピーするというオプション
ffmpeg -i 入力動画 -r 24 -c:a copy 出力動画
  • 動画で行なっている例の命令
ffmpeg -i in.mp4 -r 24 -c:a copy out.mp4
  • 静止画像と、音声から動画を作成する時には(出力FPSが24の時)
ffmpeg -i 入力画像 -i 音声ファイル -r 24 出力動画
  • 動画で行なっている例の命令
ffmpeg -i in.png -i in.wav -r 24 out.mp4

24.4 この章のまとめ

  • 出力動画のフレームレートを指定する方法の紹介

25 早送りorスロー化

  • 最近良く使ってるテク

25.1 関係文書

25.2 以下の操作を行っている動画


25.3 早送りorスロー化のやりかた

25.3.1 x倍速の場合(xは0.5から2の場合)

  • 入力ファイルがin.mp4、出力ファイルがout.mp4、1.5倍速の場合
  • vfオプションのsetptsを利用 https://ffmpeg.org/ffmpeg-filters.html#setpts_002c-asetpts
  • afオプションのatempoを利用 https://ffmpeg.org/ffmpeg-filters.html#atempo
    • 0.5〜100.0の範囲。 テンポが2を超えると、一部のサンプルがブレンドされるのではなくスキップされる。これらを避けるには、atempoの複数のインスタンスをデイジーチェーン接続する(ex.3倍ならatempo=sqrt\(3\),atempo=sqrt\(3\))
ffmpeg -i 'in.mp4' -vf setpts=PTS/x -af atempo=x 'out.mp4'

25.3.2 1.5倍速の場合

ffmpeg -i 'in.mp4' -vf setpts=PTS/1.5 -af atempo=1.5 'out.mp4'

25.3.3 2.1倍速の場合

ffmpeg -i 'in.mp4' -vf setpts=PTS/2.1 -af atempo=sqrt\(2.1\),atempo=sqrt\(2.1\) 'out.mp4'

25.4 この章のまとめ

  • 早送りorスロー化のやり方を紹介

26 動画の一部のみを切り出して早送りorスロー

26.1 関係文書

26.2 以下の操作を行っている動画


26.3 動画の一部のみを切り出して早送りorスロー

26.3.1 切り出し部分の開始時間 t1, 切り出し部分の終了時間が t2, x倍速の場合(xは0.5から2の場合)

  • 入力ファイルがin.mp4、出力ファイルがout.mp4、1.5倍速の場合 ( -i 入力ファイルの前にssとtoオプションで指定。後ろにすると変速した動画の切り出し時間の指定となる)
  • 切り出し部分の開始時間 t1, 切り出し部分の終了時間が t2
  • vfオプションのsetptsを利用 https://ffmpeg.org/ffmpeg-filters.html#setpts_002c-asetpts
  • afオプションのatempoを利用 https://ffmpeg.org/ffmpeg-filters.html#atempo
    • 0.5〜100.0の範囲。 テンポが2を超えると、一部のサンプルがブレンドされるのではなくスキップされる。これらを避けるには、atempoの複数のインスタンスをデイジーチェーン接続する(ex.3倍ならatempo=sqrt(3),atempo=sqrt(3))
ffmpeg -ss t1 -to t2 -i 'in.mp4' -vf setpts=PTS/x -af atempo=x 'out.mp4'

26.3.2 10秒から15秒を1.6倍速化の場合

ffmpeg -ss 10 -to 15 -i 'in.mp4' -vf setpts=PTS/1.6 -af atempo=1.6 'out.mp4'

26.4 この章のまとめ

  • 前の章の一部切り出して、再生速度変更する方法を紹介

27 動画の一部のみを切り出して画像サイズ変更して、さらに早送りorスロー

27.1 関係文書

27.2 以下の操作を行っている動画


27.3 動画の一部のみを切り出して画像サイズ変更して、さらに早送りorスロー

27.3.1 切り出し部分の開始時間 t1, 切り出し部分の終了時間が t2, x倍速の場合(xは0.5から2の場合), 新しい画像の比率を同じで幅をwに変更する場合

  • 入力ファイルがin.mp4、出力ファイルがout.mp4、1.5倍速の場合 ( -i 入力ファイルの前にssとtoオプションで指定。後ろにすると変速した動画の切り出し時間の指定となる)
  • 切り出し部分の開始時間 t1, 切り出し部分の終了時間が t2
  • vfオプションのsetptsを利用 https://ffmpeg.org/ffmpeg-filters.html#setpts_002c-asetpts
  • afオプションのatempoを利用 https://ffmpeg.org/ffmpeg-filters.html#atempo
    • 0.5〜100.0の範囲。 テンポが2を超えると、一部のサンプルがブレンドされるのではなくスキップされる。これらを避けるには、atempoの複数のインスタンスをデイジーチェーン接続する(ex.3倍ならatempo=sqrt(3),atempo=sqrt(3))
  • 画像のサイズは、元画像の幅や高さによっては設定出来ない場合がある。エラーがでたら、数値を変更して対応すればエラーを回避できます。
ffmpeg -ss t1 -to t2 -i 'in.mp4' -vf scale=-1:h,setpts=PTS/x -af atempo=x 'out.mp4'

27.3.2 10秒から15秒を1.6倍速化,画像の高さを300の場合

ffmpeg -ss 10 -to 15 -i 'in.mp4' -vf scale=-1:300,setpts=PTS/1.6 -af atempo=1.6 'out.mp4'

27.4 この章のまとめ

  • 前の章の一部切り出して、再生速度変更に加えて、画像のサイズも同時に変更する方法を紹介

28 今後

  • 今後も文書追加していきます。

29 この文書のチェンジログ

  • 2021/01/12 初版
  • 2021/01/15 複数の同じエンコード動画の無劣化結合 の章追加
  • 2021/01/16 音量調節 の章追加
  • 2021/01/22 動画から複数の画像に分離, 複数の静止画像から動画作成, 1枚の静止画像から指定長さの動画作成, 1枚の静止画像から用意した音ファイルと同じ長さの動画作成, 同じ動画をN回繰り返す動画の作成, バックグラウンドミュージックをループで動画に追加 の章追加
  • 2021/01/23 バックグラウンドミュージックをボリューム調節して、ループで音あり動画に追加 の章追加
  • 2021/10/31 画像サイズを同じ比率で縮小 の章, 画像を範囲指定で切り抜き の章, 出力動画のフレームレート(FPS)を指定する の章 を追加
  • 2022/01/14 早送りorスロー化, 動画の一部のみを切り出して早送りorスロー, 動画の一部のみを切り出して画像サイズ変更して、さらに早送りorスローの章追加

著者: NM Max

Created: 2022-01-19 水 01:33

Validate