2012年6月30日土曜日

日記ちゃんGetProcessImageFileName


 生産がす: 日記ちゃんバグの問題を無事解決。
いままでの方法でパスの取得がうまくいかなかったら新しい方法でパスを取得する。

 Obtaining a File Name From a File Handle (Windows)
を参考にした。



 GetLogicalDriveStrings()で
{"A:\",0,"C:\",0,0}
といったNULLで区切られたドライブ名一覧を得られる。

 QueryDosDevice()は
A:C:といったドライブ名から
\Device\Floppy0\Device\HarddiskVolume1といったデバイス名を取得できる。

 GetLogicalDriveStrings()で得たドライブ名から'\'を削った文字列を、
つまりC:\C:にして、
QueryDosDevice()に渡してデバイス名を得られる。

 GetProcessImageFileName()を使うと
\Device\HarddiskVolume1\sumishiro\(省略)\test.exe
といった文字列を取得できる。

 ので、さっき取得したデバイス名をGetProcessImageFileName()で得た文字列と比較して
一致するようなら元のドライブ名で置換すれば
C:\sumishiro\(省略)\test.exeといった文字に変換できる。
という流れ。

\Device\HarddiskVolume1\sumishiro\(省略)\test.exe
\Device\HarddiskVolume1の部分を
C:に置換する。



 std::stringが使えるのならもっと手軽にできそう。
実行ファイルがでかくなるから使わなかったが。

2012年6月29日金曜日

日記ちゃんバグ


 ActiveWindowLoggerだけど
Windows7で動かしたところ一部パスを正常に取得できずに文字化けする現象に遭遇。
InternetExplorerはパスは正常でも、エクスプローラやメモ帳のパスは文字化ける。
 パスの取得にGetModuleFileNameEx()を使用しているから、
そことWindows7の関係を調べればなんとかなるとは思うが、
開発環境はないからめっちゃ時間かかりそう! 
 
 ほかにもタイムライン画像の下側が地味におかしかったりいろいろ荒が。
 
 どうも64bitだと転ぶらしい。
Why do win32 calls GetModuleFileNameEx, EnumProcessModules, EnumProcessModulesEx fail in Wow64 ? | Winprogger

2012年6月26日火曜日

日記ちゃんver1.1PV


 ver 1.1のPV。

 AVI2.0で記録後、wmvに変換。
ムービーメーカで編集して各シーンごとにwmvでいったん別々のファイルに出力。
その後ムービーメーカで音とあわせて結合、って流れで作った。
 編集後すぐに完成形を出力できればよかったんだけど、
一晩たってもプログレスバーがぜんぜん進んでなかったから、
とりあえず素材動画をぶった切った。
ぶった切って結合すればちゃんと出力されるようになった。

 youtubeだと30fpsになる問題を解決しようとAVIUtlを弄繰り回してみたがうまくいかず断念。
スムーズダウンで点滅をうまく処理できないかと思ったが、
30fps->24fpsにする機能のようで、60fpsの動画を30に落とす時点で点滅が死ぬのでどうにもならなかった。
なんか方法があるだろうがちょっと調べただけではわからなかったからあきらめた。

 肝心のv1.1パッチがまだ作ってすらいない。
タスクがあと2,30ぐらいあって消化しないと出せそうもないからけっこうかかると思う。

2012年6月25日月曜日

日記ちゃん

すごいいまさらのようだけどYoutubeに60FPSの動画あげても
30FPSに変換されるのね。よそのPC環境触っててはじめて気がついた。
どうしよ、どっか適当な動画サイトを探しておこう。
 ということはAndroidで動画を見ると30FPS云々と以前書いたが、
別にそんなことはなかったって落ちになるのかも。
 ためしてみた。元の動画ファイルをAndroidに転送して再生したところ、
60FPSで再生されている様子だったが、すげえかくかくだった。
1280x720はちょっと無茶だったか。
 では640x360, 30fpsの動画でもぼちぼちがくがく、
320x180, 30fpsでもカクツク。動画再生には向いてないのかな。
ブラウザでyoutubeみるとカクツカずにすんなり再生されるからソフトの差かも。

 GoogleDriveに動画をアップしたらどうなるかなーとおもって試してみたが、
Youtubeと同様に30fpsに落とされるみたい。
https://docs.google.com/open?id=0B6WvlSoXGWBidGFqZmFsVl9RbXc
ダウンロードすれば元のデータを取ってこれるみたいだから、使えないこともないが。




 AcitiveWindowRecorderとActiveWindowLoggerの比較テスト。
一日動かしてメモリ消費量をみた。
VirtualSizeが50%減
WorkingSetも60%減とぼちぼちメモリ消費が減っているように見える。
 ログファイルは
ActiveWindowLogger: 562KB
ActiveWindowRecorder: 783KB
と28%ほど小さくなった。最近のHDD容量からすれば誤差レベルではあるが。
 とはいえログファイルが気がつけばけっこう溜まってしまい、HDDを圧迫する。
1ヶ月以上前のデータは削除するといった機能をつけたほうがいいかもしれないが、暴発が怖いか。

2012年6月24日日曜日

ActiveWindowLogger ver2.0





https://skydrive.live.com/redir?resid=8CD7CF5EA9FBCA55!138
size: 114KB

vectorからでもDL可能です。
ActiveWindowLoggerの詳細情報 : Vector ソフトを探す!



■概要
  PCで一日何をしていたかログを取るソフトです。
  この日のこの時間に何をしていたか、
  あるいは一日でアプリケーションを何時間使用したかが一目でわかります。
 
 
 
■動作環境
  Windows XP, 7
  CPU 800MHz以上
  メモリ 256MB以上
 
 
 
■アンインストール
  レジストリは触っておりません。
  削除の際はフォルダごとゴミ箱へどうぞ。
 
 
 
■使い方
  起動しっぱなしにするだけ。
  ログはプログラム終了時と日付が変わった直後にlogフォルダに保存されます。
 
  記録されるのは
    時刻(time)
    表示時間(duration)
    操作中のウィンドウタイトル(title)
    操作中のプログラムのパス(exe)
  の4つです。
 
 
 
  logボタン
    ログデータファイル(.bin2)を選択し、読み込んで表示します
    ウィンドウにファイルをD&Dでも可
    またActiveWindowRecorderのログ(.bin)の読み込むことができます。
  prevボタン
    現在表示中のログより前のログがあれば表示します
    マウスの戻るボタンでも同様
  nextボタン
    現在表示中のログより後のログがあれば表示します
    マウスの進むボタンでも同様
  todayボタン
    今日の最新のログを表示します
 
  タイムラインイメージ
    大雑把に使用時間をプログラム別に着色して表示しています。
    上下に分かれており、上が24時間表示、下がカーソル位置付近の1時間のデータになります。
 
 
 
  txtで保存
  csvで保存
    現在表示中のログをテキストがCSV形式でファイルに出力します
    csvは「time, duration, "title", "path"」で記録されます
  アプリケーション使用率
    表示中のログから各アプリケーションの使用時間などを表示します
      比率 (rate)
      使用時間 (duration)
      平均使用時間 (ave)
      プログラムパス (exe)
      表示回数 (num)
  基本設定
    ウィンドウチェック周期
      アクティブが切り替わっていないかチェックする周期の設定で、
      数値が小さいほど細かくチェックしますが負担が大きくなります
    マウスカーソル位置チェック周期
      タイムラインイメージ上のカーソル位置をチェックする周期です。
      タイムラインイメージ上でのリストの選択速度に影響が出ます。
      数値が小さいほど細かくチェックしますが負担が大きくなります
    オートセーブ
      ログの保存タイミングの設定です。
      「しない」であれば0時に、
      「一時間おき」であれば時刻が変わった瞬間に、
      「半日おき」であれば0時と12時に、ログを保存します
  色
    リストの色分けに関する設定です。ダブルクリックで色を変更、
    右クリックで項目の追加や削除が可能です
 
    アプリケーション別
      アプリケーションごとに別々の色をつけます
    汎用
      アプリケーションごとに適当に色を振り分けます
    時間別
      1時間ごとに色を変更します
    なし
 
    save, load
      色の設定のみをファイルに保存できます。
      D&Dでも読み込み可能
    clear
      色設定を初期状態に戻します
 
 
 
■スタートアップに登録すればより便利に
  「スタート」メニューの「スタートアップ」を右クリックし「開く」をクリック。
  開いたフォルダにショートカットを作成すれば、ウィンドウズといっしょに自動で起動できます。
 
 
 
■そのほか
  このソフトウェアを使用し、何らかの障害が発生しても責任を取りかねますのでご了承ください。
 
  zlibライブラリを使用しています。
    Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler
    http://www.winimage.com/zLibDll/
 
 
 
■更新履歴
  2012-06-24 ver2.00



 ActiveWindowRecorderの省メモリ版。
タイトルもより適当なものに変更し、インターフェースももっとシンプルに。
保存されるログもさらに小さいサイズになるよう設計しなおしました。

 Vectorに登録する際、掲載カテゴリ設定をすっかり忘れてたから適当なところに配置された様子。
あと登録したよメールが来なくなってた。いつまでも待ってたよ…


2012年6月19日火曜日

日記ちゃん

Android Coding: Drawing on SurfaceView
を参考にonPause(), onResume()を使用したところ、
実機で正常に起動するアプリを作成ができた。
 とはいえ速度が15fpsぐらい。
解像度800x480は大きすぎるからどうにか低解像度にできないもんかな。
背景クリア、サイズ3のドット(?)を512個、●をひとつと文字列の描画のみ。
画像を描画とかしだすともっと食うだろうけど。

 アンチェリ切ったりして若干の工夫で18fps程度に。
とりあえず満足したから次へ。



 適当においてるドットの数を512から128にへらしたら50fps程度に。
そこからThread.sleep()で適当にウェイトをかけてだいたい30fpsにすることができた。
 いまひとつ作法がわからないが要勉強だろう。
ガベコレちゃんがちょくちょくちょっかいかけてくるとかなんとかだから、
できるだけメモリを要求しないつくりにする必要があるが、Javaでメモリ管理なんてできるのかな。
c++ならplacement newとかでどうとでもなるが、
javaだと昔ながらのあらかじめ配列を用意する管理がシンプルか。うーん想像がつかない。

 あとはcanvasで400x240ぐらいの小さいオフスクリーンをつくって
最後に拡大して表に出すぐらいすればそこそこ速度が稼げそうな気がしないでもない。
画像の持ち方が良くわからんが、まだいいか。

 いまさらだけど端末エミュレータを毎回起動しなおしてたけど、
起動しっぱなしにしておけばよかったのね。面倒くさいなとは思ったんだ。

 小さいBitmapを作ってそっちにレンダリング、最後に表に拡大して描画という方法を試してみたが、
かえって速度が落ちてしまった。15fpsぐらい。
400x240でも200x120でも15fps程度に落ち着いてしまうからなんかありそうではあるが。

 Bitmap.Config.RGB_565でBitmapを作成するようにしたところ37~40fpsぐらいになった。
変換くらってたみたい。それでも巣で描画したほうがきれいで速い。
画像をまともに描画しようとするとまた違うのかも。

2012年6月16日土曜日

日記ちゃん

Android。
ちょいちょいサンプルを消化していってるが、
どうもスレッドの生成がおっそいらしく。
いくらかのサンプルが起動後20秒程度プチフリ状態に。
アプリが起動しないと勘違いしてたが、辛抱強く待つと起動するようだ。
待たずに戻るボタンを押すと強制停止する。
アプリ起動にコツがいるとか聞いてないよ……。
 調べてみるとスレッド生成が遅いといった記事は見つからないから、
個体差かそもそもスレッドとか関係なくアプリの起動が遅いのかもしれない。
 
 同じコードなのに動いたり動かなかったりする。どういうことなの。
onTouchEvent()を使用しないようにしたら動いた、なんかあるのかな。
 とりあえずループするだけのプログラムでFPSを計ったところ82FPSぐらいでてた。
エミュレートだと8FPSしかでないけど。
30FPSぐらいで作成予定だから、割と余裕があるかもしれない。
 
 しばらくしたらアプリが正常に起動しなくなってた。
再起動してもだめだったし、何が原因なのか。

2012年6月15日金曜日

日記ちゃん


 不二家のチョコの懸賞があたった。
小さいボールペンのまわりにビーズがぽつぽつ接着されてる。
ペン先のほうとかかなり無理やり感が漂ってて面白い。
しかも予備のビーズつき。はがれること前提なのな。
ある種のエラー処理としては適切なのかもしれないが、エラーが発生しうる製品仕様はどうだろう。

2012年6月14日木曜日

日記ちゃんbom

UNICODE定義してるのでWriteFileするときに
bom[] = { 0xFF, 0xFE };
を記述するようにしたのだけど、確認したらなぜかSJISで認識されてて、
何でだろうと調べまわった。
 結局、確認につかったテキストエディタがsakuraだったのだけど、
使用ファイル履歴がのこるらしくて、
履歴からエンコードを選択しているようだ。
履歴を削除して開きなおしたらUNICODEで開くようになった。
 この間、3時間ぐらい。
sakuraの設定で履歴MAX=0にしてるから思いもよらなかったよ。
いったんsakuraを終了するまでは履歴が蓄積するんだろう。

 Android。OpenGL動かないかと思ってたけど
解説サイトのOpenGLサンプルは動いたから動くようだ。
動かないのはSurfaceViewらしい。少なくともサンプルが動かないだけだが。
ちょいちょい調べまわってるがどうもGCで安定しないとか、
音周りが反応遅いとかなかなか苦戦しそう。

SurfaceViewも一応動いたわ。じゃあ何が動かないんだ。このサンプル? 

2012年6月11日月曜日

日記ちゃん


 ActiveWindowRecorderをより省メモリにしたバージョンを製作中。
ActiveWindowRecorderはバージョンアップごとに機能が追加されていき、
だんだんごちゃごちゃしていった。
インターフェースをばっさりと変更し、
colorボタン等あまり触らないであろう機能をメニューへ追いやり、
シンプルに(予定)。
 
 をしつつ、Androidさわりつつ、ring^-27パッチ作りつつ、DirectXさわる。
Androidが2.2なのだけど2.2に対応したソフトなら何でも動くかと思ってたけどそうでもないみたい。
未確認だけどOpenGLを使用したソフトはたぶん動かない。インストールはできたけど。
 あと傾きセンサーはあるけど、温度や角度、磁器センサーは搭載してないから動かない、
動くけど0しか帰ってこないのか、エラーを無視してるのかは不明だが。
カメラも振動もないけど、傾きがあれば割と楽しいことができそうだから別にいいよね!

ActiveWindowRecorder ver1.04b




activeWindowRecorder104b.zip (size: 103KB)

VectorからもDL可能です。
ActiveWindowRecorderの詳細情報 : Vector ソフトを探す!

 
■概要
  PCで一日何をしていたかログを取るソフトです。
  この日のこの時間に何をしていたか、あるいは一日でアプリケーションを何時間使用したかが一目でわかります。
 
 
 
■動作環境
  Windows XP, 7
  CPU 800MHz以上
  メモリ 256MB以上
 
 
 
■アンインストール
  レジストリは触っておりません。
  削除の際はフォルダごとゴミ箱へどうぞ。
 
 
 
■使い方
  起動しっぱなしにするだけ。
  ログはプログラム終了時と日付が変わった直後にlogフォルダに保存されます。
 
  記録されるのは
    時刻(time)
    表示時間(duration)
    操作中のウィンドウタイトル(title)
    操作しているプログラムのパス(exe)
  の4つです。
 
  outputボタン
    表示中のログをtxtかcsvファイルに出力します
  appInfo
    表示中のログのアプリケーション別の使用率(rate)、使用時間(duration)、プログラムパス(exe)を表示します
  color
    色設定を行います。プログラム別、大雑把、時間別、色なしの4種から設定可能。
    リスト項目をダブルクリックで個別に色を設定します。
    プログラム別のリストのみ右クリックで削除できます。
    saveボタンで色設定のみをファイルに保存できます。特定のプログラムのみ色分けした場合に便利かもしれません。
    loadボタンで色設定を読み込み。
  logボタン
    ログデータファイル(.bin)を選択し、読み込んで表示します
  prevボタン
    現在表示中のログより前のログがあれば表示します。
    マウスの戻るボタンでも同様
  nextボタン
    現在表示中のログより後のログがあれば表示します
    マウスの進むボタンでも同様
  todayボタン
    今日の最新のログを表示します
 
  タイムラインイメージ
    大雑把に使用時間をプログラム別に着色して表示しています。
    上下に分かれており、上が24時間表示、下がカーソル位置付近の1時間のデータになります。
 
 
 
■そのほか
  デフォルトでは100msに一度程度アクティブのウィンドウをチェックしています。
  もっと細かいほうがいい、あるいはもっとゆるくてもいいという場合は、
    config.ini内[timer]セクションのcheckTimerの値を変更してください。(単位ms)
 
  このソフトウェアを使用し、何らかの障害が発生しても責任を取りかねますのでご了承ください。
 
  zlibライブラリを使用しています。
    Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler
    http://www.winimage.com/zLibDll/
 
 
 
■更新履歴
  2012-04-19 ver1.04b
    多重起動防止メッセージ後、元のウィンドウを表示するよう修正
    マニフェストファイルの記述を修正
    config.ini, autosave.colorのパスを実行ファイルと同じディレクトリになるよう修正
    TimeLineImageの一定時間以上のものは横幅計算でオーバーフローしているのを修正。ms計算からsecに変更して
  2012-01-11 ver 1.04
    マウスの進む、戻るボタンでprev, nextボタンが動作するよう修正。
    色設定ウィンドウのプログラムリストをソートしてから表示するよう修正。
  2012-01-10
    デフォルトのlogフォルダ以外からログを参照した時に
      prev, nextボタンでの読みこみ先を、参照したフォルダになるよう修正
    色設定ウィンドウ作成
    色設定、およびアプリケーション使用率に関する設定をiniファイルに保存するよう修正
  2011-12-29
    リストの背景色を一時間ごとに色付けするよう修正。
  2011-12-28
    日付をまたぐと表示を最新にするよう修正。
  2011-11-16
    XPStyleで表示すると下側の表示が見切れるのを修正
  2011-09-03 ver1.03a
    分計算がおかしかったのを修正
  2011-08-08 ver1.03a 未公開
    アプリケーション使用率にaverage追加
    durationを分までだったのを、時に対応。
  2011-07-31 ver1.03
    アプリケーション別の使用時間を別ウィンドウに表示する機能の作成。
    ログオフおよびシャットダウンでプログラムが終了したとき、ウィンドウ情報が保存されないのを修正
  2011-06-21 ver1.02a
    起動したとき最小化を維持するよう修正
  2011-06-19 ver1.02
    タイムラインイメージを大きくした
      上半分を24時間のタイムライン、
      下半分を1時間分のタイムラインに変更
  2011-06-17 ver1.01c
    Windowsをシャットダウン、ログオフ時にログを保存するよう修正
    多重起動チェックをするよう修正
  2011-06-15 ver1.01b
    起動直後、ログに同じ項目が現れるのを修正
    起動後すぐ終了するなどで、ログが0のときにセーブしようとすると落ちるのを修正。
      終了時に現在アクティブなウィンドウ情報を更新してから終了するよう修正。
  2011-06-13 ver1.01b(未公開)
    複数選択状態でソートすると処理に時間がかかるのを修正
  2011-06-11 ver1.01
    ログデータを圧縮して保存するよう修正
    output[txt]でプログラム別の使用率の表示順を降順に
  2011-06-11 ver1.00c
    タイムライン利用で複数項目選択を選択したとき、項目数が多いと処理に時間がかかっていたのを修正
  2011-06-09 ver1.00b
    ウィンドウ情報を保存するよう修正
    output[txt]でプログラム別に使用時間と使用率を出力するよう修正
  2011-06-08 ver1.00
    公開

ZenStyleM100-CreatePlayList v102

https://skydrive.live.com/redir?resid=8CD7CF5EA9FBCA55!390

size: 7KB

■概要
ZenStyleM100にてフォルダ別にプレイリストを作成する

■バージョン
ver 1.02 出力ファイル一覧を処理終了時にまとめて表示するように修正
ver 1.01 フォルダとファイルのタイムスタンプ(作成日時、更新日時)、m3u内のファイルの有無、ファイル数をチェックする機能を追加
ver 1.00 公開

■使い方
お使いのZenStyleM100のルートフォルダにCreatePlayList101.vbsを置いて実行してください。
ファイル内の outputDirectory および searchDirectory のパスを書き換えれば
お好きな位置で実行することもできます。

Musicフォルダ直下にあるフォルダ名で、PlayListフォルダにプレイリストが出力されます。
(デフォルトの場合)

■使い方(詳しく)
1. お使いのM100のルートフォルダにダウンロードしたCreatePlayList.vbsを置いてください。



2. CreatePlayList.vbsを実行
(windowsならダブルクリックで実行される。ほかは知らん)
確認ウィンドウがでるので良ければ[OK]


3. しばし終わるのを待つ
終わったら[終了]と出ます


その後本体起動時にリストのチェックが入るのか起動時間が長くなります。
気長にお待ちくださいませ。


無事プレイリストの読み込みが完了した

Musicフォルダには以下画像のように配置しています。

完了後のPlayListフォルダはこんな感じに。




■仕様
設定したフォルダパス(デフォではMusic)内にあるフォルダの数だけm3uを作成します。
たとえば、

G:.
├─Music
│ ├─フォルダ1
│ │ ├─01.mp3
│ │ └─02.mp3
│ ├─フォルダ2
│ │ ├─disc1
│ │ │ ├─01.mp3
│ │ │ ├─02.mp3
│ │ │ └─03.mp3
│ │ └─disc2
│ │   ├─01.mp3
│ │   ├─02.mp3
│ │   └─03.mp3
│ └─フォルダ3
├─Pictures
├─Video
├─Recorded
└─Playlist

って構成になっていたら作成されるm3uは
フォルダ1.m3u
フォルダ2.m3u
の2つが、PlayListフォルダに出力されます。
フォルダ2は中にさらにフォルダがありますが、
まとめてフォルダ2.m3uに保存されることになります。
またフォルダ3には曲がないのでフォルダ3.m3uは出力されません。

CreatePlayList.vbsの中身を書き換えることで、
出力先フォルダ、入力フォルダを変更することができます。
デフォルトではPlayListフォルダに出力する設定になっていますが、
既存のプレイリストとごちゃごちゃになるのが気になる方は出力先を変更すると良いでしょう。



■CreatePlayList102.vbsソースコード
' Zen Style M100の曲ファイルをフォルダ別にm3uにして、PlayListに保存するプログラム
' 2012-03-25 ver 1.02
' author sumishiro@gmail.com
' 本プログラムのご利用に際し如何なる損失や損害が発生しても、一切の責任を負いかねます。ご了承ください。
'
'
' 1012-05-26 ver 1.02
'     出力ファイル一覧を処理終了時にまとめて表示するように修正
' 1012-03-25 ver 1.01
'     フォルダとファイルのタイムスタンプ(作成日時、更新日時)、m3u内のファイルの有無、ファイル数をチェックする機能を追加
' 1012-03-21 ver 1.00
 
 
Option Explicit
 
 
 
Dim outputDirectory
Dim searchDirectory
Dim rootFileName
 
 
 
' m3u出力先。フォルダが存在しないと出力されないようなので注意
outputDirectory = ".\PlayList"
 
 
 
' 調べるディレクトリパス
' このディレクトリにあるフォルダ名でm3uファイルを作成し、
' 各フォルダ内にある音楽ファイルをm3uに突っ込む
' 同名のm3uは上書きされるので注意
searchDirectory = ".\Music"
 
 
 
' searchDirectoryに直接おいてある曲もm3uリストにしたい場合
' 以下に出力m3uファイル名を定義してね
' rootFileName = "root"
 
 
' 出力ファイル一覧をいれる
Dim outputFiles
outputFiles = ""
 
 
 
Dim fso
Set fso = WScript.CreateObject("Scripting.FileSystemObject")
 
 
 
' 指定m3uファイルをチェック。
' ファイルがすべて存在してm3uとして問題がなければTrueを返す。
' 参照引数rLineNumに該当ファイルの行数(記述されたファイル数)を入れるが、
' 存在しないファイルがあった場合は最後までカウントしないので注意
Function checkPlayList( fileObj, rLineNum )
  Dim file
  Set file = fso.OpenTextFile( fileObj, 1, False, -1 )
 
  ' C: D:といったドライブ名を取得
  Dim driveName
  driveName = Left( searchDirectory, InStr(searchDirectory, "\") )
 
  rLineNum = 0
  Do Until file.AtEndOfStream=True
    Dim strLine
    strLine = file.ReadLine()
 
    If fso.FileExists(driveName & strLine)=False Then
      checkPlayList = False
      Exit Function
    End If
    
    rLineNum = rLineNum + 1
  Loop
 
  file.Close()
  checkPlayList = True
End Function
 
 
 
' m3uの作成が必要かどうかチェック。
' 指定した日時よりファイル日時のほうが新しければFalse, それ以外はTrue
' @return 作成の必要があるならばTrue
Function needCreatePlayListFile( filePath, dateTime, musicFileNum )
  If fso.FileExists(filePath) Then
    ' m3uが指定時刻より新しいかチェック。新しければ関数終了
    If fso.GetFile(filePath).DateLastModified < dateTime Then
      needCreatePlayListFile = True
      Exit Function
    End If
 
    ' m3uファイルの妥当性チェック
    Dim lineNum
    If checkPlayList(fso.GetFile(filePath), lineNum) Then
      ' 数チェック
      If lineNum=musicFileNum Then
        needCreatePlayListFile = False
        Exit Function
      End If
    End If
 
  End If
 
  needCreatePlayListFile = True
End Function
 
 
 
' 引数date, file.更新日時, file.作成日時の3つの中から一番最新の日時を返す
' fileにはFileオブジェクトかFolderオブジェクト
Function getMostNewDate( file, date )
  Dim tmp
  tmp = date
 
  If file.DateLastModified %gt; tmp Then
    tmp = file.DateLastModified
  End If
 
  If file.DateCreated %gt; tmp Then
    tmp = file.DateCreated
  End If
 
  getMostNewDate = tmp
End Function
 
 
 
' 指定ファイル名とoutputDirectoryからm3uファイルのパスを作成
Function createM3UFilePath( fileName )
  createM3UFilePath = fso.BuildPath( outputDirectory, fileName & ".m3u" )
End Function
 
 
 
' m3uファイル作成
' @param fileNameには拡張子はつけない
' あらかじめ設定した出力先にfileNameを結合してUNICODEで保存
Function createM3UFile( filePath, fileData )
  If Len(fileData) %gt; 0 Then
    Dim file
    Set file = fso.OpenTextFile( filePath, 2, True, -1 )
    file.Write( fileData )
    file.Close
 
    createM3UFile = True
 
'    WScript.Echo "create " & fso.GetAbsolutePathName(filePath)
    outputFiles = outputFiles & fso.GetAbsolutePathName(filePath) & vbNewLine
  Else
    createM3UFile = False
  End If
End Function
 
 
 
' パスからドライブ名を除く
' [c:\folder\folder\file.ext]-%gt;[\folder\folder\file.ext]
Function delDriveName( filePath )
  Dim pos
  pos = InStr( filePath, "\" )
 
  Dim strLen
  strLen = Len( filePath )
 
  Dim ret
  ret = Right( filePath, strLen-pos+1 )
 
  delDriveName = ret
End Function
 
 
 
' ディレクトリ内にあるwmv, mp3, wav, ogg ファイルのパスをfileDataに列挙。
' そのときC:などのドライブ名は削除し\Music~といったファイルパスに変換されて保存する
Function scanMusicFile( dirObj, fileData, mostNewDate )
  Dim fileNum
  fileNum = 0
  
  Dim fileObj
  For Each fileObj In dirObj.Files
    Dim ext
    ext = LCase( fso.GetExtensionName(fileObj) )
    If ext="wmv" Or ext="mp3" Or ext="wav" Or ext="ogg" Then
      mostNewDate = getMostNewDate( fileObj, mostNewDate )
 
      fileData = fileData & delDriveName( fileObj ) & vbNewLine
      fileNum = fileNum + 1
    End If
  Next
  
  scanMusicFile = fileNum
End Function
 
 
 
' ディレクトリ走査
' 音楽ファイルへのパスをあつめるところまで
Sub scanDirectory( dirObj, fileData, mostNewDate, musicFileNum )
  mostNewDate = getMostNewDate( dirObj, mostNewDate )
 
  Dim subDirObj
  For Each subDirObj In dirObj.SubFolders
    Call scanDirectory( subDirObj, fileData, mostNewDate, musicFileNum )
  Next
 
  musicFileNum = musicFileNum + scanMusicFile( dirObj, fileData, mostNewDate )
End Sub
 
 
 
' searchDirectoryに定義したパスを走査
Sub scanRootDirectory( dirPath )
  Dim src
  Set src = fso.GetFolder( dirPath )
 
  Dim filePath
 
  Dim subDirObj
  For Each subDirObj In src.SubFolders
    Dim fileData
    Dim mostNewDate
    Dim musicFileNum
 
    fileData = ""
    mostNewDate = subDirObj.DateLastModified
    musicFileNum = 0
    Call scanDirectory( subDirObj, fileData, mostNewDate, musicFileNum )
    
    filePath = createM3UFilePath( fso.GetFileName(subDirObj) )
    If needCreatePlayListFile(filePath, mostNewDate, musicFileNum) Then
      Call createM3UFile( filePath, fileData )
    End If
  Next
 
  ' searchDirectoryにある曲をm3u出力する
  If Len(rootFileName) %gt; 0 Then
    fileData = ""
    mostNewDate = src.DateLastModified
    musicFileNum = scanMusicFile( src, fileData, mostNewDate )
    
    filePath = createM3UFilePath( rootFileName )
    If needCreatePlayListFile(filePath, mostNewDate, musicFileNum) Then
      Call createM3UFile( filePath, fileData )
    End If
  End If
End Sub
 
 
 
' 処理の開始
outputDirectory = fso.GetAbsolutePathName( outputDirectory )
searchDirectory = fso.GetAbsolutePathName( searchDirectory )
 
Dim message
message = "処理を開始します" & vbNewLine & "input = " & searchDirectory & vbNewLine & "output = " & outputDirectory 
 
If MsgBox(message, 1, "確認")=1 Then
  Call scanRootDirectory( searchDirectory )
 
  If Len(outputFiles)%gt;0 Then
    WScript.Echo outputFiles
  End If
 
  MsgBox("終了")
End If

 生成したファイル確認を表示を一括にして、とても若干便利に。

 曲の追加や削除をしたにもかかわらず、
本体のプレイリストに反映されない場合があります。
その場合、PlayListフォルダを_PlayListなどにリネームし本体を起動、
その後またPlayListに戻して本体を起動すると、
強引に認識しなおさせることができるようです。

2012年6月8日金曜日

日記ちゃん

アンドロイド端末が届いたので触ってみてるが、
アプリ探すのが一苦労だねこれ。

 買ったのはこれ。
Amazon.co.jp: AKART 5インチアンドロイドタブレット CH-AND500: パソコン・周辺機器
 コメントどおりバッテリが1~2時間ほどしか持たないし、
すげえもっさりしてるしで使いづらさがまじぱねぇんだけど、
価格もあいまって、好きに弄繰りまわして壊してもいいのよって感じのおもちゃっぽさがなかなかグッド。
 こまったらコンセントさしっぱで稼動すればいいし、
もっさりもちょっと前の携帯ぐらいだからさほど気にはならない。
マーケットでゲームの類がほとんどダウンロードできないが、対応機種外だからだろうか。
ゲームを遊ぶ目的なら買わないほうがいいと思う。
android marketアプリだとダウンロードできて、
アンドロイドマーケットアプリおよびgoogle playからのダウンロードはできない。なぞだ。
google play対応機種一覧に乗ってないのでそんなものなのかも。
 
 自分の場合アプリ作りたいだけだから問題ない。
手持ちにUSBオスオスがないからPCと接続できないが、つなげばたぶんデバッグ可能に。
 
 ほかに有用な使い道があればいいが
しばらくしたらごみになりそうな……。
 5インチって結構でかいから持ち歩くには辛い。
あと初期状態ではmp3も再生できないから適当に再生アプリが必要になる。
IMEも適当に入れないと日本語が不自由、simejiが起動しなかったから別のを入れた。
 60fpsのyoutube動画(自分のw)をみたが30fpsで表示されているようだったから、
ゲームなんかも60fpsは無理っぽいかなと思う。再生自体はできたから動画を楽しむことは可能。
 スリープモード?だとほとんどバッテリ使わないみたいだから、基本的には電源切らなくてもいいっぽい。




BLくるー。

2012年6月6日水曜日

32bitDIBから無圧縮AVI2.0

とりあえず1TBまで対応。
AVIファイルフォーマットが大変参考になりました。

1280x720、60FPSで2分30秒ほどの動画を作成。 38GBほどのファイルが出力された。
そのままでは重くて見られなかったのでffmpegおよび、
VideoConverter 1.10でwmvに変換、再生したところ正しく再生できる様子だった。


曲は適当。



■仕様
・RIFF-AVIは1GB以下
・4GBセグメントを越えないようRIFFサイズを512MBにした。
・RIFFサイズを512MBぴったりにJUNKを調整する
・最低1TB記録するにはAVISUPERINDEX_ENTRYが2048個必要
・moviリスト後が2048バイト境界ぴったりにする
・JUNKに無駄がでないようにAVISUPERINDEX_ENTRYにまわす
といった条件を満たすよう計算しファイルに記録していきます。
512MBではなく1GBにしても特に問題はないがなんとなく。



■使い方 AVIRecorder avi;
avi.create( filename, width, height, fps );
avi.update( image );
avi.update( image );
...
avi.close();

 create()でファイル先頭に適切なサイズのスペースをあけ
update()で画像を記録、
512MBいっぱいになったら次のRIFFを作成
close()でRIFFのサイズ、moviサイズと
ファイル先頭のヘッダ情報(総フレーム数とか)を書き換えて記録終了な流れになります。



■AVIファイルフォーマット
RIFF - AVI
├─LIST - hdrl
│ ├─avih
│ │ ├─dwMicroSecPerFrame
│ │ ├─dwMaxBytesPerSec
│ │ ├─dwPaddingGranularity
│ │ ├─dwFlags
│ │ ├─dwTotalFrames
│ │ ├─dwInitialFrames
│ │ ├─dwSuggestedBufferSize
│ │ ├─dwWidth
│ │ ├─dwHeight
│ │ └─dwReserved[4]
│ │
│ ├─LIST - strl
│ │ ├─strh
│ │ │ ├─fccType
│ │ │ ├─fccHeader
│ │ │ ├─dwFlags
│ │ │ ├─dwPriority
│ │ │ ├─dwLanguage
│ │ │ ├─dwInitialFrames
│ │ │ ├─dwScale
│ │ │ ├─dwRate
│ │ │ ├─dwStart
│ │ │ ├─dwLength
│ │ │ ├─dwSuggestedBufferSize
│ │ │ ├─dwQuality
│ │ │ ├─dwSampleSize
│ │ │ └─rcFrame
│ │ │
│ │ ├─strf
│ │ │ ├─biSize
│ │ │ ├─biWidth
│ │ │ ├─biHeight
│ │ │ ├─biPlanes
│ │ │ ├─biBitCount
│ │ │ ├─biSizeImage
│ │ │ ├─biXPelsPerMeter
│ │ │ ├─biYPelsPerMeter
│ │ │ ├─biClrUsed
│ │ │ └─biClrImportant
│ │ │
│ │ └─index (32 + 16*x byte)
│ │   ├─fcc
│ │   ├─cd
│ │   ├─wLongsPerEntry
│ │   ├─bIndexSubType
│ │   ├─bIndexType
│ │   ├─nEntriesInUse
│ │   ├─dwChunkID
│ │   ├─dwReserved[3]
│ │   │(* riff)
│ │   ├─qwOffset
│ │   ├─dwSize
│ │   └─dwDuration
│ │
│ └JUNK

├ LIST - movi
│ ├─00db
│ ├─00db
│ ├─00db...
│ └─ix00
│   ├─fcc
│   ├─cd
│   ├─wLongsPerEntry
│   ├─bIndexSubType
│   ├─bIndexType
│   ├─nEntriesInUse
│   ├─dwChunkID
│   ├─qwBaseOffset
│   ├─dwReserved
│   │(* frame)
│   ├─dwOffset
│   └─dwSize

└─idx1
  │(* frame)
  ├─ckid
  ├─dwFlags
  ├─dwChunkOffset
  └─dwChunkLength
 
RIFF - AVIX
├─JUNK
├─LIST - movi
│ ├─00db
│ ├─00db
│ ├─00db...
│ └─ix00
│   ├─fcc
│   ├─cd
│   ├─wLongsPerEntry
│   ├─bIndexSubType
│   ├─bIndexType
│   ├─nEntriesInUse
│   ├─dwChunkID
│   ├─qwBaseOffset
│   ├─dwReserved
│   │(* frame)
│   ├─dwOffset
│   └─dwSize

└─JUNK
 
RIFF - AVIX
RIFF - AVIX...
 
 
 
■サンプルプログラム
 実行すると実行時間に応じたoutput.aviが出力される。
#include <cstdio>
#include <vector>
#include <windows.h>
#include <vfw.h>
#include <aviriff.h>
 
 
const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const int TIMER_ID = 1000;
const int FPS = 60;
 
 
 
class DIB32
{
public:
  /**
   * Point
   */
  struct Point
  {
    LONG x, y;
    LONG u, v;
 
    void set( LONG x, LONG y, LONG u, LONG v )
    {
      this->x = x;
      this->y = y;
      this->u = u;
      this->v = v;
    }
  };
 
  class PutColorAdd
  {
  public:
    static inline void update( DWORD& dest, DWORD src )
    {
      DWORD color = (dest & 0x00fefefe) + (src & 0x00fefefe);
      DWORD mask = color & 0x01010100;
      dest = (dest&0xFF000000) | color | (mask - (mask >> 8));
    }
  };
 
 
 
 
  DIB32()
    : m_pixel( NULL )
  {
    ZeroMemory( &m_bmi, sizeof(m_bmi) );
  }
 
  virtual ~DIB32()
  {
    release();
  }
 
  virtual bool create( LONG width, LONG height )
  {
    if ( width <= 0 || height <= 0 ) return false;
    release();
 
    m_pixel = new DWORD[ width * height ];
    if ( !m_pixel ) return false;
 
    m_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    m_bmi.bmiHeader.biWidth = width;
    m_bmi.bmiHeader.biHeight = height;
    m_bmi.bmiHeader.biPlanes = 1;
    m_bmi.bmiHeader.biBitCount = 32;
    m_bmi.bmiHeader.biSizeImage = width * height * 4;
    m_bmi.bmiHeader.biCompression = BI_RGB;
    m_bmi.bmiHeader.biXPelsPerMeter = 3780;  //96dpiだと3780らしい。0の場合もあるとのこと
    m_bmi.bmiHeader.biYPelsPerMeter = 3780;
 
    return true;
  }
 
  /** create from HBITMAP */
  bool create( LPCTSTR fileName )
  {
    HBITMAP hBmp = static_cast<HBITMAP>( LoadImage( NULL, fileName, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE ) );
    if ( !hBmp ) return false;
 
    BITMAP bm = {0};
    GetObject( hBmp, sizeof(BITMAP), &bm );
 
    // create DC
    HDC hdc = CreateCompatibleDC( NULL );
    if ( !hdc ) return false;
 
    // create buf
    if ( !create( bm.bmWidth, bm.bmHeight ) )
    {
      DeleteDC( hdc );
      return false;
    }
 
    // copy
    GetDIBits( hdc, hBmp, 0, bm.bmHeight, m_pixel, &m_bmi, DIB_RGB_COLORS );
 
    DeleteDC( hdc );
    DeleteObject( hBmp );
 
    return true;
  }
 
  virtual void release()
  {
    if ( m_pixel )
    {
      delete [] m_pixel;
      m_pixel = NULL;
    }
    ZeroMemory( &m_bmi, sizeof(m_bmi) );
  }
 
  bool render( HDC hdc, HWND hWnd ) const
  {
    RECT rect;
    GetClientRect( hWnd, &rect );
    return GDI_ERROR != StretchDIBits(
      hdc, 0, 0, rect.right, rect.bottom,
      0, 0, getWidth(), getHeight(),
      m_pixel, &m_bmi, DIB_RGB_COLORS, SRCCOPY );
  }
 
 
 
  template < class T >
  inline void renderLine(
    LPDWORD destLine,
    LONG destMaxWidth,
    LONG startX, LONG endX,
    float startU, float startV,
    float endU, float endV )
  {
    if ( startX > endX )
    {
      std::swap( startX, endX );
      std::swap( startU, endU );
      std::swap( startV, endV );
    }
 
    if ( startX > destMaxWidth || endX < 0 || (endX-startX)==0 ) return;
 
    const float addU = (endU-startU) / static_cast<float>(endX - startX);
    const float addV = (endV-startV) / static_cast<float>(endX - startX);
 
    // cliping-left
    if ( startX < 0 )
    {
      startU += (addU * static_cast<float>(-startX));
      startV += (addV * static_cast<float>(-startX));
      startX = 0;
    }
    // cliping-right
    if ( endX > destMaxWidth )
    {
      endX = destMaxWidth;
    }
 
    float u = startU;
    float v = startV;
    for (LONG x=startX; x<endX; x++)
    {      
      T::update( *(destLine + x), getPixel(static_cast<LONG>(u), static_cast<LONG>(v)) );
 
      u += addU;
      v += addV;
    }
  }
 
  template < class T >
  bool triangle( DIB32& dest, const Point& point1, const Point& point2, const Point& point3 )
  {
    Point p1( point1 ), p2( point2 ), p3( point3 );
 
    // 手動ソート
    if ( p1.y > p2.y ) std::swap( p1, p2 );
    if ( p1.y > p3.y ) std::swap( p1, p3 );
    if ( p2.y > p3.y ) std::swap( p2, p3 );
 
    const float height1_2 = static_cast<float>( p2.y-p1.y ? p2.y-p1.y : 1 );
    const float height1_3 = static_cast<float>( p3.y-p1.y ? p3.y-p1.y : 1 );
    const float height2_3 = static_cast<float>( p3.y-p2.y ? p3.y-p2.y : 1 );
    
    const float destAddX1_2 = static_cast<float>(p2.x-p1.x) / height1_2;
    const float destAddX1_3 = static_cast<float>(p3.x-p1.x) / height1_3;
    const float destAddX2_3 = static_cast<float>(p3.x-p2.x) / height2_3;
    
    const float addU1_2 = static_cast<float>(p2.u-p1.u) / height1_2;
    const float addU1_3 = static_cast<float>(p3.u-p1.u) / height1_3;
    const float addU2_3 = static_cast<float>(p3.u-p2.u) / height2_3;
    const float addV1_2 = static_cast<float>(p2.v-p1.v) / height1_2;
    const float addV1_3 = static_cast<float>(p3.v-p1.v) / height1_3;
    const float addV2_3 = static_cast<float>(p3.v-p2.v) / height2_3;
    
    // 1~2
    float startX = static_cast<float>( p1.x );
    float endX = static_cast<float>( p1.x );
    float startU = static_cast<float>( p1.u );
    float startV = static_cast<float>( p1.v );
    float endU = static_cast<float>( p1.u );
    float endV = static_cast<float>( p1.v );
    LPDWORD destLine = dest.getPixelAddr( 0, p1.y );
 
    for (LONG y=p1.y; y<p2.y&&y<dest.getHeight(); y++)
    {
      // render line
      if ( y >= 0 )
      {
        renderLine<T>( destLine, dest.getWidth(), static_cast<LONG>(startX), static_cast<LONG>(endX), startU, startV, endU, endV );
      }
 
      startX += destAddX1_2;
      endX += destAddX1_3;
      startU += addU1_2;
      endU += addU1_3;
      startV += addV1_2;
      endV += addV1_3;
 
      destLine -= dest.getWidth();
    }
    // 2~3
    startX = static_cast<float>( p2.x );
    startU = static_cast<float>( p2.u );
    startV = static_cast<float>( p2.v );
    for (LONG y=p2.y; y<p3.y&&y<dest.getHeight(); ++y)
    {
      if ( y >= 0 )
      {
        renderLine<T>( destLine, dest.getWidth(), static_cast<LONG>(startX), static_cast<LONG>(endX), startU, startV, endU, endV );
      }
 
      startX += destAddX2_3;
      endX += destAddX1_3;
      startU += addU2_3;
      endU += addU1_3;
      startV += addV2_3;
      endV += addV1_3;
 
      destLine -= dest.getWidth();
    }
 
    return true;
  }
 
  
  LONG getWidth() const { return m_bmi.bmiHeader.biWidth; }
  LONG getHeight() const { return m_bmi.bmiHeader.biHeight; }
  const LPDWORD getPixelAddr() const { return m_pixel; }
  const LPDWORD getPixelAddr( LONG x, LONG y ) const { return m_pixel + ((getHeight()-1)-y)*getWidth() + x; }
  LPDWORD getPixelAddr() { return m_pixel; }
  LPDWORD getPixelAddr( LONG x, LONG y ) { return const_cast<LPDWORD>(static_cast<const DIB32&>(*this).getPixelAddr(x, y)); }
 
  DWORD getPixel( LONG x, LONG y ) const
  {
    if ( x >= 0 && x < getWidth() && y >= 0 && y < getHeight() )
    {
      return *getPixelAddr( x, y );
    }
    return 0;
  }
  
 
protected:
 
  LPDWORD m_pixel;
  BITMAPINFO m_bmi;
};
 
 
 
 
 
class AVIRecorder
{
public:
 
#pragma pack(push, 2)
  struct LIST
  {
    DWORD dwList;
    DWORD dwSize;
    DWORD dwFourCC;
  };
 
  struct CHUNK
  {
    DWORD dwFourCC;
    DWORD dwSize;
  };
 
 
 
  struct AVISUPERINDEX
  {
    FOURCC fcc;
    DWORD cd;
    WORD wLongsPerEntry;
    BYTE bIndexSubType;
    BYTE bIndexType;
    DWORD nEntriesInUse;
    DWORD dwChunkID;
    DWORD dwReserved[3];
  };
 
  struct AVISUPERINDEX_ENTRY
  {
    DWORDLONG qwOffset;
    DWORD dwSize;
    DWORD dwDuration;
  };
 
 
 
  struct AVISTDINDEX
  {
    FOURCC fcc;
    DWORD cd;
    WORD wLongsPerEntry;
    BYTE bIndexSubType;
    BYTE bIndexType;
    DWORD nEntriesInUse;
    DWORD dwChunkID;
    DWORDLONG qwBaseOffset;
    DWORD dwReserved;    
  };
 
  struct AVISTDINDEX_ENTRY
  {
    DWORD dwOffset;
    DWORD dwSize;
  };
 
 
 
  enum
  {
    //MAX_RIFF_SIZE = 1 << 30, //< 1024MB
    MAX_RIFF_SIZE = 1 << 29, //< 512MB
    //MAX_RIFF_SIZE = 1 << 27, //< 128MB
    //MAX_RIFF_SIZE = 1 << 25, //< 32MB
    MIN_NUM_OF_RIFF = 2048,
    PADDING_GRANULARITY = 2048,
    NEED_SUPER_INDEX_ENTRYS_SIZE = MIN_NUM_OF_RIFF * sizeof(AVISUPERINDEX_ENTRY)
  };
 
 
 
  struct AVIHeader
  {
    LIST aviList;
    LIST hdrlList;
    CHUNK avih;
    MainAVIHeader mainAVIHeader;
    LIST strlList;
    CHUNK strh;
    AVIStreamHeader streamHeader;
    CHUNK strf;
    BITMAPINFOHEADER streamFormat;
 
    AVISUPERINDEX superIndex;
 
 
    void setData(
      DWORD width, DWORD height,
      DWORD frame,
      DWORD fps, DWORD numOfRiff, DWORD headerJUNKSize )
    {
      const DWORD imageSize = width * height * 4;
 
      // RIFF - AVI 
      aviList.dwList = mmioFOURCC('R','I','F','F');
      aviList.dwSize = sizeof(aviList)
        + sizeof(hdrlList)
        + sizeof(avih) + sizeof(mainAVIHeader)
        + sizeof(strlList)
        + sizeof(strh) + sizeof(streamHeader)
        + sizeof(strf) + sizeof(streamFormat)
        + sizeof(superIndex) + sizeof(AVISUPERINDEX_ENTRY)*numOfRiff
        + sizeof(LIST) + sizeof(AVIEXTHEADER)
        + headerJUNKSize 
        + sizeof(LIST)
        + (sizeof(CHUNK) + imageSize)*frame
        + sizeof(AVISTDINDEX) + sizeof(AVISTDINDEX_ENTRY)*frame
        + sizeof(CHUNK) + sizeof(AVIINDEXENTRY)*frame
        //+ tailJUNKSize //< AVI互換だからJUNKは入れなくて良し
        - 8;
      aviList.dwFourCC = mmioFOURCC('A','V','I',' ');
 
      // LIST - hdrl
      hdrlList.dwList = mmioFOURCC('L','I','S','T');
      hdrlList.dwSize = sizeof(hdrlList)
        + sizeof(avih) + sizeof(mainAVIHeader)
        + sizeof(strlList)
        + sizeof(strh) + sizeof(streamHeader)
        + sizeof(strf) + sizeof(streamFormat)
        + sizeof(superIndex) + sizeof(AVISUPERINDEX_ENTRY)*numOfRiff
        + sizeof(LIST) + sizeof(AVIEXTHEADER)
        - 8;
      hdrlList.dwFourCC = mmioFOURCC('h','d','r','l');
 
      // avih chunk
      avih.dwFourCC = mmioFOURCC('a','v','i','h');
      avih.dwSize = sizeof(mainAVIHeader);
 
      // MainAVIHeader
      mainAVIHeader.dwMicroSecPerFrame = 1000000 / fps;
      mainAVIHeader.dwMaxBytesPerSec = imageSize * fps;
      mainAVIHeader.dwPaddingGranularity = PADDING_GRANULARITY;
      mainAVIHeader.dwFlags = 2064;
      mainAVIHeader.dwTotalFrames = frame;
      mainAVIHeader.dwInitialFrames = 0;
      mainAVIHeader.dwStreams = 1;
      mainAVIHeader.dwSuggestedBufferSize = imageSize + sizeof(CHUNK);
      mainAVIHeader.dwWidth = width;
      mainAVIHeader.dwHeight = height;
 
      // LIST - strl
      strlList.dwList = mmioFOURCC('L','I','S','T');
      strlList.dwSize = sizeof(strlList)
        + sizeof(strh) + sizeof(streamHeader)
        + sizeof(strf) + sizeof(streamFormat)
        + sizeof(superIndex) + sizeof(AVISUPERINDEX_ENTRY)*numOfRiff
        - 8;
      strlList.dwFourCC = mmioFOURCC('s','t','r','l');
 
      // strh chunk
      strh.dwFourCC = mmioFOURCC('s','t','r','h');
      strh.dwSize = sizeof(streamHeader);
 
      // AVIStreamHeader
      streamHeader.fccType = mmioFOURCC('v','i','d','s');
      streamHeader.fccHandler = mmioFOURCC('D','I','B',' ');
      streamHeader.dwFlags = 0;
      streamHeader.wPriority = 0;
      streamHeader.wLanguage = 0;
      streamHeader.dwInitialFrames = 0;
      streamHeader.dwScale = 1;
      streamHeader.dwRate = fps;
      streamHeader.dwStart = 0;
      streamHeader.dwLength = frame;
      streamHeader.dwSuggestedBufferSize = mainAVIHeader.dwSuggestedBufferSize;
      streamHeader.dwQuality = 0;
      streamHeader.dwSampleSize = imageSize;
      streamHeader.rcFrame.left = 0;
      streamHeader.rcFrame.top = 0;
      streamHeader.rcFrame.right = width;
      streamHeader.rcFrame.bottom = height;
 
      // strf chunk
      strf.dwFourCC = mmioFOURCC('s','t','r','f');
      strf.dwSize = sizeof(streamFormat);
 
      // BITMAPINFOHEADER
      streamFormat.biSize = sizeof(streamFormat);
      streamFormat.biWidth = width;
      streamFormat.biHeight = height;
      streamFormat.biPlanes = 1;
      streamFormat.biBitCount = 32;
      streamFormat.biCompression = 0;
      streamFormat.biSizeImage = imageSize;
      streamFormat.biXPelsPerMeter = 3780;
      streamFormat.biYPelsPerMeter = 3780;
      streamFormat.biClrUsed = 0;
      streamFormat.biClrImportant = 0;
 
      // AVISUPERINDEX
      superIndex.fcc = mmioFOURCC('i','n','d','x');
      superIndex.cd = sizeof(superIndex) + sizeof(AVISUPERINDEX_ENTRY)*numOfRiff - 8;
      superIndex.wLongsPerEntry = 4;
      superIndex.bIndexSubType = 0;
      superIndex.bIndexType = 0;
      superIndex.nEntriesInUse = numOfRiff;
      superIndex.dwChunkID = mmioFOURCC('0','0','d','b');
    }
  };
#pragma pack(pop)
 
 
 
  /** constructor */
  AVIRecorder()
    : m_file( INVALID_HANDLE_VALUE )
    , m_nowRiffFrame( 0 )
    , m_totalFrame( 0 )
    , m_numOfRiff( 0 )
    , m_maxNumOfFrame( 0 )
    , m_maxNumOfRiff( 0 )
    , m_tailJUNKSize( 0 )
    , m_isAVIX( false )
    , m_aviFrame( 0 )
    , m_width( 0 )
    , m_height( 0 )
    , m_fps( 0 )
    , m_aviHeaderJUNKSize( 0 )
  {
    ZeroMemory( &m_nowRiffFilePos, sizeof(m_nowRiffFilePos) );
    ZeroMemory( &m_dataStartPos, sizeof(m_dataStartPos) );
  }
 
  /** desturctor */
  ~AVIRecorder()
  {
    clear();
  }
 
  /** clear */
  void clear()
  {
    close();
 
    m_nowRiffFrame = 0;
    m_totalFrame = 0;
    m_numOfRiff = 0;
    m_maxNumOfFrame = 0;
    m_maxNumOfRiff = 0;
    m_tailJUNKSize = 0;
    ZeroMemory( &m_nowRiffFilePos, sizeof(m_nowRiffFilePos) );
    ZeroMemory( &m_dataStartPos, sizeof(m_dataStartPos) );
 
    m_isAVIX = false;
    m_aviFrame = 0;
    m_fps = 0;
    m_width = 0;
    m_height = 0;
    m_aviHeaderJUNKSize = 0;
  }
 
  /** isOpen */
  bool isOpen() const { return m_file!=INVALID_HANDLE_VALUE; }
 
  /** create */
  bool create( LPCTSTR fileName, DWORD width, DWORD height, DWORD fps )
  {
    clear();
 
    m_file = CreateFile( fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
    if ( isOpen() )
    {
      m_width = width;
      m_height = height;
      m_fps = fps;
      m_imageSize = width * height * 4;
 
      calcNewAVIParams();
 
      
 
      // とりあえず2048Byte書き込みしてヘッダ部分をスキップ
      std::vector< BYTE > tmp( 532 + m_maxNumOfRiff*sizeof(AVISUPERINDEX_ENTRY) + m_headerJUNKSize );
      DWORD writeSize;
      WriteFile( m_file, &tmp[0], tmp.size(), &writeSize, NULL );
 
      // データ開始位置記憶
      // 'ix00'の記録に使用する
      saveNowFilePos( m_dataStartPos );
 
 
      return true;
    }
 
    return false;
  }
 
  /** update */
  void update( const DIB32& image )
  {
    if ( isOpen() )
    {
      CHUNK chunk;
      chunk.dwFourCC = mmioFOURCC('0','0','d','b');
      chunk.dwSize = m_imageSize;
 
      DWORD writeSize;
      WriteFile( m_file, &chunk, sizeof(chunk), &writeSize, NULL );
      WriteFile( m_file, const_cast<LPDWORD>(image.getPixelAddr()), chunk.dwSize, &writeSize, NULL );
 
      m_nowRiffFrame++;
      m_totalFrame++;
 
 
      if ( m_nowRiffFrame == m_maxNumOfFrame )
      {
        if ( m_numOfRiff+1 > m_maxNumOfRiff )
        {
          close();
        }
        else
        {
          nextRIFF();
        }
      }
    }
  }
 
  /** close */
  void close()
  {
    if ( isOpen() )
    {
      exitRIFF();
 
      // close
      CloseHandle( m_file );
      m_file = INVALID_HANDLE_VALUE;
    }
  }
 
  /** getTotalFrame */
  DWORD getTotalFrame() const { return m_totalFrame; }
 
private:
 
  void nextRIFF()
  {
    saveSuperIndexPos();
 
    // RIFF終端辺を書き込み
    write_ix00();
 
    if ( m_isAVIX == false )
    {
      write_idx1();
      m_aviFrame = m_nowRiffFrame;
    }
 
    writeJUNK( m_tailJUNKSize );
 
 
 
 
 
    // 新しいRIFFの準備
    m_isAVIX = true;
    m_nowRiffFrame = 0;
 
    // save RIFF-AVIX pos
    saveNowFilePos( m_nowRiffFilePos );
 
 
    // パラメタ計算
    // 内部でm_nowRiffFilePos使用するので先にデータをセットすること
    calcNewAVIXParams();
 
 
    // write RIFF-AVIX
    // とりあえず最大サイズで記述しておけば、次のRIFF作成時に戻ってこずにすむ
    write_AVIX( MAX_RIFF_SIZE - 8 );
    // write JUNK chunk
    writeJUNK( m_headerJUNKSize );
 
    // save movi LIST pos
    //saveNowFilePos( m_moviPos );
 
    // write LIST-movi
    {
      DWORD maxMoviSize = sizeof(LIST) + sizeof(AVISTDINDEX)
        + (sizeof(CHUNK) + m_imageSize) * m_maxNumOfFrame
        + sizeof(AVISTDINDEX) + sizeof(AVISTDINDEX_ENTRY)*m_maxNumOfFrame
        - 8;
      write_movi( maxMoviSize );
    }
  
 
    // save imageData startPos
    saveNowFilePos( m_dataStartPos );
 
 
 
    m_numOfRiff++;
  }
 
  /**
   * exitRIFF
   */
  void exitRIFF()
  {
    saveSuperIndexPos();
 
    // RIFF終端辺を書き込み
    write_ix00();
    if ( m_isAVIX == false )
    {
      write_idx1();
      m_aviFrame = m_nowRiffFrame;
    }
    // AVIXヘッダ書き直し
    else
    {
      SetFilePointer( m_file, m_nowRiffFilePos.LowPart, &m_nowRiffFilePos.HighPart, FILE_BEGIN );
 
      // write RIFF-AVIX
      DWORD avixSize = sizeof(LIST) + m_headerJUNKSize 
        + sizeof(LIST) + (sizeof(CHUNK) + m_imageSize)*m_nowRiffFrame
        + sizeof(AVISTDINDEX) + sizeof(AVISTDINDEX_ENTRY) * m_nowRiffFrame
        //+ m_tailJUNKSize
        - 8;
      write_AVIX( avixSize );
 
      // write junk
      writeJUNK( m_headerJUNKSize );
 
      // write movi
      DWORD moviSize = sizeof(LIST)
        + (sizeof(CHUNK) + m_imageSize)*m_nowRiffFrame
        + sizeof(AVISTDINDEX) + sizeof(AVISTDINDEX_ENTRY) * m_nowRiffFrame
        - 8;
      write_movi( moviSize );
    }
 
    // ファイルヘッダの記述
    SetFilePointer( m_file, 0, NULL, FILE_BEGIN );
    writeHeader();
  }
 
 
  /**
   * 'AVI 'の最大格納数などを計算
   */
  void calcNewAVIParams()
  {
    // MAX_RIFF_SIZEに格納可能な最大フレーム数を計算する
    // 572および32は固定の必須データサイズ (MainAVIHeaderとか)
    m_maxNumOfFrame = (MAX_RIFF_SIZE - (572 + NEED_SUPER_INDEX_ENTRYS_SIZE + PADDING_GRANULARITY)) / (32 + m_imageSize);
 
    // 最大フレーム数格納時の、空きスペースを計算
    DWORD freeSize = MAX_RIFF_SIZE - (572 + NEED_SUPER_INDEX_ENTRYS_SIZE + PADDING_GRANULARITY + (32 + m_imageSize)*m_maxNumOfFrame) + PADDING_GRANULARITY;
 
    DWORD tmpHeaderJUNK = 0;
    m_tailJUNKSize = 0;
 
    if ( PADDING_GRANULARITY )
    {
      // 空きスペースから'movi'前のJUNKを抜き取り(2048境界を考慮して)
      tmpHeaderJUNK = PADDING_GRANULARITY - ((532 + freeSize - sizeof(CHUNK)) % PADDING_GRANULARITY);
 
      // 空きスペースから終端のJUNKサイズ分をぬきとり。
      m_tailJUNKSize = ((532 + freeSize - sizeof(CHUNK)) % PADDING_GRANULARITY) + sizeof(CHUNK);
    }
 
    // 空きスペースからAVISUPERINDEX_ENTRY分をぬきとり
    DWORD superIndexEntrySize = freeSize - tmpHeaderJUNK - m_tailJUNKSize;
    
    // 最小のJUNKを計算
    DWORD addHeaderJUNK = superIndexEntrySize % sizeof(AVISUPERINDEX_ENTRY);
    m_headerJUNKSize = tmpHeaderJUNK + addHeaderJUNK;
 
    // 格納可能なRIFF数を計算
    m_maxNumOfRiff = (superIndexEntrySize - addHeaderJUNK) / sizeof(AVISUPERINDEX_ENTRY) + MIN_NUM_OF_RIFF;
 
    m_aviHeaderJUNKSize = m_headerJUNKSize;
  }
 
  /**
   * 'AVIX'の最大格納数などを計算
   * 必要:m_nowRiffPos, m_imageSize
   */
  void calcNewAVIXParams()
  {
    if ( PADDING_GRANULARITY )
    {
      m_headerJUNKSize = PADDING_GRANULARITY - ((m_nowRiffFilePos.LowPart + sizeof(LIST) + sizeof(LIST)) % PADDING_GRANULARITY);
    }
    else
    {
      m_headerJUNKSize = 0;
    }
 
    m_maxNumOfFrame = (MAX_RIFF_SIZE - (sizeof(LIST) + m_headerJUNKSize + sizeof(LIST))) / (sizeof(CHUNK) + sizeof(AVISTDINDEX_ENTRY) + m_imageSize);
    m_tailJUNKSize = MAX_RIFF_SIZE - (sizeof(LIST) + m_headerJUNKSize + sizeof(LIST) + (sizeof(CHUNK) + sizeof(AVISTDINDEX_ENTRY) + m_imageSize)*m_maxNumOfFrame + sizeof(AVISTDINDEX));
  }
 
 
 
  /**
   * saveNowFilePos
   */
  void saveNowFilePos( LARGE_INTEGER& pos )
  {
    pos.QuadPart = 0;
    pos.LowPart = SetFilePointer( m_file, 0, &pos.HighPart, FILE_CURRENT );
  }
 
  /**
   * saveSuperIndexPos
   */
  bool saveSuperIndexPos()
  {
    if ( isOpen() )
    {
      LARGE_INTEGER superIndexPos = {0};
      saveNowFilePos( superIndexPos );
 
      AVISUPERINDEX_ENTRY entry = {0};
      entry.dwSize = sizeof(AVISTDINDEX) + sizeof(AVISTDINDEX_ENTRY)*m_nowRiffFrame;
      entry.dwDuration = m_nowRiffFrame;
      entry.qwOffset = superIndexPos.QuadPart;
      m_superIndexEntryList.push_back( entry );
 
      return true;
    }
 
    return false;
  }
 
  /**
   * write_AVIX
   */
  bool write_AVIX( DWORD size )
  {
    if ( isOpen() )
    {
      LIST avix;
      avix.dwList = mmioFOURCC('R','I','F','F');
      avix.dwSize = size;
      avix.dwFourCC = mmioFOURCC('A','V','I','X');
 
      DWORD writeSize;
      WriteFile( m_file, &avix, sizeof(avix), &writeSize, NULL );
 
      return true;
    }
 
    return false;
  }
 
  /**
   * write_odml
   */
  bool write_odml()
  {
    if ( isOpen() )
    {
      DWORD writeSize;
 
      LIST odml;
      odml.dwList = mmioFOURCC('L','I','S','T');
      odml.dwSize = sizeof(LIST) + sizeof(AVIEXTHEADER) - 8;
      odml.dwFourCC = mmioFOURCC('o','d','m','l');
      WriteFile( m_file, &odml, sizeof(odml), &writeSize, NULL );
 
      AVIEXTHEADER dmlh = {0};
      dmlh.fcc = mmioFOURCC('d','m','l','h');
      dmlh.cb = sizeof(dmlh) - 8;
      dmlh.dwGrandFrames = m_totalFrame;
      WriteFile( m_file, &dmlh, sizeof(dmlh), &writeSize, NULL );
 
      return true;
    }
 
    return false;
  }
 
  /**
   * write_movi
   */
  bool write_movi( DWORD size )
  {
    if ( isOpen() )
    {
      LIST movi;
      movi.dwList = mmioFOURCC('L','I','S','T');
      movi.dwSize = size;
      movi.dwFourCC = mmioFOURCC('m','o','v','i');
 
      DWORD writeSize;
      WriteFile( m_file, &movi, sizeof(movi), &writeSize, NULL );
 
      return true;
    }
 
    return false;
  }
 
  /**
   * write_ix00
   * ファイルポインタがimageData直後である必要がある
   */
  bool write_ix00()
  {
    if ( isOpen() )
    {
      // write std index
      AVISTDINDEX standardIndex = {0};
      standardIndex.fcc = mmioFOURCC('i','x','0','0');
      standardIndex.cd = sizeof(standardIndex) + sizeof(AVISTDINDEX_ENTRY)*m_nowRiffFrame - 8;
      standardIndex.wLongsPerEntry = 2;
      standardIndex.bIndexSubType = 0;
      standardIndex.bIndexType = 1;
      standardIndex.nEntriesInUse = m_nowRiffFrame;
      standardIndex.dwChunkID = mmioFOURCC('0','0','d','b');
      standardIndex.qwBaseOffset = m_dataStartPos.QuadPart;
      standardIndex.dwReserved = 0;
 
      DWORD writeSize;
      WriteFile( m_file, &standardIndex, sizeof(standardIndex), &writeSize, NULL );
 
      AVISTDINDEX_ENTRY entry = {0};
      entry.dwSize = m_imageSize;
      for (DWORD i=0; i<m_nowRiffFrame; ++i)
      {
        entry.dwOffset = (sizeof(CHUNK) + entry.dwSize) * i;
        WriteFile( m_file, &entry, sizeof(entry), &writeSize, NULL );
      }
 
      return true;
    }
 
    return false;
  }
 
 
 
  /**
   * write_idx1
   */
  bool write_idx1()
  {
    // 'idx1'
    if ( isOpen() )
    {
      // write index header
      CHUNK index;
      index.dwFourCC = mmioFOURCC('i','d','x','1');
      index.dwSize = sizeof(AVIINDEXENTRY) * m_nowRiffFrame;  
 
      DWORD writeSize;
      WriteFile( m_file, &index, sizeof(index), &writeSize, NULL );
 
      // write index chunks
      AVIINDEXENTRY entry = {0};
      entry.ckid = mmioFOURCC('0','0','d','b');
      entry.dwFlags = AVIIF_KEYFRAME;
      entry.dwChunkLength = m_imageSize;
      for (DWORD i=0; i<m_nowRiffFrame; ++i)
      {
        entry.dwChunkOffset = (sizeof(LIST) - 8) + (sizeof(CHUNK) + entry.dwChunkLength)*i;
        WriteFile( m_file, &entry, sizeof(entry), &writeSize, NULL );
      }
 
      return true;
    }
 
    return false;
  }
 
  /**
   * writeJUNK
   * @param size CHUNK分含む
   */
  bool writeJUNK( DWORD size )
  {
    if ( isOpen() &&
      size >= sizeof(CHUNK) )
    {
      // write junk chunk
      CHUNK junk;
      junk.dwFourCC = mmioFOURCC('J','U','N','K');
      junk.dwSize = size - sizeof(junk);
 
      // write junk data
      std::vector< BYTE > junkData( size );
      ZeroMemory( &junkData[0], junkData.size() );
 
      CopyMemory( &junkData[0], &junk, sizeof(junk) );
 
      DWORD writeSize;
      WriteFile( m_file, &junkData[0], junkData.size(), &writeSize, NULL );
 
      return true;
    }
 
    return false;
  }
 
  /**
   * writeHeader
   * dmlh記述のためラストに一括して書き込むことになる
   */
  bool writeHeader()
  {
    DWORD writeSize;
    const DWORD headerJUNKSize = m_aviHeaderJUNKSize + (m_maxNumOfRiff - m_superIndexEntryList.size()) * sizeof(AVISUPERINDEX_ENTRY);
 
    // write file header
    {
      AVIHeader header = {0};
      header.setData( m_width, m_height, m_aviFrame, m_fps, m_superIndexEntryList.size(), headerJUNKSize );
 
      WriteFile( m_file, &header, sizeof(header), &writeSize, NULL );
    }
 
    // write super index entry
    {
      DWORD size = m_superIndexEntryList.size() * sizeof(AVISUPERINDEX_ENTRY);
      WriteFile( m_file, &m_superIndexEntryList[0], size, &writeSize, NULL );
    }
 
    // write 'odml'
    write_odml();
 
    // write header junk
    writeJUNK( headerJUNKSize );
 
    // write movi
    write_movi( sizeof(LIST) + sizeof(AVISTDINDEX) + (sizeof(CHUNK) + m_imageSize + sizeof(AVISTDINDEX_ENTRY)) * m_aviFrame - 8 );
 
    return true;
  }
 
 
  HANDLE m_file;
  DWORD m_nowRiffFrame;
  DWORD m_totalFrame;
  DWORD m_numOfRiff;
  DWORD m_maxNumOfFrame;
  DWORD m_maxNumOfRiff;
  std::vector< AVISUPERINDEX_ENTRY > m_superIndexEntryList;
  
 
  LARGE_INTEGER m_nowRiffFilePos;
  LARGE_INTEGER m_dataStartPos;
 
 
 
  DWORD m_imageSize;
  DWORD m_headerJUNKSize;
  DWORD m_tailJUNKSize;
 
  bool m_isAVIX;
 
  DWORD m_width, m_height;
  DWORD m_fps;
  DWORD m_aviFrame;        //< RIFF-AVI 内のフレーム数
  DWORD m_aviHeaderJUNKSize;    //< RIFF-AVI 内のimageData前のJUNKサイズ
};
 
 
 
 
 
 
struct ObjPoint
{
  float x, y, vx, vy;
};
 
 
 
LRESULT CALLBACK wndProc(
  HWND hWnd,
  UINT msg,
  WPARAM wParam,
  LPARAM lParam )
{
  static DIB32 back, image;
  static ObjPoint point[ 4 ];
  static AVIRecorder avi;
  switch (msg)
  {
  case WM_DESTROY:
    avi.close();
    ShowWindow( hWnd, SW_HIDE );
    PostQuitMessage(0);
    break;
  case WM_CREATE:
    back.create( WINDOW_WIDTH, WINDOW_HEIGHT );
    image.create( TEXT("image.bmp") );
 
    point[0].x = 100;
    point[0].y = 100;
    point[1].x = 100;
    point[1].y = 200;
    point[2].x = 200;
    point[2].y = 100;
    point[3].x = 200;
    point[3].y = 200;
    srand( GetTickCount() );
    for (int i=0; i<4; ++i)
    {
      point[i].vx = (static_cast<float>( std::rand() & 1023 ) / 1023.f) * 32 - 16;
      point[i].vy = (static_cast<float>( std::rand() & 1023 ) / 1023.f) * 32 - 16;
    }
    avi.create( TEXT("output.avi"), back.getWidth(), back.getHeight(), FPS );
    break;
  case WM_PAINT:
    {
      PAINTSTRUCT ps = {0};
      HDC hdc = BeginPaint( hWnd, &ps );
      back.render( hdc, hWnd );
      EndPaint( hWnd, &ps );
    }
    break;
  case WM_TIMER:
    ZeroMemory( back.getPixelAddr(), back.getWidth() * back.getHeight() * 4 );
    for (int i=0; i<4; ++i)
    {
      point[i].x += point[i].vx;
      point[i].y += point[i].vy;
      if ( point[i].x < 0 || point[i].x > WINDOW_WIDTH ) point[i].vx = -point[i].vx;
      if ( point[i].y < 0 || point[i].y > WINDOW_HEIGHT ) point[i].vy = -point[i].vy;
    }
 
    {
      DIB32::Point p1, p2, p3, p4;
      p1.x = point[0].x;
      p1.y = point[0].y;
      p1.u = 0;
      p1.v = 0;
      p2.x = point[1].x;
      p2.y = point[1].y;
      p2.u = 0;
      p2.v = 32;
      p3.x = point[2].x;
      p3.y = point[2].y;
      p3.u = 32;
      p3.v = 0;
      p4.x = point[3].x;
      p4.y = point[3].y;
      p4.u = 32;
      p4.v = 32;
      image.triangle< DIB32::PutColorAdd >( back, p1, p2, p3 );
      image.triangle< DIB32::PutColorAdd >( back, p2, p3, p4 );
 
      InvalidateRect( hWnd, NULL, FALSE );
      avi.update( back );
    }
 
    {
      TCHAR title[ 128 ];
      wsprintf(title, TEXT("frame: %d(%dsec)"), avi.getTotalFrame(),avi.getTotalFrame()/FPS );
      SetWindowText( hWnd, title );
    }
    break;
  default:
    return DefWindowProc( hWnd, msg, wParam, lParam );
  }
 
  return 0;
}
 
 
 
 
 
 
 
int WINAPI WinMain(
  HINSTANCE hInstance,
  HINSTANCE, PSTR, int )
{
  LPCTSTR WINDOW_NAME = TEXT("sample");
 
  WNDCLASSEX wc;
  wc.style    = CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc  = reinterpret_cast<WNDPROC>( wndProc );
  wc.cbClsExtra  = 0;
  wc.cbWndExtra  = 0;
  wc.cbSize    = sizeof( WNDCLASSEX );
  wc.hInstance  = hInstance;
  wc.hIcon    = NULL;
  wc.hIconSm    = NULL;
  wc.hCursor    = LoadCursor( NULL, IDC_ARROW );
  wc.hbrBackground= reinterpret_cast<HBRUSH>( GetStockObject(WHITE_BRUSH) );
  wc.lpszMenuName  = NULL;
  wc.lpszClassName= WINDOW_NAME;
  if ( !RegisterClassEx(&wc) ) return 0;
 
  LONG winWidth = WINDOW_WIDTH
    + GetSystemMetrics(SM_CXEDGE)
    + GetSystemMetrics(SM_CXBORDER)
    + GetSystemMetrics(SM_CXDLGFRAME);
  LONG winHeight = WINDOW_HEIGHT
    + GetSystemMetrics(SM_CYEDGE)
    + GetSystemMetrics(SM_CYBORDER)
    + GetSystemMetrics(SM_CYDLGFRAME)
    + GetSystemMetrics(SM_CYCAPTION);
  HWND hWnd = CreateWindowEx(
    0, WINDOW_NAME, NULL, WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME,
    CW_USEDEFAULT, CW_USEDEFAULT, winWidth, winHeight,
    NULL, NULL, hInstance, NULL);
  if ( !hWnd ) return -1;
 
  ShowWindow( hWnd, SW_SHOWNORMAL );
  UpdateWindow( hWnd );
 
  SetTimer( hWnd, TIMER_ID, 1000/FPS, NULL );
  
  MSG msg;
  for (;;)
  {
    if ( !GetMessage(&msg, NULL, 0, 0) ) break;
    TranslateMessage( &msg );
    DispatchMessage( &msg );
  }
 
  
  UnregisterClass( WINDOW_NAME, hInstance );
  return msg.wParam;
}

 これでようやくPVが好きに作れる。
ムービーファイルメーカーがavi2.0に対応していないのでなんにせよ一度変換しなければならないが。

■関連記事:
生産がす: 32bitDIB(1) 作成と破棄
生産がす: 32bitDIB(2) 画像読み込み
生産がす: 32bitDIB(3) 塗りつぶし
生産がす: 32bitDIB(4) DIBに描画
生産がす: 32bitDIB(5) ファイルに出力
生産がす: 32bitDIB(6) 拡大縮小描画
生産がす: 32bitDIB(7) DIBSection
生産がす: DIB(8) - 直線描画
生産がす: DIB(9) - 回転描画
生産がす: DIB(10) - 三角形描画
生産がす: 日記ちゃん半透明合成
生産がす: 32bitDIBから無圧縮AVI2.0

2012年6月5日火曜日

日記ちゃんアンドロイド

 衝動的にやっすいアンドロイドを注文したので、
いまのうちにと開発環境を整えることにした。
基本的には
初心者のためのAndroidアプリ開発
を参考に初期設定を、
日本語化は
Eclipse 3.7 Indigoを日本語化する方法 - 大人になったら肺呼吸
を参考に行った。

なにはなくともとりあえずHelloWorldだろうと
サイトの内容を順番に消化していってるが
HelloWorldなんて一切記述しなくとも表示されちゃうのな。作った気がしない。
あと根本的にアンドロイドの端末仕様が一切わからんのが問題だ。
スクリーンロックってなんだい?  ある程度端末馴れしてないといかんともしがたいな。
ファミコンでAとBどっちをジャンプに割り当てるかって感じの問題が発生している。

デバッグウィンドウの立ち上げにえらい時間がかかるな。
仮想的な端末起動でもしてるのかも。ぜんぜん調べてないけど。

2012年6月4日月曜日

日記ちゃん

月食だったが曇りで何も見えんぬ。
日食もくもりだったし、
ありえない
何かの間違いではないのか
 
 明後日の金星通過は見れたらいいなあと思うが
予報では曇り……

2012年6月3日日曜日

日記ちゃん

届いたー。地方は1日遅れ。
ケースきつきつだし、ディスクもきつきつだという
ディスクは真ん中押しながら取り出すと取り出しやすい。
ディスクを無理に引っ張ったらめっ。

2012年6月1日金曜日

ピストンコラージュ v0.9.2.4

Pixel - pxtone

 いつのまにやらピストンコラージュがバージョンアップしてた。
ぱっと見た感じではボリュームバーが追加されてたり、
デフォルト音源にバスドラムが二つ追加されてたり。
MIDIキーボードが使用できるようだが所持していないので使用感は不明だ。
リアルタイムにピコピコ言わせたい欲求をいくらか晴らせそう。安いの買おうかな。
今までの.ptcopはそのまま読めるが、
新しく作成した.ptcopは古いバージョンでは開けないので注意が必要。
あとバグというほどではないが音源選択状態が、ファイルを開くたびに変わったり変わらなかったりする。