« 2014年4月 | トップページ | 2014年6月 »

2014年5月

2014年5月31日 (土)

crypto++をVS2013 Expressでビルドする

Qt5.3はVS2013のnativeでコンパイル出来るので、crypto++もVS2013で作ります。VC2012対応の".sln"ファイルがあるので、多分大丈夫です。

VS2013 Express for Windows Desktopしか持っていないので、早速起動します。"cryptest.sln"をメニューから開くか、エクスプローラからダブルクリックします。時々Microsoft Accountを入れろと言われるのが鬱陶しいのですが、無料版なので仕方ないですね。

読み込み時にVS2013用のソリューションに設定ファイルを変換するよ、と言われてしばらく待ちます。VS2012->VS2013なので、大変ではなさそうですが、やっぱりバージョンの違いはあるので仕方ないですね。

変換が終了すると、webブラウザが立ち上がり、変換結果が表示されます。エラーはないようですが、なんかwarningはたくさん出てます。どうしますかね…

Crypto_sln_conv_report

UpgradeLog.htmというファイル名で残っているので、後で検討します。
しかし、暗号化ライブラリをイチかバチかでビルドするのは、まずいかもしれませんね(^-^;

ですか、とりあえず…「ソリューションのビルド」を実行しました。

Win32というフォルダが出来ていて、さらにその下に幾つかフォルダが出来ていて、ダイナミックリンクライブラリが出来てますね。

んんんんん…

「Relese」にして、リリースビルドもやってみよう…

出来た、みたいです。

gccでもclangでもVS2013でもビルドできるのは、やはり丁寧にコーディングされているからなんでしょうね。

public domainなので、ソースツリーに取り込ませていただくかどうかも検討したいですね。

(追記)

Backupフォルダの下にオリジナルのソリューションファイルが保存されています。
もともとVS2005用のソリューションファイルでした。

$ diff -u Backup/cryptest.sln cryptest.sln > cryptest.sln.diff

した結果が、以下です。Convertした後のファイルを残したほうがいいですかねぇ。

--- Backup/cryptest.sln 2010-08-06 17:44:26.000000000 +0900
+++ cryptest.sln 2014-05-31 21:39:24.822578400 +0900
@@ -1,21 +1,14 @@
-Microsoft Visual Studio Solution File, Format Version 9.00
-# Visual Studio 2005
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cryptdll", "cryptdll.vcproj", "{EBD86293-69A9-456B-B814-916E12AA9BBF}"
- ProjectSection(ProjectDependencies) = postProject
- {9EAFA456-89B4-4879-AD4F-C2C341184CF5} = {9EAFA456-89B4-4879-AD4F-C2C341184CF5}
- EndProjectSection
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Express 2013 for Windows Desktop
+VisualStudioVersion = 12.0.30110.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cryptopp", "cryptdll.vcxproj", "{EBD86293-69A9-456B-B814-916E12AA9BBF}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cryptest", "cryptest.vcproj", "{9EAFA456-89B4-4879-AD4F-C2C341184CF5}"
- ProjectSection(ProjectDependencies) = postProject
- {3423EC9A-52E4-4A4D-9753-EDEBC38785EF} = {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}
- EndProjectSection
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cryptest", "cryptest.vcxproj", "{9EAFA456-89B4-4879-AD4F-C2C341184CF5}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cryptlib", "cryptlib.vcproj", "{3423EC9A-52E4-4A4D-9753-EDEBC38785EF}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cryptlib", "cryptlib.vcxproj", "{3423EC9A-52E4-4A4D-9753-EDEBC38785EF}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dlltest", "dlltest.vcproj", "{A7483CE8-2784-46CE-8CB8-8C0C1D27E232}"
- ProjectSection(ProjectDependencies) = postProject
- {EBD86293-69A9-456B-B814-916E12AA9BBF} = {EBD86293-69A9-456B-B814-916E12AA9BBF}
- EndProjectSection
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dlltest", "dlltest.vcxproj", "{A7483CE8-2784-46CE-8CB8-8C0C1D27E232}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

暗号化についても考えてみる その4

crypto++ の続きですが…

cryptlib.hを利用するために、中身を確認しました。

今まで私が書いてきたC++が以下に稚拙かということを思い知らされますね。C言語の概念が抜け切らないのがもろバレです。公開止めようかな…

crypto++のようなライブラリは不特定多数の人が利用することを想定しているので、やっぱりちゃんと書いておかなければならないですよね。似非C++ではあまり手本にならないな…

いい機会だと思って、リファクタリングしますかね…こんなことしてたら、いつまでたっても公開できない気がしてきました┐(´д`)┌ヤレヤレ

思いつく所は

1. Doxgen用コメント(もともとコメント自体少ないのですが)
2. 昔のC++コンパイラへの対応(そーんなに神経質になる必要はないかもしれませんが)
3. 定数値へのマクロ利用
4. 例外の積極的な利用
5. クラス定義への関数実装(inline展開が期待できるのかな)
6. 派生を意識したクラス構成

はぁ…、疲れた。

Doxygenで生成されるドキュメントを読みたいと思います。まず、…Doxgenを使ったドキュメントファイルの作成方法から、勉強しなくてはなりません。気が遠くなるーぅ。

とりあえず、Readme.txtには以下のようにあります。ホームページでも見られるみたいですが、一応勉強のため、生成方法とかやってみますかねぇ。どうせ、コメント入れる、かもしれないし。

*** Documentation and Support ***

Crypto++ is documented through inline comments in header files, which are
processed through Doxygen to produce an HTML reference manual. You can find
a link to the manual from http://www.cryptopp.com. Also at that site is
the Crypto++ FAQ, which you should browse through before attempting to
use this library, because it will likely answer many of questions that
may come up.

どんどん、横道へそれてゆく…

今考え中なことをまとめると、

1. Linuxにおける画像と音声のズレをどうにかする

2. 設定ダイアログの追加

(1) Qt DesignerでのGUI構築

(2) Qt Designerで生成した設定Dialog用のソースコードの取り込み

3. パスワードの暗号化

(1) crypto++ のドキュメントを読む

(2) cryptlib.hをincludeして、実際に暗号化/復号化を実装する

4. ソースコードのリファクタリングをする

公開なんて、大げさなことを言うのはやめます…個人勉強プロジェクトだし(^-^;

2014年5月30日 (金)

キー入力とマウス

#また横道に逸れますが…

Brynhildrサーバに、デバイスの入力(PCだと主にキーボード、マウスですね)を連絡する必要がありますが、その通信内容、手順の詳細は問い合わせ中なので、そこで必要になるであろう情報について考えました。

キーボードだと、どのキーを押したかですね。これは遥か昔に書いたのですけど、イベントという形で伝わります。

Keyを押した時: void keyPressEvent(QKeyEvent *event)

Keyを離した時: void keyReleaseEvent(QKeyEvent *event)

がそれぞれ呼ばれます。引数eventに押されたキーなどの情報が入っていますので、これから情報を取得することになります。Qt AssistantでQKeyEventを検索してみます。

QKeyEvent::key()というメンバ関数でどのキーか分かるようです。返ってくる値はQt::Key_????というような値だそうです。例えば、右カーソルキーなら、Qt::Key_Right, '+'ならQt::Key_Plusです。Qt AssistantのQKeyEventのメンバ関数key()の記述の近くにあるQt::Keyというリンクをクリックすると一覧が見られます。enum Qt::Keyと定義されているようです。例えば、こんな感じです。プログラム中では、Qt::Key_????を使うことになります。

	 
Qt::Key_SysReq	0x0100000a	 
Qt::Key_Clear	0x0100000b	 
Qt::Key_Home	0x01000010	 
Qt::Key_End	0x01000011	 
Qt::Key_Left	0x01000012	 
Qt::Key_Up	0x01000013	 
Qt::Key_Right	0x01000014	 
Qt::Key_Down	0x01000015	 

Qt NamespaceでAt Assinstantを検索すると、すべてのQtの名前空間での定数値を調べられます。
これらのキー情報を、Brynhildrサーバさんが解釈できる情報に変換してあげる必要があります、たぶん。
Brynhildrサーバさんは、Qtのキー情報を知っているはずがないのでこの値を送っても、知らないよって言われます(^_^;

次は、マウスの情報です。マウスの情報といえば、とりあえず思いつくところでは、ウィンドウ内の位置(x,y)、押されたボタン(右、左)、離されたボタン(右、左)、くらいしか思いつかないのですが、早速Qt Assistantで調べてみます。

なんか、こんな感じの関数が呼ばれるようです。

マウスボタンを押した時 void mousePressEvent(QMouseEvent *event)

マウスボタンを離した時 void mouseReleaseEvent(QMouseEvent *event)

マウスを動かした時 void mouseMoveEvent(QMouseEvent *event)

他にあります。

基本的にはQMouseEventというものとして、情報が渡されるようです。# QWheelEventというものもあるそうですが。
検索して、メンバー関数をさらっと眺めるといろいろと情報が渡ってくるのが分かります。

ウィンドウ内の位置(x,y)、デスクトップ内の位置(x,y)、どのボタン、といった情報のようです。

恐らくBrynhildrサーバさんから送られてきたデスクトップのJPEG画像の中で、今どのウィンドウにフォーカスがあるのか、とかを判断しなくてはならないので、JPEGで表示したデスクトップ画像のウィンドウ内のマウスカーソルの位置とかが必要になってくるのかもしれません。まだ、憶測の域を超えませんが、なんとなく必要な情報は渡せそうな気がしてきました。

QKeyEvent, QMouseEvent, QWheelEventを必要になったら調べることになりそうです。とりあえず、目処は付いた気になったので、この辺りで元に戻ろうかと…思います。

暗号化についても考えてみる その3

Crypto++ Library 5.6.2をダウンロードしました。

http://www.cryptopp.com/cryptopp562.zip

zipファイルなので、なんでもいいんですが、unzipコマンドで展開します。引数を与えずにunzipするとカレントディレクトリにわらわらと沢山ファイルが出現するので、びっくりします。-dオプションで展開するディレクトリを指定します。

$ unzip cryptopp562.zip -d cryptopp562

cryptopp562の下にファイルが出来上がります。テキストらしきファイルは

$ ls *.txt
License.txt Readme.txt

2つですね。Readme.txtを読んでみます。

まず、いろいろな特徴について書かれていてます。暗号化アルゴリズムとか。

なにやら、「どんな目的にもただで使ってもいいよ。でも、License.txtは読んでね」らしいです。

You are welcome to use it for any purpose without paying me, but see License.txt for the fine print.

サポートするコンパイラ及び環境、それぞれに関する注意みたいなこと、最後にバージョンアップ内容の履歴です。

Lincense.txtには、開発に貢献した人とそのソースのリスト、最後にこのライブラリのライセンスについて記述されています。

まず、ライブラリを同梱もしくは静的にリンクすることに問題はなさそうです。では、MinGW-4.8.2用に作りましょう。

VC用のプロジェクトファイルとかTurboC++用のプロジェクトファイルがありますが、今回使うのは、GNUmakefileだけです。このファイルを自分の環境に合わせて、チョコチョコと直してmakeすればいいのではないでしょうかね。

イチかバチかでmakeって実行してみましょうかね( ̄ー ̄)ニヤリ

cygwinのmakeを…とここまで書いて気づきました。cygwinのmakeもoMinGWのmakeも同じver 4でした。つまり、mingw32-make.exeをalias しなくてもcygwinのmakeを使えるのでは?

と思いましたが、ダメでした。cygwinのmake(4.0)とmingw32-make.exe(4.0.90)は違うようです。4.0で変更したけど、結局戻したものが4.0.90なのかもしれません…ダメか。

気を取り直して、crypto++をmakeしてみました。

5分後、ライブラリが出来てしまいました。スタティックリンク用のライブラリです。静的にくっついちゃうライブラリなので、別途ダイナミックリンクライブラリを入れる必要がないのでこれでいいです。

大丈夫なのだろうか、これで…

2種類の警告が出ていました。

1つめ、"usingキーワードを使って、簡単にアクセスしたらいいんじゃない?"という提案みたいです。
「ほっとけ」、と思うのですが気を利かせてご指摘くださっています。
GNUmakefileで-Wdeprecatedが指定されているためですね。これを削除すれば、警告が出なくなるかもしれませんが、ま、理由がわかっていれば、指摘してくれる分には問題ないので、そのままにします。

winpipes.h:115:2: warning: access declarations are deprecated in favour of using-declarations; suggestion: add the 'using' keyword [-Wdeprecated]
  NetworkSource::GetMaxWaitObjectCount;

2つめは、defineマクロの2重定義でした。Windowsのバージョンを示すマクロみたいですね。crypto++側は0x0400、MinGW側は0x502です。「redefined [enabled by default]」とあるので、再定義はO.K.のようです。
ということで0x400が今は有効になっているようです。

fipstest.cpp:11:0: warning: "_WIN32_WINNT" redefined [enabled by default]
 #define _WIN32_WINNT 0x0400
 ^
In file included from C:/Tools/develop/Qt/Qt5.3.0/Tools/mingw482_32/i686-w64-mingw32/include/crtdefs.h:10:0,
                 from C:/Tools/develop/Qt/Qt5.3.0/Tools/mingw482_32/i686-w64-mingw32/include/stddef.h:7,
                 from C:/Tools/develop/Qt/Qt5.3.0/Tools/mingw482_32/lib/gcc/i686-w64-mingw32/4.8.2/include/stddef.h:1,
                 from stdcpp.h:9,
                 from cryptlib.h:83,
                 from seckey.h:8,
                 from rijndael.h:7,
                 from aes.h:4,
                 from dll.h:11,
                 from fipstest.cpp:8:
C:/Tools/develop/Qt/Qt5.3.0/Tools/mingw482_32/i686-w64-mingw32/include/_mingw.h:229:0: note: this is the location of the previous definition
 #define _WIN32_WINNT 0x502
 ^

http://www.westbrook.jp/Tips/Win/OSVersion.htmlによると、0x400はWindows95らしいです。20年近く前ですが…

0x502は5.2でしょうから、Windows XP (64bit) Professionalみたいです。どちらにすべきでしょうか?

とりあえず、システム側であるMinGWの方を優先しましょう。なにかあったら、再ビルドすればいいですし。

fipstest.cppの11行目を"//"でコメントアウトしました。

で、再度makeを実行します。

というわけで、静的にリンクする用のライブラリ libcryptopp.a が出来上がりました。

cryptest.exeという実行ファイルが出来上がっていますので、ほんとにちゃんと出来てるのか気になる人はテストできます。

しかし、ライブラリは出来ましたが、使い方が分かりませんねぇ(^-^;
カンなんですが、cryptlib.hをインクルードすればいいんじゃないですかね、たぶん。

$ ./cryptest.exe
Test Driver for Crypto++(R) Library, a C++ Class Library of Cryptographic Schemes
- To generate an RSA key
        cryptest g
- To encrypt and decrypt a string using RSA
        cryptest r
- To sign a file using RSA
        cryptest rs privatekeyfile messagefile signaturefile
- To verify a signature of a file using RSA
        cryptest rv publickeyfile messagefile signaturefile
- To digest a file using several hash functions in parallel
        cryptest m file
- To encrypt and decrypt a string using DES-EDE in CBC mode
        cryptest t
- To encrypt or decrypt a file
        cryptest e|d input output
- To secret share a file (shares will be named file.000, file.001, etc)
        cryptest ss threshold number-of-shares file
- To reconstruct a secret-shared file
        cryptest sr file share1 share2 [....]
        (number of shares given must be equal to threshold)
- To information disperse a file (shares will be named file.000, file.001, etc)
        cryptest id threshold number-of-shares file
- To reconstruct an information-dispersed file
        cryptest ir file share1 share2 [....]
        (number of shares given must be equal to threshold)
- To gzip a file
        cryptest z compression-level input output
- To gunzip a file
        cryptest u input output
- To encrypt a file with AES in CTR mode
        cryptest ae input output
- To base64 encode a file
        cryptest e64 input output
- To base64 decode a file
        cryptest d64 input output
- To hex encode a file
        cryptest e16 input output
- To hex decode a file
        cryptest d16 input output
- To forward a TCP connection
        cryptest ft source-port destination-host destination-port
- To run the FIPS 140-2 sample application
        cryptest fips
- To generate 100000 random files using FIPS Approved X.917 RNG
        cryptest fips-rand
- To run Maurer's randomness test on a file
        cryptest mt input
- To run a test script (available in TestVectors subdirectory)
        cryptest tv filename
- To run validation tests
        cryptest v
- To display version number
       cryptest V
- To run benchmarks
        cryptest b [time allocated for each benchmark in seconds] [frequency of CPU in gigahertz]

(追記)

Readme.txtの他の特徴(Other features)の記述が興味深いです。

暗号化/復号化アルゴリズム以外にいろいろな処理が提供されています。
乱数発生器、素数生成とvelification、gzip/zipの圧縮と展開、なんかは興味がありますね。
PCMデータを圧縮して保存とかできそうですね。

  * pseudo random number generators (PRNG): ANSI X9.17 appendix C, RandomPool
  * password based key derivation functions: PBKDF1 and PBKDF2 from PKCS #5,
    PBKDF from PKCS #12 appendix B
  * Shamir's secret sharing scheme and Rabin's information dispersal algorithm
    (IDA)
  * fast multi-precision integer (bignum) and polynomial operations
  * finite field arithmetics, including GF(p) and GF(2^n)
  * prime number generation and verification
  * useful non-cryptographic algorithms
      + DEFLATE (RFC 1951) compression/decompression with gzip (RFC 1952) and
        zlib (RFC 1950) format support
      + hex, base-32, and base-64 coding/decoding
      + 32-bit CRC and Adler32 checksum
  * class wrappers for these operating system features (optional):
      + high resolution timers on Windows, Unix, and Mac OS
      + Berkeley and Windows style sockets
      + Windows named pipes
      + /dev/random, /dev/urandom, /dev/srandom
      + Microsoft's CryptGenRandom on Windows
  * A high level interface for most of the above, using a filter/pipeline
    metaphor
  * benchmarks and validation testing
  * x86, x86-64 (x64), MMX, and SSE2 assembly code for the most commonly used
    algorithms, with run-time CPU feature detection and code selection
  * some versions are available in FIPS 140-2 validated form

暗号化についても考えてみる (TrueCrypt開発終了?)

あまり関係ありませんが、(暗合つながりで)「TrueCrypt開発終了」だそうです。

http://internet.watch.impress.co.jp/docs/news/20140530_651011.html

以下は個人的な意見です。

結構使っている人いたんですけどね、あまりにひどいバグあって自責の念に潰されそうになったのかなぁと思うと、なんだか同情してしまいます。詳細は分かりませんが。

使ってもらえると嬉しいのですが、責任問題となると、多くの人に使ってもらっていればいるだけ、おおごとになってしまいますね。責任は取れません、と明示してはいるのでしょうけど。

優秀なツールだっただけに、ちょっとショッキングな開発中止の流れに、残念な気持ちがします。

暗号化についても考えてみる その2

プラットフォームを確認しました。どんなコンパイラに対応しているかですが、

MSVC 6.0 - 2012
GCC 3.3 - 4.7
Clang 3.2
Intel C++ Compiler 11 - 13
Solaris Studio 12.3
C++Builder 2010 (for version 5.6.1)

だそうです。今使ってるのはgcc-4.8.2、これから使うかもしれないのはVC2013、最新の正式リリースClangは3.4、リポジトリを追っかけてる人は3.5ですが。それ以外は使わないかも。

最新の5.6.2をダウンロードします。ダウンロードしている間に、とwebには書いてあります。

・Take a look at the related links page. It includes links to crypto libraries for other languages, products that use Crypto++, etc.
・Consider the list of recommended books for Crypto++ users.
・Examine the Crypto++ license agreement.
・Read the Crypto++ User Guide.
・Browse the Crypto++ Reference Manual.
・View these Crypto++ class hierarchy charts to see how Crypto++ is organized. Note that these charts only include a small number of actual algorithms as examples.
 ・symmetric algorithms and hash functions
 ・public key algorithms

ダウンロードは一瞬で終わるのですが、その間にこれだけやれって難しくないですか(◎´∀`)ノ
というか無理。1MBのアーカイブを1時間かけて1200bpsとか2400bpsのモデムでダウンロードしていたパソコン通信の時代ならともかく…運が悪いと途中でエラーっと出てやり直し…

2014年5月28日 (水)

暗号化についても考えてみる

パスワードなんですが、今普通に見えます。レジストリエディタで開く、もしくはコンフィギュレーションファイルを表示すれば、生のパスワードが表示されます。

そのまま保存するのはマズイですよね。あまり大げさにしたくはないのですが、丸見えだと困ります。

UNIXな世界では、簡単な暗号化ならlibcryptというのがよく使われるそうです。

生のパスワードを引数で渡すと、暗号化した文字列を返してくれてたり、生のパスワードを引数で渡して、暗号化されて保存されている文字列と比較して正誤を判定してくれたりするライブラリというのが私のイメージです。

一から自分で作るよりは、このようなライブラリを利用したほうが楽で確実ですね。

libcryptは基本的にC言語用のライブラリです。今回はC++を使っているので、どうせならC++のライブラリの方がいいですよね。

C++用のフリーな暗号化ライブラリを公開されている方がいました。Debianとかでも採用されているようです。

Crypto++ というライブラリだそうです。
http://www.cryptopp.com/

早速ライセンスを調べますと、

「under the Boost Software License 1.0」

だそうです。

「Boost Software License 1.0」は以下にあります。
http://www.boost.org/LICENSE_1_0.txt

------------------------ ここから --------------------------------
Boost Software License - Version 1.0 - August 17th, 2003

Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
------------------------ ここまで --------------------------------

参考のために日本語訳されている方がいました。

http://hamigaki.sourceforge.jp/doc/html/license.html

なんとなくですが、使えそうですね。Debianではパッケージとして提供されているので、そのまま使えそうですが、Windowsとかは同梱した方がよさそうですね…

とりあえず、ブックマークしておいて、今度調べましょう。

Qt Designerを立ちあげてみる その4

やっぱり、Qt Designerです。

"setting_dialog.ui"ファイルをダブルクリックせずに、Qt Designerをスタートメニューから起動します。
で、「ファイル」メニューから読み込みました。

まず、作ったばかりのDialogを表示するために自動生成されるソースコード見てみましょう。

「フォーム」メニューから「コードを表示…」を選択します。

20140528_201732

コピペしました。これをどうにかして自分のプログラムに組み込むようですね。さて、その作法を勉強しなくてはなりません。

実装の入ったヘッダーファイルっぽいので、includeしてなんかこねくりまわすのでしょうねヽ(´▽`)/
/********************************************************************************
** Form generated from reading UI file 'setting_dialogRM6016.ui'
**
** Created by: Qt User Interface Compiler version 5.3.0
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/

#ifndef SETTING_DIALOGRM6016_H
#define SETTING_DIALOGRM6016_H

#include <QtCore/QVariant>
#include <QtWidgets/QAction>
#include <QtWidgets/QApplication>
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QDialog>
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QHeaderView>

QT_BEGIN_NAMESPACE

class Ui_Dialog
{
public:
    QDialogButtonBox *buttonBox;

    void setupUi(QDialog *Dialog)
    {
        if (Dialog->objectName().isEmpty())
            Dialog->setObjectName(QStringLiteral("Dialog"));
        Dialog->resize(400, 300);
        buttonBox = new QDialogButtonBox(Dialog);
        buttonBox->setObjectName(QStringLiteral("buttonBox"));
        buttonBox->setGeometry(QRect(30, 240, 341, 32));
        buttonBox->setOrientation(Qt::Horizontal);
        buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);

        retranslateUi(Dialog);
        QObject::connect(buttonBox, SIGNAL(accepted()), Dialog, SLOT(accept()));
        QObject::connect(buttonBox, SIGNAL(rejected()), Dialog, SLOT(reject()));

        QMetaObject::connectSlotsByName(Dialog);
    } // setupUi

    void retranslateUi(QDialog *Dialog)
    {
        Dialog->setWindowTitle(QApplication::translate("Dialog", "Dialog", 0));
    } // retranslateUi

};

namespace Ui {
    class Dialog: public Ui_Dialog {};
} // namespace Ui

QT_END_NAMESPACE

#endif // SETTING_DIALOGRM6016_H

Qt Designerを立ちあげてみる その3

立ち上げようと思い、保存した"setting_dialog.ui"をダブルクリックしました。

…Qt Creatorが立ち上がりました。「お!(o^-^o)」

もしかして、Qt Designerは時代遅れ(obsolete)ですか?

表題を「Qt Creatorを立ちあげてみる」に変更しなくてはならないではないですか!

20140528_193959

というわけで、「Qt Creatorを立ちあげてみる」に変更。

と、思ったのですが、「デバッグ」メニューとかありますけど…これって統合開発環境なの…もしかして。

Qt DesignerはGUIデザインツール、Qt CreatorはGUIデザインツールを含んだ統合開発環境なのですか?

こんなんで大丈夫なんでしょうか?(;´д`)トホホ…

Qt Creatorだとイベントの接続とかもGUIからできるような感じです。

できればGUI部品の構築だけQt Designerに任せて、本体との接続は自分でやりたい気分でございます。
まず、Qt Designerを勉強してみます。(迷走中)

Qt5.3に付属のMinGW-4.8.2は64bit対応したものでした…

(追記)

この記事は正確ではありませんでした、すみません。

MinGW-W64 projectの成果物に入れ替わっていますが、バージョン表記にもある通り
Qt5.3.xに入っているのは i686 posix thread の32bitコンパイラです。

MinGW-W64という記述を見て64bit対応版だと早とちりしてしまいました…

------------------------ ここから ------------------------------------

自分で書いておいて難ですが、Qt5.3に付属のMinGWは64bit対応されたものでした…

gcc version 4.8.2 (i686-posix-dwarf-rev3, Built by MinGW-W64 project)

ここに導入に関する記述がありました、他にもたくさんの方が書かれています。

http://www.geocities.jp/penguinitis2002/computer/programming/MinGW-w64.html

Qt5.2.1に付属のMinGWは 32bit版のMinGWのオリジナルのようです。

gcc version 4.8.0 (rev2, Built by MinGW-builds project)

ですので、普通にMinGWのページからダウンロードできるものは32bit版のgcc-4.8.1が最新のようです。今後は64ビット優先になるのかもしれません。

しかし、今の設定だと64bit版を作ることになりますね。32ビット版を作る時は、コンパイラを替えなければならないのでしょうか…また、悩みが増えそうです。32ビット対応を無理に無くす必要はないのですが、間違いなく、効率よく、すべての環境をテストするのが一手間かかりそうです。64bitオンリーになれば、よいのですが。

(追記)

mingw32-makeが4.0.90になってます。Qt5.3のqmakeで作ったMakefile.Relese/Makefile.Debugは4.0.90でも大丈夫なようになっていますので、qmakeを実行しなおして、Makefile.Release/Makefile.Debugを作り直します。

$ mmake -v
GNU Make 4.0.90
Built for Windows32
Copyright (C) 1988-2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Qt5.2.1に戻した場合にはqmakeを再度実行し直した方がよいです。

2014年5月27日 (火)

Qt Designerを立ちあげてみる その2

「Dialog with Buttons Bottom」を選んで、「作成」を押しました。このウィンドウは「ファイル(File)」メニューから「新規(New)」を選んでも表示できます。

Default_dialog

Window Titleとかデフォルトのサイズとかは後でどうにでもなりそうです。これをベースに部品をベタベタ貼っていけばよさそうです。

画面の部品は、左にある「ウィジェットボックス」というところにあります。この部品を上記のダイアログにドラッグ&ドロップすればよいのでしょうね。

Widget1_3

Widget2_2

なにか面白そうな部品があるでしょうか?よく見る部品は大抵揃っていそうです。

設定ダイアログでよくあるタブは「Tab Widget」を配置すれば良さそうです。さて、どういう風にドラッグ&ドロップしていけばいいのでしょうね。

とりあえず、それは明日にして、一旦終了します。多分保存するのですよね。

保存せずに終了しようとすると、「名前をつけてフォームを保存」というダイアログが出ます。適当な場所に保存します。本番ではないので、どこでもいいですね。"setting_dialog.ui"という名前で保存しました。これで明日再開できます。

参考のために、上記ファイルの内容を見てみます。いろいろ書いてありますが、それっぽい情報かなという想像がつきそうですね。

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Dialog</class>
 <widget class="QDialog" name="Dialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Dialog</string>
  </property>
  <widget class="QDialogButtonBox" name="buttonBox">
   <property name="geometry">
    <rect>
     <x>30</x>
     <y>240</y>
     <width>341</width>
     <height>32</height>
    </rect>
   </property>
   <property name="orientation">
    <enum>Qt::Horizontal</enum>
   </property>
   <property name="standardButtons">
    <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections>
  <connection>
   <sender>buttonBox</sender>
   <signal>accepted()</signal>
   <receiver>Dialog</receiver>
   <slot>accept()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>248</x>
     <y>254</y>
    </hint>
    <hint type="destinationlabel">
     <x>157</x>
     <y>274</y>
    </hint>
   </hints>
  </connection>
  <connection>
   <sender>buttonBox</sender>
   <signal>rejected()</signal>
   <receiver>Dialog</receiver>
   <slot>reject()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>316</x>
     <y>260</y>
    </hint>
    <hint type="destinationlabel">
     <x>286</x>
     <y>274</y>
    </hint>
   </hints>
  </connection>
 </connections>
</ui>

Qt Designerを立ちあげてみる

とりあえず、Qt Designerを立ちあげてみました。メニュー等も日本語化されています。

20140527_210318

初期設定だと、下のウィンドウが手前に出てきます。次回から出ないようには出来ますね。

20140527_210644

多分、設定(settins)ダイアログは「OK」、「CANCEL」が基本でしょうから、テンプレートは今フォーカスが当たっている「Dialog with Buttons Bottom」でO.K.でしょう。

しかし、少し遊んでみたいので今回は「キャンセル(CANCEL)」しました。で、「ヘルプ」メニューから「Qt について」を選択しました。

20140527_212736

なんか、日本語で表示されてますね。初めてみました。どうにかすれば、日本語された「Qtについて」ダイアログが表示できるのでしょうか? Qt Brynhildr(仮称)だと英語のままです。

気になります…ので、これも含めてちょっといじってみたいなぁと思います。


Qt5.3.0に付属のMinGW-4.8.2を使う

使っていたMinGWがQt5.3.0に付属のMinGWよりも古いことに気がつきました。今後はQt5.3.0に付属のMinGWを使うようにします。

私の場合は、PATHの

/c/MinGW/bin

/c/Tools/develop/Qt/Qt5.3.0/Tools/mingw482_32/bin

に書き換えました。

gcc version 4.8.2 (i686-posix-dwarf-rev3, Built by MinGW-W64 project)

mingw32-make.exeへのエイリアス(alias)も変更しました。

alias mmake='mingw32-make'

これまで通り、"mmake debug", "mmake release"でビルドできます。

Qtに付属のgcc(MinGW)の方が安全ではありますね。

(追記)
なぜか分かりませんが、普通にMinGWをインストールしても最新はgcc-4.8.1止まりですね。この4.8.2はどういう位置づけなのでしょうかね…。ちなみにcygwin x64のgccは4.8.2です。cygwinをupdateした時に"4.9.0"という文字が見えましたが、"Objective-C++"というコンパイラでした…Qt5.3のドキュメント読めば分かるのでしょうか。適当に想像してもマズイのですが(個人的意見というか知識です)。

あとcygwinで、インストールに使う"setup-x86_64.exe"が新しくなった時に"新しいのがあるよ"ってメッセージが出てくるのですが、"それも自動でダウンロードして継続してくれないかなぁ"、と妄想するのは私だけでしょうか。
もしかして、できるのかなぁ…

(追記2)
Qt5.3に含まれるMinGW-4.8.2はこれまでの32bit MinGWとは別物でした、嘘情報流して、すみません。
フォルダー名は"mingw482_32"となっていたので、32bitと思い込んでしまいました。"mingw482_64"なら気づけたのかも…しかし、"gcc -v"の出力結果をよく見ろ、と言われたら返す言葉もありません(;ω;)

進捗記録 2014.5.27

記録しておきます。

http://youtu.be/O_vq8hZhRkw

ボケが始まっている…設定ダイアログが必要な気がする

「とりあえず、あと必要なのは画像とサウンドの同期ですが」と嘘をついてました。

重要なものを忘れてました。

「設定」を変更するためのダイアログです。レジストリや.configの下に設定を保存したり、読み込んだりする仕組みがあるのですが、それをユーザが設定する手段がありません。(今はソースコードに決め打ちで書いてます。)

自信を持って書いたのですが、ボケが始まっています。

設定ダイアログのソースコードを手動で書いてもいいのですが、ここはやはりQt Designerを使うべきでしょうね。さて、午後からQt Designerでもいじって遊んでみましょうかね。なんかGUIを使ってウィンドウを作成して、C++のソースコードを自動生成してくれるそうです。あとは生成されたソースコードを使えるように合体させるそうですが、どうやるんでしょうか、まだ分かりません。

とりあえず、設定項目は「サーバ名」、「サーバータイプ」、「ポート番号」、「パスワード」、「画質選択」、「音質選択」、隠しで「グラフィックバッファのサイズ」、「サウンドバッファのサイズ」、「サウンドのキャプチャ方法選択」、デバッグ用に「デバッグログ出力の有無」、「転送されてきたJPEGファイルのファイル出力の有無」、「転送されてきた生のPCMデータのファイル出力の有無」って感じです。

2014年5月26日 (月)

進捗記録 2014.5.26

とりあえず、あと必要なのは画像とサウンドの同期ですが、ここで一区切り付けておきます。

日本語環境対応 デスクトップキャプチャ動画 [Windows 8.1 update]

日本語環境対応 デスクトップキャプチャ動画 [Linux Ubuntu 14.04 LTS 日本語Remix版]

いつになったらソースコードが公開できるのでしょうか(o^-^o)
Windows版を公開する日はいつかくるのでしょうかねぇ…
Mac版なんていつ…

多言語対応の準備をしましょうかね 最後

「6. Qtに含まれるlreleaseコマンドを使って多言語対応用のバイナリファイルを作る」です。

翻訳情報の入った.tsファイルが出来上がりましたので、これをバイナリファイルである.qmファイルに変換します。変換には"lrelease"コマンドを使います。

実行は以下の様な感じです。

$ lrelease -verbose brynhildr.pro
Updating 'D:/home/masaaki/Src/qtbrynhildr/src/translations/brynhildr_ja_JP.qm'...
Generated 10 translation(s) (8 finished and 2 unfinished)

「お、終わった」と思っていたら、2箇所完了マークを付けるのを忘れてました…もう一度 Linguistで編集しました。

$ lrelease -verbose brynhildr.pro
Updating 'D:/home/masaaki/Src/qtbrynhildr/src/translations/brynhildr_ja_JP.qm'...
Generated 10 translation(s) (10 finished and 0 unfinished)

今度は全部完了していたようです。translationディレクトリの下に.qmファイルが出来上がっていました。

$ ls -al translations/
合計 12
drwxrwxr-x+ 1 masaaki masaaki    0 5月  26 17:19 .
drwxrwxr-x+ 1 masaaki masaaki    0 5月  23 23:12 ..
-rwxrwxr-x  1 masaaki masaaki  844 5月  26 17:29 brynhildr_ja_JP.qm
-rwxrwxr-x  1 masaaki masaaki 3007 5月  26 17:27 brynhildr_ja_JP.ts

これで翻訳作業は完了です。

ビルド(make debug)して実行します。これで日本語化できました。ロシア語にしたかったら、ロシア人の知り合いを見つけて、同じような作業をお願いすることになります。
#ロシア人の知り合いを見つけるのはどうしたらいいのかは分かりません( ̄ー ̄)ニヤリ

20140523_091839

(追記)忘れてました。qmake brynhildr.proでMakefile.Releas/Makefile.Debugを更新しました。

多言語対応の準備をしましょうかね その6

「5. Qtに含まれるLinguist(GUI)を起動して、翻訳作業を行なう」です。

Linguistを起動します。cygwinからコマンドで起動してもいいですが、今回はスタートメニューから起動しました。

「ファイルメニュー」から.tsファイルを開くと以下の様な画面になります。

20140526_165324

「ソースとファーム」というところにソースコードが表示され、翻訳すべき部分にフォーカスが当ります。

真ん中の少し下辺りに、tr()マクロで囲んだデフォルトの文字列が「ソーステキスト」として表示され、その下に「日本語訳」「日本語訳 翻訳者のコメント」という欄があります。この2つの欄に日本語訳とコメントを入力します。

20140526_170038

日本語訳を「メインウィンドウを設定しています…」、コメント欄に「translated by funfun」 と入力しました。ソーステキストは「Setting up the main window ...」です。

これでいいと思ったら、「翻訳」メニュー→「完了して次へ」を選びます(アイコンをクリックするのでもいいです)。そうすると完了したよっとマークして、次の翻訳対象に進みます。

これをすべて完了するまで繰り返します。すべて翻訳したら、「Cntr-s」を押すか、「ファイル」メニューから「保存」を選択します。いつでも保存できるので、こまめに「Ctrl-s」した方がよいかもしれません。

こうして、翻訳情報の入った.tsファイルが出来上がります。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ja_JP">
<context>
    <name>QObject</name>
    <message>
        <location filename="../main.cpp" line="41"/>
        <source>Setting up the main window ...</source>
        <translatorcomment>translated by funfun<fu.aba.dc5@gmail.com></translatorcomment>
        <translation>メインウィンドウを設定しています…</translation>
    </message>
</context>
<context>
    <name>brynhildr::Brynhildr</name>
    <message>
        <location filename="../brynhildr.cpp" line="91"/>
        <source>Exit</source>
        <translatorcomment>translated by funfun<fu.aba.dc5@gmail.com></translatorcomment>
        <translation>終了</translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="92"/>
        <source>Exit Qt Brynhildr</source>
        <translatorcomment>translated by funfun<fu.aba.dc5@gmail.com></translatorcomment>
        <translation>Qt Brynhildrを終了します</translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="96"/>
        <source>About</source>
        <translatorcomment>translated by funfun<fu.aba.dc5@gmail.com></translatorcomment>
        <translation type="unfinished">Qt Brynhildr について</translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="97"/>
        <source>Show Qt Brynhildr</source>
        <translatorcomment>translated by funfun<fu.aba.dc5@gmail.com></translatorcomment>
        <translation type="unfinished">Qt Brynhildr について</translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="101"/>
        <source>About &Qt</source>
        <translatorcomment>translated by funfun<fu.aba.dc5@gmail.com></translatorcomment>
        <translation>Qt について</translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="102"/>
        <source>Show Qt library's About box</source>
        <translatorcomment>translated by funfun<fu.aba.dc5@gmail.com></translatorcomment>
        <translation>Qt について</translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="109"/>
        <source>&File</source>
        <translatorcomment>translated by funfun<fu.aba.dc5@gmail.com></translatorcomment>
        <translation>&ファイル</translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="115"/>
        <source>&Help</source>
        <translatorcomment>translated by funfun<fu.aba.dc5@gmail.com></translatorcomment>
        <translation>&ヘルプ</translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="151"/>
        <source>connected : </source>
        <translatorcomment>translated by funfun<fu.aba.dc5@gmail.com></translatorcomment>
        <translation>connected: </translation>
    </message>
</context>
</TS>

多言語対応の準備をしましょうかね その5

「4. Qtに含まれるlupdateコマンドを使って多言語対応したい文字列の情報を取得する」です。

lupdateコマンドを実行すると.proで指定されたソースファイル(SOURCES)を全部解析して、tr()マクロで書かれた文字列定数の翻訳のためのデータのテンプレート(.ts)を生成します。

実行するコマンドは以下の様な感じです。

lupdate -verbose brynhildr.pro

出来上がった.tsファイルは以下の様な感じです。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ja_JP">
<context>
    <name>QObject</name>
    <message>
        <location filename="../main.cpp" line="39"/>
        <source>Setting up the main window ...</source>
        <translation type="unfinished"></translation>
    </message>
</context>
<context>
    <name>brynhildr::Brynhildr</name>
    <message>
        <location filename="../brynhildr.cpp" line="91"/>
        <source>Exit</source>
        <translation type="unfinished"></translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="92"/>
        <source>Exit Qt Brynhildr</source>
        <translation type="unfinished"></translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="96"/>
        <source>About</source>
        <translation type="unfinished"></translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="97"/>
        <source>Show Qt Brynhildr</source>
        <translation type="unfinished"></translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="101"/>
        <source>About &Qt</source>
        <translation type="unfinished"></translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="102"/>
        <source>Show Qt library's About box</source>
        <translation type="unfinished"></translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="109"/>
        <source>&File</source>
        <translation type="unfinished"></translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="115"/>
        <source>&Help</source>
        <translation type="unfinished"></translation>
    </message>
    <message>
        <location filename="../brynhildr.cpp" line="151"/>
        <source>connected : </source>
        <translation type="unfinished"></translation>
    </message>
</context>
</TS>
sourceタグに囲まれた文字列がソースコードに埋め込まれたデフォルトの文字列です。翻訳が終わっていないので"unfinished"という文字列が見えますね。さて、次はやっと翻訳作業です。

2014年5月25日 (日)

今後の妄想をつぶやく

ブログを書き始めて、96日(Schedule Watcherで確認)経ちました。
#海外の人(日本国内の外国の人?かも)からコメント2件いただきました。びっくりですね。

一日平均25行というハイペースコーディング(o^-^o)と完全プライベートな自己満足日記と化していますね。
ま、自分の備忘録という役目もありますので。

今のところ画像、音声で動作確認済みなプラットフォームは、

 Linux (Ubuntu 14.04 LTS x32/x64), Windows8.1 Professional update, Windows7 Home (x64) SP1

です。

妄想中対応プラットフォームは、

 MacOS X, iOS, Android, FreeBSD(PC-BSD), Windows Surface, Windows Phone

酷い妄想です。(*^ω^*)ノ彡

Windowsデスクトップ版はOpenGL+MinGWでなくVS2013(x32/x64)で作ったほうがよいのかもなぁと思うのですが。いつ試せるか分かりません…

PCMデータの同期をどうするかを考える前に

Linuxで顕著な音声の遅れについて考えます。えっと、頭の中を整理するのに今どきどんだけアナログって気もしますが、図を書きました。

Qtb_pic_2

(追記) Diff -> Delay ですかね、やっぱり。

Brynhildrサーバさんとの通信を概念的に書くとこんな感じです。

JPEGデータの届き方とPCMデータの届き方が少し違うので、単純に1画面分のJPEG画像ファイルが届くたびにQtに描画を依頼するのと、細切れに送られてくるPCMデータを寄せ集めて、再生用バッファに書き込むのでは、時間がずれてきます。

この絵をイメージしながら、同期をどうするか検討します。
#手で書いたほうが、うまく検討できるのは、アナログ時代な人間だけではないはず、と思う今日この頃。

2014年5月24日 (土)

この回答はつらいかも…

ぜんぜん関係ないのですが、調べ物をしていて思ったことです。

ビルド時のエラーを見て、検索した結果Debianのbug tracking systemが引っかかりました。

https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=343746

ビルド時に以下のようなエラーが出ました。少しピンとこないので、同じエラーになっている人がいないか調べてみたのです。

settings.cpp: In member function 'void brynhildr::Settings::readSettings()':
settings.cpp:77:34: error: conversion from 'long int' to 'const QVariant' is ambiguous
QTB_DEFAULT_VIDEOQUALITY).toInt());
^
settings.cpp:77:34: note: candidates are:
In file included from C:/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/include/QtCore/qvariantanimation.h:48:0,
from C:/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/include/QtCore/qpropertyanimation.h:45,
  :
  :

実は、この最後の行以降に重要なヒントが書かれているのですが、質問者はそれに気づいていないようです。Debianのbug tacking systemに書くくらいの開発者なら、気づいてもよいのでは?という判断でしょうか…

回答者の答えは、「Subjectを見ろ」の一言です( ^ω^ )(あなたがもう答えを書いているというユーモアかも)

私の場合で答えるとすれば、QSettingsクラスのvalueという関数を使っていたのですが、
この関数の定義はQt Assistantによれば、以下のようになっています。

QVariant QSettings::value(const QString & key, const QVariant & defaultValue = QVariant()) const

エラーはlong int から QVariantへの変換が曖昧です、というメッセージです。

具体的には以下のように書いていました。

// load videoQualilty
setVideoQuality(settings.value(QTB_VIDEOQUALITY, QTB_DEFAULT_VIDEOQUALITY).toInt());

第二引数がマクロQTB_DEFAULT_VIDEOQUALITY(5)という定数値だったのですが、これをvalue()に適用するなら、第二引数はQVariantでなければならないので変換しなければならないけど、候補がたくさんあって分からないよ、ってことのようです。

今回は以下のように明示してあげました。

// load videoQualilty
setVideoQuality(settings.value(QTB_VIDEOQUALITY, (qint32)QTB_DEFAULT_VIDEOQUALITY).toInt());

このようにかけば、5という値はqint32なので、これをQVariantに変換すればいいのですね、とコンパイラが判断してくれて、エラーが解決するということのようです。

確かにSubjectに

settings.cpp:77:34: error: conversion from 'long int' to 'const QVariant' is ambiguous

って書いてあれば、「答えはそこにあなたが書いてあるでしょ」と、言いたくなるのも分からなくはないですが、少しさみしいですよね…

多言語対応の途中ですが…

多言語対応をしながら、ストレステストをしているのですが、ちょっと気になったので調査します。

VMware Workstation上で動いているWindows XP上のBrynhildrサーバに接続して、ストレステストをしていました。

20140524_195639

Advanced System Careのスキャンを行っているデスクトップ画面です。ちょっとわかりにくいのですが、やや画像がにじんでいて、画質が劣化しています。Brynhildrサーバに画像を要求する時にクライアント(この場合はQt Brynhildr(仮称))から画質をある程度指定してJPEGデータの送信を要求するのですが、サーバの性能や負荷などを調べて、最終的な画質を決めているような感じがします。作者の方にはまだ裏を取っていませんが(^-^;

できれば、きれいな画像で操作したいですよね。で、JPEGの画像圧縮について調べてみます。googleさんで検索すると以下のページがひっかかりました。

画質・圧縮率について

JpegAnalyzerというソフトを公開されている方のページみたいです。

静止画像圧縮に関してまとめられているようですね、素晴らしいです。

結構なボリュームなので、また別途読ませていただくことにして、とりあえず上記のページだけ、さらっと眺めました。

このページよると、画質を決める要素としては、以下の3つのパラメータがあるそうです。

・量子化テーブル
・サンプリング比(間引き率)
・ハフマン圧縮の効率

んん、もう少し調査してから、また書きます。

多言語対応の準備をしましょうかね その4

「3. 多言語対応するための追加情報を.pro/.qrcファイルに入れる」です。

日本語での表示を行うために、実行時にデータファイルを読み込む仕組みは入れました。次は、実際に読み込むデータを作ります。このデータは、英語の文字列と対応する日本語の文字列の対応表みたいな情報を定義したものです。

翻訳するのは人間ですので、人間に分かる形のファイルを編集することになります。このファイルが".ts"という拡張子のファイルになります。プログラムが読み込むデータは事前に読み込みやすいようにバイナリに変換したファイル(".qm"という拡張子)になります。

これらのファイルの情報を.pro/.qrcファイルに追加します。

まずは、.proファイルです。"TRANSLATUINS="の行です。

---------------------- ここから ------------------------------------------
######################################################################
# Automatically generated by qmake (3.0) ? 2 12 23:44:23 2014
######################################################################

TEMPLATE = app
TARGET = qtbrynhildr
INCLUDEPATH += .
QT += gui
QT += network
QT += widgets
QT += multimedia
RESOURCES = brynhildr.qrc
TRANSLATIONS = translations/brynhildr_ja_JP.ts

# Input
HEADERS += brynhildr.h mainwindow.h util.h common.h protocols.h machine/protocols_x86.h machine/protocols_x64.h soundbuffer.h settings.h parameters.h
SOURCES += main.cpp brynhildr.cpp mainwindow.cpp soundbuffer.cpp settings.cpp
---------------------- ここまで ------------------------------------------

次は.qrcファイルです。".qm"ファイルの指定を追加しました。この".qm"ファイルは".ts"ファイルを変換して後で生成します。

---------------------- ここから ------------------------------------------


                images/3Bjunior.jpg
                translations/brynhildr_ja_JP.qm


---------------------- ここまで ------------------------------------------

以上で必要な2つのファイルに関する情報を記述できました。ファイル名の拡張子の直前の文字列が以前調べた言語の情報を示す文字列である"ja_JP"になっています。

2014年5月23日 (金)

多言語対応の準備をしましょうかね その3

「2. main()に多言語対応するための仕組みを入れる」です。

2つのincludeと"// for Translation"以降の行をmain()に追加します。
#include <QLocale>
#include <QTranslator>

int main(int argc, char *argv[])
{
  QApplication app(argc, argv);

  // for Translation
  QTranslator appTranslator;
  appTranslator.load(":/translations/brynhildr_"
				 + QLocale::system().name(),
				 qApp->applicationDirPath());
  app.installTranslator(&appTranslator);

appTranslator.load()の引数に言語ごとのデータファイルを指定するのですが、":"が付いているのは特別な指定をするためです。

debug/qrc_*.cppというファイルの中身を見るとなんとなく分かるかもしれません。実はこのqrc_*.cppというソースファイルは、言語ごとのデータとかアイコン用の画像データなどが埋め込まれたソースファイルで、makeの時に画像データや言語データ、その他指定したデータが実行ファイルに埋め込まれるようになっています。このように埋め込まれたファイルにアクセスするために":"というのが目印になっているそうです。

ですので、アイコン用の画像データや言語データは別途同梱する必要はなさそうですね。

多言語対応の準備をしましょうかね その2

まだ2,500行ほどのソース行数なので翻訳すべき文字列定数は11箇所しかありませんでした。

以下のように、英語表記を日本語表記に表示したいです。メニューとかステータスバーの表記ですね。

"File" -> "ファイル"
"Exit" -> "終了"
"Help" -> "ヘルプ"

いっぺんには出来ないので、少しずつ行います。流れは以下の様な感じです。

1. システムロケール文字列を調べる

2. main()に多言語対応するための仕組みを入れる

3. 多言語対応するための追加情報を.pro/.qrcファイルに入れる

4. Qtに含まれるlupdateコマンドを使って多言語対応したい文字列の情報を取得する

5. Qtに含まれるLinguist(GUI)を起動して、翻訳作業を行なう

6. Qtに含まれるlreleaseコマンドを使って多言語対応用のバイナリファイルを作る

7. makeする

8. 動作確認する

こんな感じに日本語になります。

20140523_091839

まず、「1, システムロケール文字列を調べる」です。

この文字列を利用して表示したい言語の情報を読み込むので必要らしいです。Qt Assistantで調べても分かるのでしょうけど、実際どうなってるのかソースコードに書いて実際に実行して調べました。

以下の2行をmain()にでも書いてみます。普通にcoutに出力しようとするとコンパイルで怒られるので、qDebug()に出力させます。

qDebug() << "[Brynhildr()] QLocale::system().name() : "
<< QLocale::system().name() << endl << flush;

makeして実行すると以下のように表示されました。

$ ./debug/qtbrynhildr.exe
[Brynhildr()] QLocale::system().name() : "ja_JP"

言語の情報は"ja"、国の情報は"JP"みたいです。この"ja_JP"をどこかにメモっておきます。

で、「2. main()に多言語対応するための仕組みを入れる」に進みます。

2014年5月22日 (木)

多言語対応の準備をしましょうかね その1

Qtでは、文字列の情報をunicodeで保持します。ですので、もしメニューとかステータスバーなどの表示が日本語のみでよければ、日本語でラベル文字列を直に指定すれば済みます。当然ですが、英語圏の人が立ちあげても、日本語で表示しようとしますね。フォントがなければ、おかしな表示になるんでしょうかね、たぶん。ま、ちゃんと表示されても、日本語なので??なことになるんでしょう。

それだとやっぱり英語圏の人は使いづらいですよね。ロシア語とかでメニューが表示されたら、日本人の私は手も足も出ません(;ω;)

というわけで、いろんな国の文字で表示できるように準備だけはしておきます。

まず、最初にしておくべきことは文字列を指定するときには、trマクロ(tr())を使うということです。

例えば、ファイルメニューの文字列を指定する場面で

fileMenu = menuBar()->addMenu("File");

と書く所は、

fileMenu = menuBar()->addMenu(tr("File"));

と書いておきます。文字列で翻訳が必要そうなところは全部tr("File")という感じで書いておきます。迷ったら、常にtrマクロで書いておいてもいいかもしれません、エラーにはなりませんので。ただ、マクロの引数に文字列定数でなくて変数を書く時は注意が必要です。また、引数の文字列定数は初期設定ではLatin-1という文字コードでかくことになってますが、普通に半角英数文字で書いておくのがよいかと思います。

これをすべてのソースコードを調べて必要な所を変更しておきます。これを変更し忘れたら、メニューが一部英語のままになるので、その場合は該当箇所がtrマクロになっていない可能性がありますのでその時に再チェックします。とりあえず目についた所だけ修正します。

配布用に必要なファイルを探しましょう

Windows用フリーソフトウェアとして配布するのに必要なファイルを以前調べました。

実行ファイルであるexeファイルの他にQt5が提供しているダイナミックリンクライブラリ(DLL)を同梱するか、それらをすべてくっつけた大きな実行ファイル(スタティックリンクしたもの)を作って提供するかを選ぶ必要があります。

以前は、必要なファイルをくっつけた巨大な実行ファイルを作ろう、という話をしていましたが、やっぱりダイナミックリンクライブラリを同梱した方がいいかなという気になりました。

配布に使う実行ファイルはデバッグ用の実行ファイルではなく、リリース用に作った実行ファイルでなくてはなりません。MinGW版だとreleaseフォルダの下に出来上がるexeファイルです。

mingw32-make release

とすれば出来上がります。普段のデバッグ用には

mingw32-make debug

です。デバッグ用の実行ファイルサイズは2439167バイト(約2.4MB)もありますが、リリース用実行ファイルサイズは193536バイト(189KB)です。

さて、Qt5.3が提供するDLLも一緒に提供しなくてはなりません。cygwinのlddコマンドを使います。

ldd release/qtbrynhildr.exe

と実行すると、一瞬スラッシュウィンドウが表示されますが、すぐ実行は終わります。どうやら、実際にリンク作業させるために、実行されるようです。ウィンドウが閉じた後、結果が表示されます。

ldd release/qtbrynhildr.exe >& ldd.log

とすればldd.logというファイルに結果を保存できるます。

ldd.logの内容は以下のようになりました。

ntdll.dll => /c/Windows/SYSTEM32/ntdll.dll (0x7ff8de7f0000)
ntdll.dll => /c/Windows/SYSTEM32/ntdll.dll (0x77460000)
wow64.dll => /c/Windows/SYSTEM32/wow64.dll (0x773a0000)
wow64win.dll => /c/Windows/system32/wow64win.dll (0x773f0000)
wow64cpu.dll => /c/Windows/system32/wow64cpu.dll (0x77390000)
OPENGL32.DLL => /c/Windows/SYSTEM32/OPENGL32.DLL (0x4c0000)
KERNEL32.DLL => /c/Windows/SYSTEM32/KERNEL32.DLL (0x770e0000)
OPENGL32.DLL => /c/Windows/SYSTEM32/OPENGL32.DLL (0x4c0000)
OPENGL32.DLL => /c/Windows/SYSTEM32/OPENGL32.DLL (0x4c0000)
KERNEL32.DLL => /c/Windows/SYSTEM32/KERNEL32.DLL (0x770e0000)
KERNELBASE.dll => /c/Windows/SYSTEM32/KERNELBASE.dll (0x77010000)
msvcrt.dll => /c/Windows/SYSTEM32/msvcrt.dll (0x76f40000)
libgcc_s_dw2-1.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/libgcc_s_dw2-1.dll (0x6e940000)
libstdc++-6.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/libstdc++-6.dll (0x6fc40000)
Qt5Core.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/Qt5Core.dll (0x68880000)
Qt5Gui.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/Qt5Gui.dll (0x61940000)
Qt5Multimedia.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/Qt5Multimedia.dll (0x6b480000)
Qt5Network.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/Qt5Network.dll (0x69700000)
Qt5Widgets.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/Qt5Widgets.dll (0x61dc0000)
libwinpthread-1.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/libwinpthread-1.dll (0x64940000)
USER32.dll => /c/Windows/SYSTEM32/USER32.dll (0x75050000)
ADVAPI32.dll => /c/Windows/SYSTEM32/ADVAPI32.dll (0x75c20000)
ole32.dll => /c/Windows/SYSTEM32/ole32.dll (0x75870000)
SHELL32.dll => /c/Windows/SYSTEM32/SHELL32.dll (0x75d80000)
WS2_32.dll => /c/Windows/SYSTEM32/WS2_32.dll (0x75d30000)
icuin52.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/icuin52.dll (0x69e00000)
icuuc52.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/icuuc52.dll (0x66480000)
GDI32.dll => /c/Windows/SYSTEM32/GDI32.dll (0x75490000)
OPENGL32.DLL => /c/Windows/SYSTEM32/OPENGL32.DLL (0x4c0000)
CRYPT32.dll => /c/Windows/SYSTEM32/CRYPT32.dll (0x756f0000)
DNSAPI.DLL => /c/Windows/SYSTEM32/DNSAPI.DLL (0x717f0000)
sechost.dll => /c/Windows/SYSTEM32/sechost.dll (0x75cf0000)
RPCRT4.dll => /c/Windows/SYSTEM32/RPCRT4.dll (0x75350000)
combase.dll => /c/Windows/SYSTEM32/combase.dll (0x755a0000)
SHLWAPI.dll => /c/Windows/SYSTEM32/SHLWAPI.dll (0x75ca0000)
NSI.dll => /c/Windows/SYSTEM32/NSI.dll (0x752b0000)
icudt52.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/icudt52.dll (0x6bbc0000)
GLU32.dll => /c/Windows/SYSTEM32/GLU32.dll (0x50350000)
DDRAW.dll => /c/Windows/SYSTEM32/DDRAW.dll (0x64aa0000)
MSASN1.dll => /c/Windows/SYSTEM32/MSASN1.dll (0x75420000)
SspiCli.dll => /c/Windows/SYSTEM32/SspiCli.dll (0x74e80000)
DCIMAN32.dll => /c/Windows/SYSTEM32/DCIMAN32.dll (0x67430000)
CRYPTBASE.dll => /c/Windows/SYSTEM32/CRYPTBASE.dll (0x74e70000)
bcryptPrimitives.dll => /c/Windows/SYSTEM32/bcryptPrimitives.dll (0x74e10000)
IMM32.DLL => /c/Windows/system32/IMM32.DLL (0x75bf0000)
MSCTF.dll => /c/Windows/SYSTEM32/MSCTF.dll (0x75af0000)
SHCORE.dll => /c/Windows/SYSTEM32/SHCORE.dll (0x74bb0000)
profapi.dll => /c/Windows/SYSTEM32/profapi.dll (0x74c70000)
qwindows.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/plugins/platforms/qwindows.dll (0x6a880000)
OLEAUT32.dll => /c/Windows/SYSTEM32/OLEAUT32.dll (0x751e0000)
WINMM.DLL => /c/Windows/SYSTEM32/WINMM.DLL (0x74d80000)
WINMMBASE.dll => /c/Windows/SYSTEM32/WINMMBASE.dll (0x74c50000)
cfgmgr32.dll => /c/Windows/SYSTEM32/cfgmgr32.dll (0x75270000)
DEVOBJ.dll => /c/Windows/SYSTEM32/DEVOBJ.dll (0x74c30000)
kernel.appcore.dll => /c/Windows/SYSTEM32/kernel.appcore.dll (0x74ba0000)
uxtheme.dll => /c/Windows/system32/uxtheme.dll (0x71ff0000)
dwmapi.dll => /c/Windows/system32/dwmapi.dll (0x360000)
qjpeg.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/plugins/imageformats/qjpeg.dll (0x626c0000)
CRYPTSP.dll => /c/Windows/SYSTEM32/CRYPTSP.dll (0x72120000)
rsaenh.dll => /c/Windows/system32/rsaenh.dll (0x720f0000)
bcrypt.dll => /c/Windows/SYSTEM32/bcrypt.dll (0x720d0000)

Windowsが提供するDLLは必要ないので以下のようにして選別します。

grep -v Windows ldd.log > ldd_Qt.log

Qt関係(正確にはWindowsが提供するDLL以外)の情報だけがldd_Qt.logに入ります。

libgcc_s_dw2-1.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/libgcc_s_dw2-1.dll (0x6e940000)
libstdc++-6.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/libstdc++-6.dll (0x6fc40000)
Qt5Core.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/Qt5Core.dll (0x68880000)
Qt5Gui.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/Qt5Gui.dll (0x61940000)
Qt5Multimedia.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/Qt5Multimedia.dll (0x6b480000)
Qt5Network.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/Qt5Network.dll (0x69700000)
Qt5Widgets.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/Qt5Widgets.dll (0x61dc0000)
libwinpthread-1.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/libwinpthread-1.dll (0x64940000)
icuin52.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/icuin52.dll (0x69e00000)
icuuc52.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/icuuc52.dll (0x66480000)
icudt52.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/bin/icudt52.dll (0x6bbc0000)
qwindows.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/plugins/platforms/qwindows.dll (0x6a880000)
qjpeg.dll => /c/Tools/develop/Qt/Qt5.3.0/5.3/mingw482_32/plugins/imageformats/qjpeg.dll (0x626c0000)

上記に現れたDLLファイルを実行ファイルと同梱すればいいことになります。どこにあるかは、上の情報をみれば分かりますね。今のところ、最終的には以下の14個のファイルを実行ファイルと同じ所に置いておけばいいことになりそうです。

libgcc_s_dw2-1.dll
libstdc++-6.dll
Qt5Core.dll
Qt5Gui.dll
Qt5Multimedia.dll
Qt5Network.dll
Qt5Widgets.dll
libwinpthread-1.dll
icuin52.dll
icuuc52.dll
icudt52.dll
qwindows.dll
qjpeg.dll

今後新たな機能を追加すると新たなDLLを追加する必要が出てきますが、同じように調べればその時点で必要なファイルは調べられます。ただし、多言語対応しようとすると特別なファイルが必要になるのかもしれません、が今のところ日本語対応していないので、必要なさそうです。それについてはまた調べます。

Linuxでの調べ方も基本的には同じですが、実行用にQtが提供するライブラリ類は別途パッケージとして提供されるのが普通のようなので、そこは別途確認が必要です。

(追記)

「Visual C++ 2010 x86 Redistributable」パッケージも必要かしれません(すでにPCに入っている可能性もあります)。この配布ファイルは、Qtのパッケージに含まれています。具体的にはQt\Qt5.3.0\vcredistの下にある

vcredist_sp1_x86.exe

を同梱すれば良さそうです。

(追記2)

Microsoft のツールである Dependency Walker を使っても同様の情報は得られますが、使っているすべてのプラグインの情報は得られないこともあります。動作しない機能がある場合は地道に必要なプラグインを探すか、すべてのdllを同梱する、ということになりそうです。例えば、qtaudio_windows.dll とか。

2014年5月21日 (水)

進捗記録 2014.5.21


Linux(Ubuntu 14.04 LTS)版のキャプチャ動画を上げておきます。

Qt Brynhildr(仮称) Linux版(Ubuntu 14.04 LTS) [キャプチャ動画]

ブログの記事を見返すのに、バックナンバーが見にくくて困っていたので、ココログの設定を少しいじりました。
デフォルトのまま使うのはやっぱりダメですね。フリー版だからしょうがないのでしょうか。

最近の開発環境ってスゴイですね

Qtもそうなんですが、最近の開発プラットフォームの充実は、凄まじくて、ついていくだけも大変ですね。

しかし、勉強すべきことは多いのですが、1から作ることを考えれば、たった2000行程度のコーディングでここまでのことができることがすごすぎます。私などjpegフォーマット、TCP/IPの詳細さえ知らないのですから(笑)

80x25のテキストベースのスクリーンで、必死に絵を書き、いかにして32KBのメモリにプログラムを押しこむかを日夜考えていた日々が嘘のようです。Z80のマシン語(コード)をほぼ覚えていたり、机上アセンブル、逆アセンブルしたりすることが今あるのでしょうか?(笑)グラフック表示はVSYNCの復帰中に書かなきゃね、とか(笑)
あと、FDD内のサブCPUを使い倒すには、どうするかとか(キリがないのでこのへんでやめときます)

今はグラフィック、サウンド、デバイスすべてが巨大なライブラリでサポートされるので、アイデアさえあれば、ある程度のものが出来てしまいます。メモリもHDDも使えきれないくらいありますから、問題ないし。

CUDAとかにも興味あるんですが、時間が…というか、寿命も足りないかも(笑)

#といっても私のPCにはメモリが24GB、HDDが10台(10TB)しかつながっていません…パワーユーザなら32GB/20TBくらいですかね(^_^;

勉強するという意味では、たった2000行のソースコードでも参考になることが多いのかもしれませんね。いきなり複雑なものを見ても理解が追いつかないので、これくらいのソースの方がいいのかも。「なんだ、こんなコードでいいのか」と思う人もいると思いますが、「こう処理するのがいいのでは」と提案してくれるとお互い勉強になるでしょうしね。

UNIXでさえ、最初はサンプル程度の数百行から始まったはずですし、たぶん。

いきなり、「3D CGバリバリのゲームを作りたいんですが、教えてください」といわれても困りますが、ね。

読者と一緒に勉強していく本(教科書)があってもいいのかもしれませんね。間違える過程も書くという( ^ω^ )
いつになったら終わるのか分からないので困りますかね(笑)

2014年5月20日 (火)

Qt 5.3 がリリースされました

ベータだったQt5.3がリリースされました。(http://qt-project.org/qt5/qt53

「すべてが改善された!」とのことです。

Windows版はWindows8.1,VS2013(32bit/64bit)に対応しました。Android向け(Linux, Windows上で開発), WinRT(Surfaceとか)向けもダウンロードできます。(http://qt-project.org/downloads

これでAndroid向けにビルドできます。シミュレータは別途SDKを入れたらO.K.でしょう。

GPS, Blutooth, 印刷機能の充実、らしいです。

「QNX Neutrino 6.6 and QNX Neutrino 6.5 SP1」、「Windows Phone 8.0/8.1」が正式サポートとなってますが、うーん。

新規機能は以下です。

http://qt-project.org/wiki/New-Features-in-Qt-5.3

まずは、一報だけ。

(追記)
Windows8.1 update/Ubuntu 14.04 LTS 日本語Remixで、動作だけ確認できました。

(追記2)
あとMinGWが4.8.2にアップデートされているので別途インストールしなくても済みます。もちろんPATHの設定は変更が必要です。

cygwinだと例えば,

/c/Qt/Qt5.3.0/Tools/mingw482_32/bin (C:\Qt\Qt5.3.0\Tools\mingw482_32\bin)のような感じです。

ただし、例の場合cygwinの/etc/fstabを以下のように書いておく必要があります。元のcygdriveに関する行

none /cygdrive cygdrive binary,posix=0,user 0 0

を以下のように書き換えます。

none / cygdrive binary,posix=0,user 0 0

fstabを書き換えたら、mount -aを実行して、一旦bashを閉じて、開き直します。
そうするとc:\Toolsなら/c/Toolsでアクセスできるようになります。

デフォルトのまま書き換えないとしたら、/cygdrive/c/Toolsとアクセスする必要があって、毎回/cygdriveと打つのが面倒なんですよね。TABで補完もできますが、それも面倒という…。環境設定オタクは実はめんどくさがりであるということでもあるのかもしれません。(蛇足ですが、d:\homeなら/d/homeでアクセスできます)

ちなみに私はこんなふうに書いてます。

d:\home -> /home
d:\src -> /src
d:\tmp -> /tmp

って感じです。

------------- ここから -------------------------------
# For a description of the file format, see the Users Guide
# http://cygwin.com/cygwin-ug-net/using.html#mount-table

# This is default anyway:
none / cygdrive binary,posix=0,user 0 0
d:/home /home ntfs override,binary,auto 0 0
d:/src /src ntfs override,binary,auto 0 0
d:/tmp /tmp ntfs override,binary,auto 0 0
------------- ここまで -------------------------------

2014年5月19日 (月)

PCMデータのスループットが気になる…

Brynhildrでも指摘がある(Siegfriedでは改善済みらしい)のですが、通信帯域が狭い時に生のPCMデータはかなり重いようです。安定した有線LANならほぼ問題ないと思われますが、品質の悪い無線LANになるとプチノイズが乗る可能性があります。

素のWindowsアプリケーションならまだ軽いのですが、Qt5になるとスループットが低く、低速無線LANだとぎりぎりっぽいです。画質を程々にしないと音が間に合いません。なんか改善できるのか調べてはみますが…

プラグインコーディックを利用してCompressed Audio(MP3とか)にしたいんですが、その場合サーバ側(機能?)も作らないとなりません…大事(おおごと)になります(@Д@;

Brynhildrクライアント互換にして、サーバも別途作るか…。風呂敷広げすぎだな…

とりあえず、キャプチャしました。バージョンが"0.02"になった記念で。

キャプチャソフトをoCamにすると動画にマークが入らない(フリーソフトなので)ので、いいです。基本的な使い方はBandicamと変わりません。F2キーで録画終了することだけ覚えていれば大体O.K.です。YouTubeへのアップロードも問題なしです。

Qt Brynhildr(仮称)の動作テスト [キャプチャ動画]

Windows 8.1 update 上での数時間のストレステストでは問題なし(落ちない、大きなメモリリークはなさそう)です。
Ubuntu 14.04 LTSはこれから…

VMwareで確認しましたが、画像データを同期せずに全速で表示しているので、PCMデータが置いてけぼりをくってるようです( ^ω^ )

実機で試そう…と思ったら、最新stableカーネル(3.14.4)が不安定(´・ω・`)ショボーン
マシンが古いから、かも。

実機で試すと、さらに音声が置いてけぼりでございます。同期しないと…ダメだこりゃ。
そして終了時は”コアダンプ”(^-^;

#お、助かった、実機だとgdbで止まる…。VMwareだと捕捉できません…

スタックフレームを見ると…ぐわっ、PCMデータ書き込み用のタイマーの停止を忘れてたようです…(´・ω・`)
解放済みのリングバッファにアクセスしようとしたので、コアダンプでございました、恥ずかしい。

(追記)
20分後、原因が判明しました。2つ原因がありました。

1,PCMデータ書き込み用のタイマーを止めていなかった

2,デストラクタ中のインスタンスのdeleteの順番がマズイ場所があった

QAudioDeviceインスタンスのdeleteの前に、QIODeviceインスタンスを開放しなければなりません。
厳密には、QIODeviceの開放時にQAudioDeviceの開放が行われるようです。
よってQAudioOutputのdeleteだけでよいと。

最後のコアダンプ箇所の特定は結局

cout << "Here1" << endl << flush;

をソースに入れて、いわゆるprintfデバッグを行いました。
コアダンプデバッグ終了…ι(´Д`υ)アセアセ

これでWindowsとUbuntuの動作差分はなくなりました。
画像とサウンド出力の同期をどうにかしないと…

OpenSSLの教訓を

OpenSSLの脆弱性が話題になっていましたが、その記事を読んだ後、何気なくソースコードを整理していたら、データのサイズチェックを全然していないことに気が付きました。性善説に則ってました、というか忘れてた(^_^;

Heartbleed脆弱性は、クライアントから要求されたデータサイズを確認せずに、固定長64KBのデータを返してしまうのだそうです。要求したデータ以上のデータが返ってきて、そこに怪しげなデータが残っているかもよ、というものらしいです。

Brynhildrの通信では、コントロール(主にキーボード、マウスを想定)、画像データ(jpegストリーム)、PCMデータの三種類があって、Qt Brynhildrでは共用の固定サイズバッファ1つと画像データ、PCMデータ(データサイズ可変)のデータを受け取るローカルデータバッファとPCMデータを保持するリングバッファを管理していますが、それぞれ最大バッファサイズを決めているため、これを超えるデータが来たら弾く必要があります。

「可変長にすればいいじゃん」という話もありますが、バッファを取り直すのはいろいろ効率悪いので。あと意味不明に大きな値が返されるとクライアントがクラッシュするので、そういう意味でも制限を掛けておきます。

こういうことを考え始めると、あらゆるところでセキュリティを考えないといけないので、なかなか難しいですね。
まずパスワードを生テキストデータで保持しているという(^_^;酷い状況をなんとかしないと…

2014年5月17日 (土)

音の遅れが気になる…

ソースコードの整理をしながらも気になるのは、やっぱり音の遅れです。
Windows8.1 updateでは特に気になりませんが、やはりLinuxで遅れるのが気になります。

Brynhildr 1.0.2ではこれが改善されているということで、どんな実装で改善されたのかを作者の方にお尋ねしました。
再生開始からの実再生時間を確認しながら、少しずつ補正しているとのこと…Qtでもそのような方法があるのでは?とアドバイス頂きました。

早速Qt Assistantで調べてみました。QAudioOutputクラスのドキュメントです。

こんなメソッドが見つかりました。

qint64 QAudioOutput::elapsedUSecs() const
Returns the microseconds since start() was called, including time in Idle and Suspend states.

qint64 QAudioOutput::processedUSecs() const
Returns the amount of audio data processed since start() was called (in microseconds).

「お、バッファサイズも指定できるんじゃん」と、いろいろまた発見しました。
QtMultimedia, QAudioOutputについて、まだドキュメントを読み込まないとだめみたいです。

今更気づいたのですが、Codecとしてすべてのプラットフォームでサポートされているのはlinear PCMなんですが、プラグインを用意すれば、compressed audio streamもいけそうです。
#が、サーバが対応しないといけなかった…

#と、書いておきながら今日一番気になるのはUstreamでの24時間配信であります(^_^; 18:00スタートです。
#この歳で”オール”は無理だよなぁ…

2014年5月15日 (木)

pullモードで実装してみる その2

と、思いましたが、しばらく延期します。とりあえずpushモードで音が出ているので。

以下を優先します。

1,ハードコーディングしている設定情報の整理及び設定ダイアログウィンドウの仮実装
2,ソースコード全般の整理
3,ソースコードでの公開のための準備

1,ハードコーディングしている設定情報の整理

将来設定ダイアログでユーザが設定するであろう情報が今はデバッグの都合でハードコーディングされています。これらをクラスにまとめて、保存/復帰する仕組みを実装します。QtではQSettingsというクラスが提供されています。このクラスはwindowsならレジストリ、unixならば例えば~/.configの下の設定ファイルなど各環境で適切に情報を保存/復帰する仕組みを提供してくれます。

windowsだとこんな感じです。まだ、整理途中ですが。

20140515_215034

Ubuntuだと今は~/.config/mcz-xoxo/Qt Brynhildr.confというファイルが作られ、以下の様な記述が入ります。

[General]
geometry=@Rect(83 72 1281 802)
serverName=urd-PC

ま、自前でやる方法もありますが、あるものは使えたら使ったほうが面倒がないので楽ですね。
設定をwindowsからunixへ移行するとかを考えるといろいろ再考すべきかもしれませんが、今はとりあえずいいかなと思います。

2,ソースコード全般の整理

クラス構成、コーディングスタイルの見直し、コメントの充実です。統一性のないクラス構成、ソースコードには機能を追加したくても、ソースファイルを開いた瞬間にやる気を無くします(^_^; 結局は自己満足ではあるのですが。

コメントは適切な量でよいのですが、「後で書こう」と後回しにしてしまい、すっかり今はてんてこ舞い状態です。
日本語で入れたいのですが、シンプルな英語にします。(自分以外に見ないなら日本語で書きます(^_^;)
doxgenなコメントを入れたいのですが、そこまでしなくてもいいかなと思います、10000行もいかないし。
(いずれ消え行くプライベートプロジェクトですので…#そのうちMarkdownで書こうかな、と)

3,ソースコードでの公開のための準備

README(README.jaとREADME.md)とライセンス情報を用意します。

README.mdは英語にしたいのですが…とりあえず日本語のテキストファイルREADME.jaの内容をコピーして、最低限Markdownに書き換えます。

ライセンスはLinuxの真似をしてCOPYINGというテキストファイルを用意し、FSFのホームページからGNU GPL v2の文面をコピーして放り込みました。かなりの他力本願です。

あと最低限の開発環境構築のための手順を書いた文章も用意しました。拙い英語で(^_^; 私にも読めるので大体の日本人にも読めると…思います。MinGWで良いのかはちょっと迷うところではあるのですが。VS2010がいいのでしょうかねぇ。Qt5.3ではVS2013で提供されるので、それを待とうかと思っています。

今のところ、このくらいを考えています。ちゃんとやるには結構時間がかかりそう。そんな暇はあるのか>自分

2014年5月12日 (月)

配布方法を考える その2

設定ダイアログ実装のため、Qt4の入門書の後購入しておいた

「Qt QuickではじめるクロスプラットフォームUIプログラミング」

を眺めていました。第7章にアプリケーションの配布について記事がありました。

Windows/Linux/Macについてまとめられていました。Qt Createrを使うのは先だろうと思い、さらっとしか目を通していませんでした。やっぱり、買った本はさらっとでも内容を頭に入れておかないと無駄な作業をしてしまうことがありますね、反省。

第8章にはモバイル(Android)への対応も書かれていました。

サーバを1.0.2にしてみたら…

普通に曲が聴けました(^_^;

データ転送速度が上がったらしく、バッファアンダフローが起きていません。前のバージョンだと転送速度が再生速度に追い付いていなかったようですが、Windows版bryhnhildrをクライアントモードで起動した場合は問題ありませんでした。

つまり、Qt版の方のデータ受信のスループットが低かった可能性が高いですね…、う、どうしたものか。
ネットワーク周りもQtライブラリを使っているんですが、そこが遅いのかも。そこだけネイティブにするか…

やや画面がチラつくのも少し気になるのですが(1.0.1.3ではなかったことかもしれないです)

なんとなく恋チュンが聴けるようになったかも [キャプチャ動画]

Linux(Ubuntu 14.04 LTS)版はこちら [キャプチャ動画]

Ubuntu版を終了させた時のコアダンプが気になるぅ…早速調査します。

しかし、音がちゃんと出るのは精神衛生上かなりいいですね。

今気づきましたが、うーん、画像と音声の同期が取れてないな、これ。Windows版はほぼ分かりませんが、Linux版はかなりの遅れがあります。

Brynhildr 1.0.2 Release だそうです

なんとなく「窓の杜」をチェックしたら、アップデートされてました。

開発ブログで修正内容を確認したら、フレーム遅延処理がクライアント機能に入ったとか…。

一つ前の記事(「ジークフリート最終形態。」)を読むと、

「それはどこかと言いますと音声伝送の音声圧縮(CELT)の部分でして、音質設定を低音質にしたりサーバー側のサウンドデバイス(CoreAudio)のサンプルレートが44100Hzだったりするとかなりのプチプチがノイズとして乗ってしまっていて、キャッシュサイズを大きくしても全く解決しないとゆーかなり難解な問題だったんですけど、何とかかんとか超必殺技で回避できやした。」

とのこと。

うーん、普通にデバッグログを観察していると「データ転送速度が足りてないかも」と思いはしていたのですが、今後どうすべきか…PULLモードにしても転送速度が間に合っていないのだとすると解決策にはなりそうにないし。

とりあえずサーバを1.0.2に置き換えてから、再検討でしょうかねぇ。
#とりあえず勉強のためPULLモードでの実装もやってはみようかな。

普段の環境をUbuntu 14.04 LTSに移すために、開発もUbuntu上で行うことを検討したいかな…
そろそろプライベートにGitHubに載っけとこうかなとも思うのですが、検討すること多すぎで困ってます。

2014年5月11日 (日)

pullモードで実装してみる その1

定期的にバッファを覗いて、データをサウンド出力用バッファにコピーするpushモードをやめて、試しにpullモードで実装してみることにします。GWの間すっかり放置していたので、Qt5のサンプルソースファイルをもう一度見直すことにしました。

サンプルソースはコメントが殆どなかったので、見直しがてらコメントを入れてみました。333行ですが結構勉強になります。

Syntax Hilighterを使いましたが、なかなかイメージどおりに表示できない…
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
**     of its contributors may be used to endorse or promote products derived
**     from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QAudioDeviceInfo>
#include <QAudioOutput>
#include <QDebug>
#include <QVBoxLayout>
#include <qmath.h>
#include <qendian.h>

#include "audiooutput.h"

#define PUSH_MODE_LABEL "Enable push mode"
#define PULL_MODE_LABEL "Enable pull mode"
#define SUSPEND_LABEL   "Suspend playback"
#define RESUME_LABEL    "Resume playback"
#define VOLUME_LABEL    "Volume:"

const int DurationSeconds = 1;	// 1秒
const int ToneSampleRateHz = 600;
const int DataSampleRateHz = 44100;	// サンプリング周波数 44.1KHz
const int BufferSize      = 32768;	// 32KB

Generator::Generator(const QAudioFormat &format,	// 独自パラメータ
                     qint64 durationUs,				// 独自パラメータ
                     int sampleRate,				// 独自パラメータ
                     QObject *parent)				// 基本クラスパラメータ
    :   QIODevice(parent)
    ,   m_pos(0)
{
    generateData(format, durationUs, sampleRate);	// PCMデータをインスタンス生成時に作成
}

Generator::~Generator()
{

}

void Generator::start()
{
    open(QIODevice::ReadOnly);	// サウンドデバイスがリードオンリーでオープンされる
}

void Generator::stop()
{
    m_pos = 0;
    close();	// PULLモード時に呼ばれる。サウンドデバイスがクローズされる
}

void Generator::generateData(const QAudioFormat &format, qint64 durationUs, int sampleRate)
{	// PCMデータ生成
    const int channelBytes = format.sampleSize() / 8;
    const int sampleBytes = format.channelCount() * channelBytes;

    qint64 length = (format.sampleRate() * format.channelCount() * (format.sampleSize() / 8))
                        * durationUs / 100000;

    Q_ASSERT(length % sampleBytes == 0); // Qtのアサーションマクロらしい。独自アサーションマクロは使わないほうがよいかも。
    Q_UNUSED(sampleBytes) // suppress warning in release builds	// 利用しない変数はこのように宣言するのがよいらしい。

    m_buffer.resize(length);
    unsigned char *ptr = reinterpret_cast<unsigned char *>(m_buffer.data());	// データバッファへのアドレスポインタを取得する
    int sampleIndex = 0;

    while (length) {	// インスタンス生成時にPCMデータテーブルの内容を初期化する
        const qreal x = qSin(2 * M_PI * sampleRate * qreal(sampleIndex % format.sampleRate()) / format.sampleRate());
        for (int i=0; i<format.channelCount(); ++i) {
            if (format.sampleSize() == 8 && format.sampleType() == QAudioFormat::UnSignedInt) {	// 8ビット unsignedのデータを設定
                const quint8 value = static_cast<quint8>((1.0 + x) / 2 * 255);
                *reinterpret_cast<quint8*>(ptr) = value;
            } else if (format.sampleSize() == 8 && format.sampleType() == QAudioFormat::SignedInt) {	// 8ビット singedのデータを設定
                const qint8 value = static_cast<qint8>(x * 127);
                *reinterpret_cast<quint8*>(ptr) = value;
            } else if (format.sampleSize() == 16 && format.sampleType() == QAudioFormat::UnSignedInt) {	// 16ビット unsignedのデータを設定
                quint16 value = static_cast<quint16>((1.0 + x) / 2 * 65535);
                if (format.byteOrder() == QAudioFormat::LittleEndian)
                    qToLittleEndian<quint16>(value, ptr);
                else
                    qToBigEndian<quint16>(value, ptr);
            } else if (format.sampleSize() == 16 && format.sampleType() == QAudioFormat::SignedInt) {	// 16ビット singedのデータを設定
                qint16 value = static_cast<qint16>(x * 32767);
                if (format.byteOrder() == QAudioFormat::LittleEndian)
                    qToLittleEndian<qint16>(value, ptr);
                else
                    qToBigEndian<qint16>(value, ptr);
            }

            ptr += channelBytes;
            length -= channelBytes;
        }
        ++sampleIndex;
    }
}

qint64 Generator::readData(char *data, qint64 len)	// PULLモード時に呼ばれる。この関数が暗黙のうちに呼ばれて、PCMデータが読み出される
{
    qint64 total = 0;
    while (len - total > 0) {	// 指定されたアドレス(data)へPCMデータをコピーする。リングバッファなので注意が必要なのかな。
        const qint64 chunk = qMin((m_buffer.size() - m_pos), len - total);
        memcpy(data + total, m_buffer.constData() + m_pos, chunk);
        m_pos = (m_pos + chunk) % m_buffer.size();
        total += chunk;
    }
    return total;	// 実際にコピーしたサイズを返す
}

qint64 Generator::writeData(const char *data, qint64 len)	// PCMデータの書き込みはサポートされない。録音の際はこれをきちんと書く必要があるらしい
{
    Q_UNUSED(data); // 使いませんと宣言
    Q_UNUSED(len); // 使いませんと宣言

    return 0;	// 常に0を返す
}

qint64 Generator::bytesAvailable() const
{
    return m_buffer.size() + QIODevice::bytesAvailable();	// 読み取り可能なデータ(バッファに溜まったデータ)のサイズを返す
}

AudioTest::AudioTest()
    :   m_pullTimer(new QTimer(this))
    ,   m_modeButton(0)
    ,   m_suspendResumeButton(0)
    ,   m_deviceBox(0)
    ,   m_device(QAudioDeviceInfo::defaultOutputDevice())
    ,   m_generator(0)
    ,   m_audioOutput(0)
    ,   m_output(0)
    ,   m_buffer(BufferSize, 0)
{
    initializeWindow();
    initializeAudio();
}

void AudioTest::initializeWindow()	// GUI部品を構築
{
    QScopedPointer<QWidget> window(new QWidget);
    QScopedPointer<QVBoxLayout> layout(new QVBoxLayout);

    m_deviceBox = new QComboBox(this);
    foreach (const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
        m_deviceBox->addItem(deviceInfo.deviceName(), qVariantFromValue(deviceInfo));	// Qtが提供するforeachマクロかな
    connect(m_deviceBox,SIGNAL(activated(int)),SLOT(deviceChanged(int)));	// シグナルと関数を結びつける
    layout->addWidget(m_deviceBox);

    m_modeButton = new QPushButton(this);
    m_modeButton->setText(tr(PUSH_MODE_LABEL));
    connect(m_modeButton, SIGNAL(clicked()), SLOT(toggleMode()));
    layout->addWidget(m_modeButton);

    m_suspendResumeButton = new QPushButton(this);
    m_suspendResumeButton->setText(tr(SUSPEND_LABEL));
    connect(m_suspendResumeButton, SIGNAL(clicked()), SLOT(toggleSuspendResume()));
    layout->addWidget(m_suspendResumeButton);

    QHBoxLayout *volumeBox = new QHBoxLayout;
    m_volumeLabel = new QLabel;
    m_volumeLabel->setText(tr(VOLUME_LABEL));
    m_volumeSlider = new QSlider(Qt::Horizontal);
    m_volumeSlider->setMinimum(0);
    m_volumeSlider->setMaximum(100);
    m_volumeSlider->setSingleStep(10);
    connect(m_volumeSlider, SIGNAL(valueChanged(int)), this, SLOT(volumeChanged(int)));
    volumeBox->addWidget(m_volumeLabel);
    volumeBox->addWidget(m_volumeSlider);
    layout->addLayout(volumeBox);

    window->setLayout(layout.data());
    layout.take(); // ownership transferred

    setCentralWidget(window.data());
    QWidget *const windowPtr = window.take(); // ownership transferred
    windowPtr->show();	// GUI部品を表示
}

void AudioTest::initializeAudio()	// オーディオ関係の初期化
{
    connect(m_pullTimer, SIGNAL(timeout()), SLOT(pullTimerExpired()));

    m_pullMode = true;	// 最初はPULLモード

    m_format.setSampleRate(DataSampleRateHz);	// サンプリング周波数(DataSampleRateHz)、チャンネル(モノラル)、データサイズ16ビット、リトルエンディアン、signed intの生PCMデータを指定
    m_format.setChannelCount(1);
    m_format.setSampleSize(16);
    m_format.setCodec("audio/pcm");
    m_format.setByteOrder(QAudioFormat::LittleEndian);
    m_format.setSampleType(QAudioFormat::SignedInt);

    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
    if (!info.isFormatSupported(m_format)) {	// 上で指定したデータ・フォーマットが標準でサポートされない場合は指定可能なフォーマットで近いものを選択する
        qWarning() << "Default format not supported - trying to use nearest";	// Qtが提供するWarning出力、Qtのクラスライブラリの出力に便利、勝手に改行
        m_format = info.nearestFormat(m_format);
    }

    m_generator = new Generator(m_format, DurationSeconds*1000000, ToneSampleRateHz, this);

    createAudioOutput();
}

void AudioTest::createAudioOutput()
{
    delete m_audioOutput;
    m_audioOutput = 0;
    m_audioOutput = new QAudioOutput(m_device, m_format, this);	// audioOutputインスタンスを再生成
    connect(m_audioOutput, SIGNAL(notify()), SLOT(notified()));
    connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), SLOT(handleStateChanged(QAudio::State)));
    m_generator->start();
    m_audioOutput->start(m_generator);	// m_generator : QIODeviceベースのインスタンス
    m_volumeSlider->setValue(int(m_audioOutput->volume()*100.0f));
}

AudioTest::~AudioTest()
{

}

void AudioTest::deviceChanged(int index)
{
    m_pullTimer->stop();	// タイマーを止めて
    m_generator->stop();	// サウンドデバイスをクローズして
    m_audioOutput->stop();	// オーディオ出力を止めて
    m_audioOutput->disconnect(this);	// SIGNALを切断
    m_device = m_deviceBox->itemData(index).value<QAudioDeviceInfo>();
    createAudioOutput();
}

void AudioTest::volumeChanged(int value)
{
    if (m_audioOutput)
        m_audioOutput->setVolume(qreal(value/100.0f));
}

void AudioTest::notified()
{
    qWarning() << "bytesFree = " << m_audioOutput->bytesFree()
               << ", " << "elapsedUSecs = " << m_audioOutput->elapsedUSecs()
               << ", " << "processedUSecs = " << m_audioOutput->processedUSecs();
}

void AudioTest::pullTimerExpired()
{
    if (m_audioOutput && m_audioOutput->state() != QAudio::StoppedState) {
        int chunks = m_audioOutput->bytesFree()/m_audioOutput->periodSize(); // bytesFree() : サウンド出力用バッファの空きサイズ(バイト)
        while (chunks) {													// periodSize() : サウンド出力用に必要なデータサイズ(バイト)
           const qint64 len = m_generator->read(m_buffer.data(), m_audioOutput->periodSize());
           if (len)
               m_output->write(m_buffer.data(), len);
           if (len != m_audioOutput->periodSize())	// データを出しきったのでループを抜ける
               break;
           --chunks;
        }
    }
}

void AudioTest::toggleMode()
{
    m_pullTimer->stop();
    m_audioOutput->stop();

    if (m_pullMode) {	// PUSHモードに変更
        m_modeButton->setText(tr(PULL_MODE_LABEL));
        m_output = m_audioOutput->start();	// サウンド出力を開始
        m_pullMode = false;
        m_pullTimer->start(20);	// タイマーを20ms毎に設定して起動する(プッシュモードでサウンド出力用バッファにデータをコピーする)
    } else {	// PULLモードに変更
        m_modeButton->setText(tr(PUSH_MODE_LABEL));
        m_pullMode = true;
        m_audioOutput->start(m_generator);	// サウンド出力を開始。readData()/writeData()は適切なタイミングで呼び出される
    }

    m_suspendResumeButton->setText(tr(SUSPEND_LABEL));
}

void AudioTest::toggleSuspendResume()	// audioOutputの状態が変化したら呼び出される
{
    if (m_audioOutput->state() == QAudio::SuspendedState) {
        qWarning() << "status: Suspended, resume()";
        m_audioOutput->resume();
        m_suspendResumeButton->setText(tr(SUSPEND_LABEL));
    } else if (m_audioOutput->state() == QAudio::ActiveState) {
        qWarning() << "status: Active, suspend()";
        m_audioOutput->suspend();
        m_suspendResumeButton->setText(tr(RESUME_LABEL));
    } else if (m_audioOutput->state() == QAudio::StoppedState) {
        qWarning() << "status: Stopped, resume()";
        m_audioOutput->resume();
        m_suspendResumeButton->setText(tr(SUSPEND_LABEL));
    } else if (m_audioOutput->state() == QAudio::IdleState) {
        qWarning() << "status: IdleState";
    }
}

void AudioTest::handleStateChanged(QAudio::State state)
{
    qWarning() << "state = " << state;
}

« 2014年4月 | トップページ | 2014年6月 »