書き殴り雑記


2011/09/14


『Singleplayer Offline Role-Playing Game』


現在ランスクエストやってます。ええ、やってます。
このゲームはセーブデータを一つしか扱えません。正道を歩かないゲーマーである俺には少しうざったいです。
なのでセーブデータ管理するツール作りました。共に邪道を歩んでも良いという人達はど~ぞ。

RQセーブデータ管理ツール ver110922
画面イメージ



以下、前作の「戦国ランス」と今作の「ランスクエスト」について思った事をつらつらと。


『戦国ランス』

『ランスクエスト』

ゲームを遊んでいると「ランスクエスト」はMMORPGを意識して作られているなあとは感じられます。
でも、改めて言うまでもないけど「ランスクエスト」はMMORPGでは無く ”Singleplayer Offline RPG” ですよ。
MMORPGで仕方なく執られる様なレベルのバランス調整(多くのユーザの不満を解消するとともに多少の別ユーザの不満を生む)なんて…多分要りませんよ。


具体的には Ver1.100 の”寄付”に関する(こっそり行われていた)仕様変更の事を言ってるのですけどね。


これ…多分ユーザ側からの要望で実装したものでは無いでしょ。アリスさん側の自己満足のためでしょ。

「苦労して開発したのにアッサリやり尽されても困る。ユーザ側には在っても不利益しか齎さないけど少しマゾ化しとこうか」

て感じにしか捉えられませんでした。



まあそんなこんなですけども「ランスクエスト」を遊んでいます。
ちょっと毒を吐いた感じがしないではないですが、多分「大帝国」への評価も重なったため発生してしまったのだと思います。


最後に一応言っときますけど、面白いですよ、遊べますよ、このゲーム。

「科目別でみると幾つか赤点を取ってはいるが、総合的にみると平均以上の  評価」

て感じのゲームです。「大帝国」とは違ってゲーム部分は存分に遊べているので俺は満足しています。





2011/05/25


『DirectShowを探究しよー! 23』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶



最後なので、載せるタイミングが見つからなくて流してきた事等をまとめて載せちまう。

  1. Direct Show
    実は立場的には旧世代の技術だったりします。
    Microsoft は Media Foundation へ移行させる腹積もり。
  2. ISampleGrabber
    実は将来消えちゃうかもしれない API です。ISampleGrabber にも其の記述があります。
    また実際に qedit.h(ISampleGrabber関連が宣言されているヘッダファイル) は Windows SDK v7.0 から除かれてます。
    理由、対処法は Where is qedit.h? 等ネット上に多数転がってるので探してみてください。
  3. vistaのサウンドレコーダーは「録音の開始」する前でも音量メーターが反応する
    少し詳しく書くと、自作サウンドレコーダーは「録音の開始」を押した後から音量メーターが反応しだします。押す前は無反応です。それに対してvistaのサウンドレコーダーは起動直後から音量メーターが反応します。この違いについての話です。
    実はvistaのサウンドレコーダーは結構面倒な事をしてコレを実現しているのではと考えています。
    具体的には ”録画用” 、”音量メーター用” と 2 つの ”フィルタグラフ” を構築している。そして、この 2 つの ”フィルタグラフ” を状況に合わせて連携(若しくは切替)させてる?
    「これを真似しても労に対して益が少なくない?」 とか思っちゃったワケで、今回の自作サウンドレコーダーでは録音している最中しか音量メーターは反応しません。
    きちんと真似したいと思う人達の為に 参考ページ を紹介しときます。少なくともコイツを使えば同じ様な動作を実装できます。
  4. プログラムで組み上げた ”フィルタグラフ” がどんな構成になっているか見たい!
    外部プロセスからのグラフのロード に解答があります。
    実際に今回作った自作サウンドレコーダーの ”フィルタグラフ” の構成を見てみましょう。
    wma保存 フィルタ連結図3
    「なん・・・だと・・・!?」
    「・・・”Smart Tee” という知らないフィルタの侵行を許している!?」『DirectShowを探究しよー! 17』

    とか言ってみましたけど、ICaptureGraphBuilder2::RenderStream の仕様です。詳しくは←の 「スマート ティー」 項を見てください。
    ”Smart Tee” の侵行を許したく無い場合は自分でピン接続をするか、”RenderStream” した後に ”Smart Tee” を除去(してピンを繋ぎ直す)する等すれば良いです。そこまでして何が得られるのかは疑問ですが。


という事でゴールです。その道の人なら数回で纏められそうな内容に 23 回も掛けましたが、何とか辿り着きました。


では、また来年。





2011/05/20


『DirectShowを探究しよー! 22』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶



「vistaのサウンドレコーダーを作ってみなイカ?」



長かったけどやっと辿り着いたよ…
とりあえず サウンド レコーダー(VC++9.0 source) を置いとく。
毎度言うけどエラー処理はきちんとしましょう。このソースそのまま使っちゃダメですよん。





2011/05/15


『DirectShowを探究しよー! 21』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


関連部分のソースを抜粋して張り付けてきたけど、特に問題は無かったよね?
ソースを部分抜粋しているから全体像が見辛くなってきているだろうけど…まぁ大丈夫としよう。


で、前回のプログラムを実行してみましたか?
きちんと音量メーターが動いたでしょう。

でもツッコミたい点もあったでしょう?
皆まで言わなくても判ってマス。代弁してあげましょう。


「音量メーターの反応が鈍いんですけど~!!」


はい、そういう事もあるでしょう。というか多くの環境ではそうでしょう。
という事で、今回からサンプルデータの取得間隔の調整に入っていきます。


まず、現在の音量メーターは SGCallback::BufferCB() が呼ばれるタイミングで更新されています。
SGCallback::BufferCB() が 1 秒に 1 回呼ばれるならば音量メーターも 1 秒に 1 回更新されます。SGCallback::BufferCB() が 0.1 秒に 1 回呼ばれるならば音量メーターも 0.1 秒に 1 回更新されます。
つまり、”音量メーターの反応が鈍い” というのは ”SGCallback::BufferCB() が呼ばれる間隔が空き過ぎてる” という事なのです。今回の仕様(音量メーターは SGCallback::BufferCB が呼ばれるタイミングで更新する)では、ね


「では SGCallback::BufferCB() はどの様なタイミングで呼ばれるのでしょうか?」

「答えは簡単です。”上流のフィルタからデータが渡された時” に呼ばれます」
「じゃ、”上流のフィルタからデータが渡される” のはどの時なのでしょうか?」
「答えは簡単です。録音時は ”キャプチャソースフィルタがデータを渡す準備ができた時” です」
「じゃ、”キャプチャソースフィルタがデータを渡す準備ができた時” というのはどの時なのでしょうか?」
「答えは簡単です。”キャプチャソースフィルタの持つ転送用バッファ一杯にデータが溜まった時” です」


という事です。

要するに、”キャプチャソースフィルタの持つ転送用バッファ一杯にデータが溜まった時”SGCallback::BufferCB() が呼ばれます。
これは、”キャプチャソースフィルタの持つバッファ” のサイズを調整してやれば SGCallback::BufferCB() の呼ばれるタイミングを制御できるという事です。
Visual C++
bool RecordWMA::Initialize(LPCOLESTR savepath, HWND notice, UINT message) {
      :
    // キャプチャソースフィルタを取得してフィルタグラフに登録
    // キャプチャソースフィルタの出力ピンに利用可能なバッファサイズ等を指示
    CComPtr<IPin> out;
    capture_->FindPin(audioCapture, PINDIR_OUTPUT, &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, TRUE, 0, &out);
    CComQIPtr<IAMBufferNegotiation> nego(out);
    ALLOCATOR_PROPERTIES prop;
    prop.cBuffers = 8;                     // バッファの個数
    prop.cbBuffer = long(44100 * 4 * 0.1); // バッファサイズ【(44.10kHz * stereo[2ch] * 16bit) * 0.1秒分のデータを格納可なサイズ】
    prop.cbAlign  = -1;                    // アラインメント(お任せ指定)
    prop.cbPrefix = -1;                    // プレフィックスのサイズ(お任せ指定)
    nego->SuggestAllocatorProperties(&prop);
      :
}
”capture_->FindPin” でキャプチャソースフィルタのキャプチャピンを取得しています。『DirectShowを探究しよー! 9』 で登場した getPin と役割は同じです。
其のキャプチャピンから ”IAMBufferNegotiation” インターフェイスを取得してバッファサイズを指示しているのですが、この処理はピンを接続する前でないと成功しません。ピンを繋げた後に変更しようとしても失敗するので注意。

今回は 0.1 秒分のデータを格納できるサイズのバッファを(8個)割り当ててみました
この結果、今まで 0.5 秒に 1 回(on 俺環境)呼ばれていた SGCallback::BufferCB() が 0.1 秒に 1 回のタイミングで呼ばれるようになりました。
何秒分のデータが渡されたかは BufferCB() の引数で渡される SampleTime で確認できます。




2011/05/10


『DirectShowを探究しよー! 20』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


20回目だってよアハハハハー。終わんねー。全く何とも終わんねー。

  1. 録音中の音声データを取得
  2. 取得した音声データを解析して音量情報を取得
  3. 音量情報を音量メーターに反映
まあ、ネタ無いよりはネタ続く方が良いだろう。って事で、早速やっていきますか。

前回 『DirectShowを探究しよー! 19』 で ”音量情報” は用意できました。
ここまで出来れば後はプログレスバーに其の値をセットするだけです。
Visual C++
enum {
    eTIMER_ID = 100,
    eMSG_NOTIFYVOLUMU = WM_APP
};

static INT_PTR onInitDialog(HWND dlg, WPARAM w, LPARAM l) {
      :
    // 録音機能初期化
    USES_CONVERSION;
    data->rec_.Initialize(T2OLE(data->tempfile_), dlg, eMSG_NOTIFYVOLUMU);
    data->recording_ = false;
    // 音量メーターの初期設定
    ::SendDlgItemMessage(dlg, IDC_PROGRESS_METER, PBM_SETRANGE, 0, MAKELPARAM(0, SHRT_MAX));
    ::SendDlgItemMessage(dlg, IDC_PROGRESS_METER, PBM_SETPOS, 0, 0);
    return FALSE;
}

static INT_PTR onNotifyVolume(HWND dlg, WPARAM w, LPARAM l) {
    ::SendDlgItemMessage(dlg, IDC_PROGRESS_METER, PBM_SETPOS, w, 0);
    return TRUE;
}

static INT_PTR CALLBACK DialogProc(HWND dlg, UINT msg, WPARAM w, LPARAM l) {
    switch (msg) {
        case WM_INITDIALOG:
            return onInitDialog(dlg, w, l);
          :
        case eMSG_NOTIFYVOLUMU:
            return onNotifyVolume(dlg, w, l);
    }
    return FALSE;
}
『DirectShowを探究しよー! 15』 から変更のある関数のみ抜き出しています。実際に追加編集されたコードは背景色が          になってる部分です。





2011/05/05


『DirectShowを探究しよー! 19』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


  1. 録音中の音声データを取得
  2. 取得した音声データを解析して音量情報を取得
  3. 音量情報を音量メーターに反映

  1. 入力を受け付けるメディア形式の指定
  2. サンプルの受け取り方法の指定
前回 「入力を受け付けるメディア形式の指定」 を済ませました。今回は 「サンプルの受け取り方法の指定」 を行います。

”サンプルグラバ” からサンプルデータを受け取る方法は以下の2通りあります。
  1. こちらが要求した時に、その時点のサンプルデータを受け取る
  2. 随時サンプルデータを取得して送って貰う
今回は 2 の方法が目的に適しているのでこちらを採用します。
Visual C++
bool RecordWMA::Initialize(LPCOLESTR savepath, HWND notice, UINT message) {
      :
    // サンプルグラバをフィルタグラフに登録
    // サンプルグラバが受け付けるメディアフォーマットを指定
    // サンプルデータをコールバック経由で取得するように設定
    CComPtr<SGCallback> cb(new SGCallback);
    cb->SetNoticeWindow(notice, message); // 通知先windowと通知メッセージidを指定
    sample->SetCallback(cb, 1);           // サンプル取得時に SGCallback の BufferCB() を呼び出す様に指示
      :
}
”SGCallback” という新たなクラスが出てきました。
ここで皆さん、お気づきだろうか? コイツ… CoCreateInstance せずに直接 new してやがります(いぁ、俺がやったんですけどね)
つまりコイツは OS に登録された COM オブジェクトじゃ無い! 一体何者なんだ!?(いぁ、俺が作ったんですけどね)
Visual C++
#include <qedit.h>

class SGCallback : public ISampleGrabberCB
{
private:
    ULONG ref_;    // com参照カウンタ
    HWND notice_;  // 通知先window
    UINT message_; // 通知メッセージsig
public:
    SGCallback(void) : notice_(NULL), ref_(0) {}
    ~SGCallback(void) {}
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {
        return E_NOINTERFACE;
    }
    STDMETHODIMP_(ULONG) AddRef() {
        return ++ref_;
    }
    STDMETHODIMP_(ULONG) Release() {
        if (--ref_ > 0) {
            return ref_;
        }
        delete this;
        return 0;
    }
    virtual HRESULT STDMETHODCALLTYPE BufferCB(double SampleTime, BYTE *pBuffer, long BufferLen) {
        if (BufferLen > 0 && notice_ != NULL) {
            // 【(16Bit[-32768 ~ 0(==無音) ~ 32767] * 2channel) * n】
            // で構成されたサンプルデータが渡されてくる(『DirectShowを探究しよー! 18』 で其うフォーマット指定しました)
            unsigned long long int value = 0;
            const short* p = reinterpret_cast<short*>(pBuffer);
            const short* end = reinterpret_cast<short*>(pBuffer + BufferLen);
            for (; p < end; ++p) {
                value += abs(*p); // overflow した場合は知らん
            }
            ::PostMessage(notice_, message_, WPARAM(value / (BufferLen / sizeof(short))), 0); // 平均値を通知
        }
        return S_OK;
    }
    virtual HRESULT STDMETHODCALLTYPE SampleCB(double SampleTime, IMediaSample *pSample) {
        // 使わないので実装しない
        return S_OK;
    }
    void SetNoticeWindow(HWND notice, UINT message) {
        notice_ = notice;
        message_ = message;
    }
};
すみません。↑で 「俺が作った」 と言っている様に唯の自作クラスです。特に別に凄い事があるわけではありません。

ISampleGrabberCB を継承したクラスを用意して ISampleGrabber::SetCallback に渡してやる」
”サンプルグラバ” から ”随時サンプルデータを取得して送って貰う” 時のお約束事です。やはり特に別に凄い事をしているワケではありません。

処理としては、独自に用意した SetNoticeWindow 関数で指定させたウィンドウに対して録音中の(その時々の)音圧平均値を通知し続けるだけです。なので
  1. 録音中の音声データを取得
  2. 取得した音声データを解析して音量情報を取得
  3. 音量情報を音量メーターに反映
なんて大げさに言いましたが、こちらも特に別に凄い事をやってるワケではありませんでした…


フィルタの初期設定が済んだなら、最後にフィルタを繋げて終わりです。
Visual C++
bool RecordWMA::Initialize(LPCOLESTR savepath, HWND notice, UINT message) {
      :
    // フィルタを繋ぐ
    capture_->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, audioCapture, grabberFilter, muxFilter);
      :
}





2011/04/30


『DirectShowを探究しよー! 18』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


  1. 録音中の音声データを取得
  2. 取得した音声データを解析して音量情報を取得
  3. 音量情報を音量メーターに反映

早速 ”フィルタグラフ””サンプル グラバ フィルタ” を追加します。
Visual C++
bool RecordWMA::Initialize(LPCOLESTR savepath) {
      :
    // サンプルグラバをフィルタグラフに登録
    CComPtr<IBaseFilter> grabberFilter;
    grabberFilter.CoCreateInstance(CLSID_SampleGrabber);
    graph->AddFilter(grabberFilter, L"sample grabber");
}
CoCreateInstance して AddFilter、もう見慣れた形のコードです。

これで ”サンプルグラバ” の登録は完了しました。
ただ、この ”サンプルグラバ” は登録するだけではダメです。初期設定をしてやる必要があります。
  1. 入力を受け付けるメディア形式の指定
  2. サンプルの受け取り方法の指定
設定してやる項目は以上の2点。

今回 「録音中の音声データを取得」 しようとしています。
つまり、必要なのは ”音声データ” です。”動画” とか ”画像” とかの ”音声” でないデータは要りません。また、 ”音声データ” といっても様々な形式があります。Windows の標準フォーマットとも云える PCM という無圧縮形式や、 MPEG1 等の圧縮された形式のデータ。これについても ”どの形式のデータ” が欲しいのかを指示してやる必要があります。
Visual C++
#include <qedit.h>

bool RecordWMA::Initialize(LPCOLESTR savepath) {
      :
    // サンプルグラバをフィルタグラフに登録
    CComPtr<IBaseFilter> grabberFilter;
    grabberFilter.CoCreateInstance(CLSID_SampleGrabber);
    graph->AddFilter(grabberFilter, L"sample grabber");
    // サンプルグラバが受け付けるメディアフォーマットを指定
    AM_MEDIA_TYPE mt = { 0 };
    WAVEFORMATEX wf = { 0 };
    mt.majortype  = MEDIATYPE_Audio;              // Audio データを受け付けるよ
    mt.subtype    = MEDIASUBTYPE_PCM;             // もっと詳しく言えば、PCM audio フォーマットのデータを受け付けるよ
    mt.formattype = FORMAT_WaveFormatEx;          // 更に詳しい情報は WAVEFORMATEX 構造体で指示するので pbFormat を見て
    mt.cbFormat   = sizeof(wf);                   // pbFormat に格納しているデータのサイズ
    mt.pbFormat   = reinterpret_cast<BYTE*>(&wf); // ブツはコイツだ
    wf.wFormatTag      = WAVE_FORMAT_PCM;                      // 欲しいデータは PCM 形式だ
    wf.nChannels       = 2;                                    // stereo(2channel)で
    wf.nSamplesPerSec  = 44100;                                // サンプリングレートは 44.1KHz
    wf.wBitsPerSample  = 16;                                   // 1サンプルの情報は2Byte品質で
    wf.nBlockAlign     = wf.nChannels * wf.wBitsPerSample / 8; // お約束1
    wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;   // お約束2
    CComQIPtr<ISampleGrabber> sample(grabberFilter);
    sample->SetMediaType(&mt);
      :
}
”ISampleGrabber” てのは ”サンプルグラバ” フィルタの機能にアクセスするためのインターフェイスです。”サンプルグラバ” とのやり取りはこのインターフェイスを使って行います。
最終的に保存する品質は 「44.1KHz 16Bit stereo品質」「WM ASF Writer」 に指示(『DirectShowを探究しよー! 7』)しているので、同じ形式のデータを要求してます。今回はキツキツにフォーマット指定してますが、大抵のケースではここまで厳密に指定する事は稀ですよん





2011/04/25


『DirectShowを探究しよー! 17』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


  1. 録音中の音声データを取得
  2. 取得した音声データを解析して音量情報を取得
  3. 音量情報を音量メーターに反映

「”録音中の音声データを取得” するにはどうすれば良いの?」

「Microsoft が用意してくれている ”サンプル グラバ フィルタ” を使えば簡単だよ!」


会話形式だと話を展開させるのが楽だよね…
ちなみに ”サンプルグラバ” を日本語に直訳すると ”標本の強奪者” って感じです。名前から其の役割が想像できますな。
まぁ兎も角、”サンプル グラバ フィルタ” を使って録音中の音声データを取得しましょう。


今回、”フィルタグラフ””サンプル グラバ フィルタ” という新たな ”フィルタ” を追加する事になります。
まず現在のフィルタ構成を再確認してみますか。
wma保存 フィルタ連結図
『DirectShowを探究しよー! 8』 で示したイメージです。これに ”サンプル グラバ フィルタ” を追加してこうします。
wma保存 フィルタ連結図2

では早速プログラムで実装していきましょう。





とか言った直後に何というかアレなんですが…次回に引きます。





2011/04/20


『DirectShowを探究しよー! 16』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


前回はソース張り付けただけで投げっ放したけど、特に解説は要らなかったよね?
ん、一般的な Windows プログラムの範疇から外れたコードでも無いし…まぁ大丈夫としよう。


で、前回のプログラムを実行してみましたか?
きちんと録音できたでしょう。

でもツッコミたい点もあったでしょう?
皆まで言わなくても判ってマス。代弁してあげましょう。


「音量メーターが動かないんですけど~!!」


はい、動きません。まだそんなコード組み込んでいませんから。
という事で、今回から音量メーター機能の実装に入っていきます。


まず、音量メーターを実装するためにしなければいけない事を列挙してみましょう。
  1. 録音中の音声データを取得
  2. 取得した音声データを解析して音量情報を取得
  3. 音量情報を音量メーターに反映
リストにすると3手順も要る感じになっていますが、実際に Direct Show が絡んでくるのは ⅰ だけです。

とりあえずやっていきましょうか。





2011/04/15


『DirectShowを探究しよー! 15』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


ウィンドウ周りのソースを載せます。
Visual C++
#include <tchar.h>
#include <windows.h>
#include <commdlg.h>
#include <commctrl.h>

#include "RecordWMA.h"

#include "resource.h"
// resource.h 内で以下を定義
//#define IDD_SOUNDRECORDER  101         // 「サウンド レコーダー」Dialog ID
//#define IDC_BUTTON_RECORD  1001        // 「録音の開始」Button ID
//#define IDC_STATIC_TIME    1002        // 「録音経過時間」Static Text ID
//#define IDC_PROGRESS_METER 1003        // 「音量メーター」Progress Control ID

#pragma comment(lib, "comctl32.lib")

enum {
    eTIMER_ID = 100
};

struct USERDATA {
    _TCHAR tempfile_[_MAX_PATH]; // 作業用ファイルパス
    RecordWMA rec_;              // 録音クラス
    bool recording_;             // true=録音中、false=待機中
};

static bool getSaveAsFileName(HWND owner, LPTSTR path) {
    _TCHAR t[_MAX_PATH];
    _tcscpy(t, _T("record.wma"));
    OPENFILENAME ofn = {0};
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = owner;
    ofn.lpstrFile = t;
    ofn.nMaxFile = _countof(t);
    ofn.lpstrFilter = _T("Windows Media オーディオ ファイル(*.wma)\0*.wma\0\0");
    ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLESIZING;
    ofn.lpstrDefExt = _T("wma");
    if (::GetSaveFileName(&ofn)) {
        _tcscpy(path, t);
        return true;
    }
    return false;
}

static INT_PTR onInitDialog(HWND dlg, WPARAM w, LPARAM l) {
    USERDATA* data = new USERDATA;
    // USERDATA を dialog と結びつける
    ::SetWindowLongPtr(dlg, DWLP_USER, reinterpret_cast<LONG_PTR>(data));
    // 作業用ファイルパスを求める
    _TCHAR tempdir[_MAX_PATH];
    ::GetTempPath(_countof(tempdir), tempdir);
    ::GetTempFileName(tempdir, _T("sr"), 0, data->tempfile_);
    // 録音機能初期化
    USES_CONVERSION;
    data->rec_.Initialize(T2OLE(data->tempfile_));
    data->recording_ = false;
    return FALSE;
}

static INT_PTR onDestroy(HWND dlg, WPARAM w, LPARAM l) {
    USERDATA* data = reinterpret_cast<USERDATA*>(::SetWindowLongPtr(dlg, DWLP_USER, NULL));
    data->rec_.Stop(); // 録音停止
    ::DeleteFile(data->tempfile_); // 作業用ファイル削除
    delete data;
    return TRUE;
}

static INT_PTR onTimer(HWND dlg, WPARAM w, LPARAM l) {
    if (w == eTIMER_ID) {
        USERDATA* data = reinterpret_cast<USERDATA*>(::GetWindowLongPtr(dlg, DWLP_USER));
        if (data->recording_) {
            LONGLONG length;
            data->rec_.GetLength(length);
            length = (length + 5000000) / 10000000; // 100 nanoseconds -> 1 seconds
            _TCHAR t[32];
            _stprintf(t, _T("%lld:%02lld:%02lld"), length / 3600, (length / 60) % 60, length % 60);
            ::SetDlgItemText(dlg, IDC_STATIC_TIME, t);
        }
        return TRUE;
    }
    return FALSE;
}

static INT_PTR onCancel(HWND dlg, WPARAM w, LPARAM l) {
    const UINT code = HIWORD(w);
    if (code == 0 || code == 1 || code == BN_CLICKED) {
        ::EndDialog(dlg, IDCANCEL);
        return TRUE;
    }
    return FALSE;
}

static INT_PTR onButtonRecord(HWND dlg, WPARAM w, LPARAM l) {
    const UINT code = HIWORD(w);
    if (code == 0 || code == 1 || code == BN_CLICKED) {
        USERDATA* data = reinterpret_cast<USERDATA*>(::GetWindowLongPtr(dlg, DWLP_USER));
        if (!data->recording_) {
            if (data->rec_.Record()) { // 録音開始
                ::SetTimer(dlg, eTIMER_ID, 1000, NULL);
                ::SetDlgItemText(dlg, IDC_BUTTON_RECORD, _T("録音の停止"));
                data->recording_ = true;
            }
        }
        else {
            data->recording_ = false;
            data->rec_.Pause(); // 録音一時停止
            _TCHAR path[_MAX_PATH];
            if (getSaveAsFileName(dlg, path)) {
                data->rec_.Stop(); // 録音終了
                if (::MoveFileEx(data->tempfile_, path, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) {
                    ::EndDialog(dlg, IDOK); // 終了
                    return TRUE;
                }
            }
            ::SetDlgItemText(dlg, IDC_BUTTON_RECORD, _T("録音の再開"));
        }
        return TRUE;
    }
    return FALSE;
}

static INT_PTR CALLBACK DialogProc(HWND dlg, UINT msg, WPARAM w, LPARAM l) {
    switch (msg) {
        case WM_INITDIALOG:
            return onInitDialog(dlg, w, l);
        case WM_DESTROY:
            return onDestroy(dlg, w, l);
        case WM_TIMER:
            return onTimer(dlg, w, l);
        case WM_COMMAND:
            switch (LOWORD(w)) {
                case IDCANCEL:
                    return onCancel(dlg, w, l);
                case IDC_BUTTON_RECORD:
                    return onButtonRecord(dlg, w, l);
            }
            break;
    }
    return FALSE;
}

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
    INITCOMMONCONTROLSEX cc;
    cc.dwSize = sizeof(cc);
    cc.dwICC = ICC_PROGRESS_CLASS;
    ::InitCommonControlsEx(&cc);
    ::CoInitialize(NULL);
    ::DialogBox(hInstance, MAKEINTRESOURCE(IDD_SOUNDRECORDER), NULL, &DialogProc);
    ::CoUninitialize();
}
環境によっては 「必要なライブラリに link されてない」 とか error が出るかもしれませんが、適当に対応してください。
C4995,C4996 warning が出るかもしれませんが、気になる人は該当関数を _s 系のセキュリティ強化版に差し替えてください。サンプルとして出すコードに _s 系の関数を使うのはまだ何か引っ掛かりがあるんだよなぁ




2011/04/10


『DirectShowを探究しよー! 14』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


「vistaのサウンドレコーダーを作ってみなイカ?」


皆忘れてるだろうから改めて言いました。何度も言いますがこれが最終目的です。


今更ですけど vista のサウンドレコーダーってどんなモノなのか確認しときましょう。
サウンドレコーダー

シンプルな画面構成です。画面の左側から
  1. 録音開始/録音停止ボタン
  2. 録音経過時間
  3. 音量メーター
  4. ヘルプボタン
という4つのパーツで構成されています。これなら真似するのも容易いです。

サウンドレコーダー(自前)
真似ました。ヘルプボタンは要らないので省きました。ダイアログ上に(左側から) ”Button”, ”Static Text”, ”Progress Control” コントロールを並べただけです。
自作サウンドレコーダの UI はこれでいきます。





2011/04/05


『DirectShowを探究しよー! 13』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


「とりあえず今までの録音関連のコードをクラス化してみなイカ?」


Visual C++
#include <tchar.h>
#include <dshow.h>
#include <atlbase.h>
#include <dshowasf.h> // for IConfigAsfWriter
#include <wmsysprf.h> // for WMProfile_V80_64StereoAudio

#pragma comment(lib, "strmiids.lib")

class RecordWMA {
private:
    CComPtr<ICaptureGraphBuilder2> capture_; // キャプチャグラフビルダー
    CComQIPtr<IMediaControl> control_;       // メディアコントロール
public:
    RecordWMA() {}
    ~RecordWMA() { Stop(); }
    bool Initialize(LPCOLESTR savepath);
    bool Record();
    bool Stop();
    bool Pause();                            // 一時停止
    bool GetLength(LONGLONG& length);        // 現在の録音時間を取得
private:
    bool getAudioDevice(IBaseFilter** audioDevice);
};


bool RecordWMA::Initialize(LPCOLESTR savepath) {
    Stop();
    capture_.Release();
    control_.Release();
    // キャプチャグラフビルダー作成
    capture_.CoCreateInstance(CLSID_CaptureGraphBuilder2);
    // フィルタグラフ作成
    CComPtr<IGraphBuilder> graph;
    graph.CoCreateInstance(CLSID_FilterGraph);
    // 対象とするフィルタグラフを ICaptureGraphBuilder2 に設定
    capture_->SetFiltergraph(graph);
    // キャプチャソースフィルタを取得してフィルタグラフに登録
    CComPtr<IBaseFilter> audioCapture;
    getAudioDevice(&audioCapture);
    graph->AddFilter(audioCapture, L"audio capture source"));
    // ファイル保存情報指定
    CComPtr<IBaseFilter> muxFilter;
    capture_->SetOutputFileName(&MEDIASUBTYPE_Asf, savepath, &muxFilter, NULL);
    // 「WM ASF Writer」の設定
    CComQIPtr<IConfigAsfWriter> config(muxFilter);
    config->ConfigureFilterUsingProfileGuid(WMProfile_V80_64StereoAudio);
    // フィルタを繋ぐ
    capture_->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, audioCapture, NULL, muxFilter);
    // 録音期間を指示
    REFERENCE_TIME end = MAXLONGLONG;
    capture_->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, audioCapture, NULL, &end, 0, 0);
    // 使い回せるように IMediaControl インターフェイスを取得して確保しとく
    control_ = graph;
    return true;
}

bool RecordWMA::Record() {
    if (control_ == NULL) {
        return false;
    }
    // 録音を開始するように指示する
    OAFilterState state;
    control_->GetState(1000, &state);
    if (state != State_Running) { // 現在録音が開始されていないなら
        control_->Run(); // 録音開始!
    }
    return true;
}

bool RecordWMA::Stop() {
    if (control_ == NULL) {
        return false;
    }
    // 録音を停止するように指示する
    OAFilterState state;
    control_->GetState(1000, &state);
    if (state != State_Stopped) { // 現在停止状態でないなら
        control_->Stop(); // 録音停止!
    }
    return true;
}

bool RecordWMA::Pause() {
    if (control_ == NULL) {
        return false;
    }
    // 録音を一時停止するように指示する
    OAFilterState state;
    control_->GetState(1000, &state);
    if (state == State_Running) { // 現在録音中なら
        control_->Pause(); // ポーズ!
    }
    return true;
}

bool RecordWMA::GetLength(LONGLONG& length) {
    if (capture_ == NULL) {
        return false;
    }
    // ICaptureGraphBuilder2::SetFiltergraph() で設定したフィルタグラフを取得
    CComPtr<IGraphBuilder> graph;
    capture_->GetFiltergraph(&graph);
    // 現在位置を取得(Time Format はデフォルトの Reference time という前提)
    CComQIPtr<IMediaSeeking> seek(graph);
    seek->GetCurrentPosition(&length);
    return true;
}

bool RecordWMA::getAudioDevice(IBaseFilter** audioDevice)
{
    assert(*audioDevice == NULL);
    // Windows に「デバイス列挙機能を備えたクラス呉れ」と要求する
    CComPtr<ICreateDevEnum> deviceEnum;
    if (FAILED(deviceEnum.CoCreateInstance(CLSID_SystemDeviceEnum))) {
        return false;
    }
    // 「音声入力デバイスを列挙してくれ」と具体的に要求
    CComPtr<IEnumMoniker> enumMoniker;
    if (FAILED(deviceEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory, &enumMoniker, 0))) {
        return false;
    }
    // (メンドクサイので)列挙された内の最初の音声入力デバイスを対象デバイスとする
    enumMoniker->Reset();
    CComPtr<IMoniker> moniker;
    if (FAILED(enumMoniker->Next(1, &moniker, NULL))) {
        return false; // 音声入力デバイスは1つも存在しないってよ…
    }
    // 「見つかったデバイス用のキャプチャフィルタ呉れ」と要求
    if (FAILED(moniker->BindToObject(0, 0, IID_IBaseFilter, reinterpret_cast<void**>(audioDevice)))) {
        return false;
    }
    // 「ありがとう」とお礼を言って去る
    return true;
}
とりあえずしてみました。録音の基本機能はこれで完成したぜ!ヤホーーーイ!
…クドいようだけど、実際はエラー処理をきちんと入れましょうね。

あ、新たに ”IMediaSeeking” てのが出てますけど、もう特に解説要りませんよね。何となく名前で判るよね。





2011/03/30


『DirectShowを探究しよー! 12』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


「とりあえず録音してみなイカ?」


Visual C++
bool initialize(LPCOLESTR savepath) {
    : // 『DirectShowを探究しよー! 10』 と重複部分なので省略
    
    // ★ お助けマンに録音期間を指示
    REFERENCE_TIME end = MAXLONGLONG;
    capture->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, audioCapture, NULL, &end, 0, 0);

    // 録音開始
    record(capture);

    const _TCHAR * const message = _T("メッセージボックスを閉じると録音を停止します。");
    ::MessageBox(NULL, message, _T("録音中..."), MB_ICONINFORMATION | MB_OK);

    // 録音停止
    stop(capture);

    return true;
}
とりあえずしてみました。これでもう録音できます。

    // ★ お助けマンに録音期間を指示
    REFERENCE_TIME end = MAXLONGLONG;
    capture->ControlStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, audioCapture, NULL, &end, 0, 0);
MSDN 見てもらうのが一番だろうけど、新しく出たこの処理について軽く解説しとく。

「実際に録音/録画を開始してから秒後から秒後までの期間のみを pickup してファイルとして保存したい」

なんて事を時には思うかもしれない。”ICaptureGraphBuilder2::ControlStream” てのは其れを実現するための method です。
今回の例だと ”録音開始直後” から ”録音終了まで” の間ファイルに保存して」 と指示してます。
所謂初期化、デフォルト処理ですね。コード化する事で ”最初から最後までを保存する” 事を明確にしているだけです。





2011/03/25


『DirectShowを探究しよー! 11』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


長かった下準備もやっと終わりました。今回から ”録音” 機能を実装していきます。
とはいったものの、録音機能の実装は割りと簡単にできます。
Visual C++
#include <dshow.h>
#include <atlbase.h>

#pragma comment(lib, "strmiids.lib")

// 録音開始処理
bool record(ICaptureGraphBuilder2* capture) {
    // ICaptureGraphBuilder2::SetFiltergraph() で設定したフィルタグラフを取得
    CComPtr<IGraphBuilder> graph;
    capture->GetFiltergraph(&graph);

    // 録音を開始するように指示する
    CComQIPtr<IMediaControl> control(graph);
    OAFilterState state;
    control->GetState(1000, &state);
    if (state != State_Running) { // 現在録音が開始されていないなら
        control->Run(); // 録音開始!
    }

    return true;
}

// 録音終了処理
bool stop(ICaptureGraphBuilder2* capture) {
    // ICaptureGraphBuilder2::SetFiltergraph() で設定したフィルタグラフを取得
    CComPtr<IGraphBuilder> graph;
    capture->GetFiltergraph(&graph);

    // 録音を停止するように指示する
    CComQIPtr<IMediaControl> control(graph);
    OAFilterState state;
    control->GetState(1000, &state);
    if (state != State_Stopped) { // 現在停止状態でないなら
        control->Stop(); // 録音停止!
    }

    return true;
}
新たに ”IMediaControl” てのが出てきました。
名前からも何となく判ると思いますけど、ストリームの ”再生” ”ポーズ” ”停止” といった状態を管理するためのインターフェイスです。

これも ”DirectShow” の仕様として
  1. ”フィルタグラフ” にストリーム状態の変更を指示したい場合、”IMediaControl” を借りて其れ経由で指示しろ(IMediaFilter 経由でも可…なんてツッコミは無しで)
  2. ”フィルタグラフ””IMediaControl” を(間接的に)持つ事。持って無い場合は即刻死刑
って感じに決まっています。”(間接的に)” って部分は、深く知りたく無い場合は忘れて良いです





2011/03/20


『DirectShowを探究しよー! 10』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


ん?前回唐突に終わったって?
もーいいじゃん、引きまくっても。寧ろ流れ的に引くべきじゃね。

…というわけで前回からの引きです。

  1. 録音した音声ファイル保存先パスの指定
  2. 各フィルタの必要に応じた初期化処理
  3. 登録した各フィルタを繋ぐ

”ICaptureGraphBuilder2” の助けを借りるとこうなります。

Visual C++
#include <dshow.h>
#include <atlbase.h>
#include <dshowasf.h> // for IConfigAsfWriter
#include <wmsysprf.h> // for WMProfile_V80_64StereoAudio

#pragma comment(lib, "strmiids.lib")

bool initialize(LPCOLESTR savepath) {
    // ★ キャプチャグラフビルダー作成
    CComPtr<ICaptureGraphBuilder2> capture;
    capture.CoCreateInstance(CLSID_CaptureGraphBuilder2);

    // フィルタグラフ作成
    CComPtr<IGraphBuilder> graph;
    graph.CoCreateInstance(CLSID_FilterGraph);

    // ★ お助け依頼契約締結
    capture->SetFiltergraph(graph);

    // キャプチャソースフィルタを取得してフィルタグラフに登録
    CComPtr<IBaseFilter> audioCapture;
    getAudioDevice(&audioCapture);
    graph->AddFilter(audioCapture, L"audio capture source"));

    // ★ お助けマンに「asf 形式で保存したいので準備して」と依頼
    CComPtr<IBaseFilter> muxFilter;
    capture->SetOutputFileName(&MEDIASUBTYPE_Asf, savepath, &muxFilter, NULL);
    // 依頼の結果、asf用のフィルタ「WM ASF Writer」を用意 + 保存先パスの設定をして貰える

    // 「WM ASF Writer」の設定
    CComQIPtr<IConfigAsfWriter> config(muxFilter);
    config->ConfigureFilterUsingProfileGuid(WMProfile_V80_64StereoAudio);

    // ★ お助けマンに「フィルタを繋いで」と依頼
    capture->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Audio, audioCapture, NULL, muxFilter);

    return true;
}
各関数の詳しい説明は MSDN で補完してね。

の付いた部分が ”ICaptureGraphBuilder2” 関連の処理です。見ての通り自分でゴチャゴチャ頑張る必要が無くなりました。
”ICaptureGraphBuilder2” のメソッドを呼ぶだけでテキパキと準備を進めてくれるので楽です。

とりあえず、コレで下準備は済みました。後は ”録音” を実行するだけです。





2011/03/15


『DirectShowを探究しよー! 9』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


答えは「下準備編をまだ続けちゃう」です。つーか、前回引いた時点で判ってんじゃん。

  1. 録音した音声ファイル保存先パスの指定
  2. 各フィルタの必要に応じた初期化処理
  3. 登録した各フィルタを繋ぐ

今回は巻きで行くので、いきなりソース載せます。
Visual C++
#include <dshow.h>
#include <atlbase.h>
#include <dshowasf.h> // for IConfigAsfWriter
#include <wmsysprf.h> // for WMProfile_V80_64StereoAudio

#pragma comment(lib, "strmiids.lib")

bool initialize(LPCOLESTR savepath) {
    // フィルタグラフ作成
    CComPtr<IGraphBuilder> graph;
    graph.CoCreateInstance(CLSID_FilterGraph);

    // キャプチャソースフィルタを取得してフィルタグラフに登録
    CComPtr<IBaseFilter> audioCapture;
    getAudioDevice(&audioCapture);
    graph->AddFilter(audioCapture, L"audio capture source"));

    // コンプレッションフィルタ兼ファイルライタである「WM ASF Writer」を取得してフィルタグラフに登録
    CComPtr<IBaseFilter> muxFilter;
    muxFilter.CoCreateInstance(CLSID_WMAsfWriter));
    graph->AddFilter(muxFilter, L"audio multiplexer"));

    // 出力先パス指定
    CComQIPtr<IFileSinkFilter> sinkFilter(muxFilter);
    sinkFilter->SetFileName(savepath, NULL)

    // 「WM ASF Writer」の設定
    CComQIPtr<IConfigAsfWriter> config(muxFilter);
    config->ConfigureFilterUsingProfileGuid(WMProfile_V80_64StereoAudio);

    // キャプチャソースフィルタと「WM ASF Writer」と繋ぐ
    CComPtr<IPin> capture_out, asfwriter_in;
    capture_out.Attach(getPin(audioCapture, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE, MEDIATYPE_Audio));
    asfwriter_in.Attach(getPin(muxFilter, PINDIR_INPUT, GUID_NULL, MEDIATYPE_Audio));
    graph->Connect(capture_out, asfwriter_in);

    return true;
}

// フィルタから指定のピンを取得する関数
IPin* getPin(IBaseFilter* filter, PIN_DIRECTION direction, GUID category, GUID mediatype) {
    CComPtr<IEnumPins> enumPins;
    // フィルタが持つピンを列挙
    if (SUCCEEDED(filter->EnumPins(&enumPins))) {
        IPin* pin;
        while (enumPins->Next(1, &pin, NULL) == S_OK) {
            // 列挙されたピンを順番にckeckしていく
            PIN_DIRECTION pd;
            if (FAILED(pin->QueryDirection(&pd)) || pd != direction) {
                // 向き(出力 or 入力)が違う!
                pin->Release();
                continue;
            }
            if (category != GUID_NULL) {
                // ピンカテゴリが指定されているのでcheck
                CComQIPtr<IKsPropertySet> prop(pin);
                GUID cate;
                DWORD ret;
                if (prop == NULL ||
                    FAILED(prop->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0, &cate, sizeof(GUID), &ret)) ||
                    cate != category
                ) {
                    // アウト!
                    pin->Release();
                    continue;
                }
            }
            bool hit = true;
            if (mediatype != GUID_NULL) {
                // major メディアタイプが指定されているのでcheck
                hit = false;
                CComPtr<IEnumMediaTypes> enummediatype;
                if (SUCCEEDED(pin->EnumMediaTypes(&enummediatype))) {
                    AM_MEDIA_TYPE* mt;
                    while (enummediatype->Next(1, &mt, NULL) == S_OK) {
                        const GUID mjtype = mt->majortype;
//                      ::DeleteMediaType(mt);
                        ::CoTaskMemFree(mt->pbFormat);
                        if (mt->pUnk != NULL) {
                            mt->pUnk->Release();
                        }
                        ::CoTaskMemFree(mt);
                        if (mjtype == mediatype) {
                            // 指定メディアタイプを許容してた!
                            hit = true;
                            break;
                        }
                    }
                }
            }
            if (hit) {
                // 有効なピンが見かった
                return pin;
            }
            pin->Release();
        }
    }
    return NULL;
}
これが ”ICaptureGraphBuilder2” 様の助けを受けない場合の下準備の全容です。改めて言うけどエラー処理はきちんとしようね。
getPin が少々”初見お断り”感を醸し出してます。

では ”ICaptureGraphBuilder2” の助けを借りた場合はどのようになるのでしょうか?





2011/03/10


『DirectShowを探究しよー! 8』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


今回で下準備を終わらせます。ていうか、流石に終わらない筈が無い。

  1. 録音した音声ファイル保存先パスの指定
  2. 各フィルタの必要に応じた初期化処理
  3. 登録した各フィルタを繋ぐ

今回の場合、↓の様にフィルタを繋げば良いのです。
wma保存 フィルタ連結図
①が ”キャプチャソース” で、②が ”コンプレッションフィルタ””ファイルライタ” である「WM ASF Writer」。
こうして見るとメチャシンプルです。今回はフィルタを2つしか利用しませんから。

では、実際に ”フィルタを繋ぐ” とはどういう処理を行うのでしょうか?
  1. フィルタの ”ピン” を取得
  2. 上流のフィルタの ”出力ピン” と下流のフィルタの ”入力ピン” をコネクトする
  3. αβを全フィルタに対して行う
という処理を行えば良いです。しかし、これが結構厄介なんです。何故かと言うと↓理由のため。
  1. フィルタに備わっている(入力、出力)ピンは1つとは限らない。複数存在し得る
  2. 各ピンには個別の役割がある
  3. 各ピンの役割に合わせて其々のピンを繋がなければならない
  4. フィルタの繋ぐ順番(上流から下流への流れ)を把握してピンを繋ぐ必要が在る
つまり、「このフィルタのこの出力ピンはこういう役割なので、あのフィルタのあの入力ピンと接続する」
って感じで、自力で繋いでいこうとすると細かい知識が求められます。

どうです?
貴方は大丈夫ですか?細かい知識が求められても大丈夫ですか?ピンとピンを地道に繋いでいくようなメンドクサイ作業に耐えられますか?

「大丈夫だ、問題ない」

なんて発言をする空気を読まない人とは此処でお別れです。次回再会しましょう。
もっと楽をしたい人は以下に続いてください。


DirectShow が提供しているインターフェイスに ”ICaptureGraphBuilder2” というものがあります。
MSDN ではこう説明されています。

The Capture Graph Builder is a helper object for building video and audio capture graphs. Capture and editing applications can use this component to construct filter graphs.

要すると 「”ICaptureGraphBuilder2” はお助けマンだ」 って事らしいです。 実際にどう”お助け”してくれるかを見てみましょう。


…ってスペース無くね?ソース載せる余裕無くね。

アレか?あれなのか?
今回で終わらないのか?下準備編がまだ続いちゃうのか?


答えは次回!





2011/03/05


『DirectShowを探究しよー! 7』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


今回で下準備を終わらせます。”終わらせます”ったら終わらせます。

  1. 録音した音声ファイル保存先パスの指定
  2. 各フィルタの必要に応じた初期化処理
  3. 登録した各フィルタを繋ぐ

基本的に要らないケースの方が多いのですが、「WM ASF Writer」はこの処理が要りやがります。
「WM ASF Writer」ってのは asf 形式のファイルを出力可能な ”ファイルライタ” です。メンドイので詳細は wikipedia に投げますが、asf って実は音声専用のフォーマットじゃ無いんです。動画もいけちゃうんです。
つまり「WM ASF Writer」ってのは動画/音声の両方に対応した ”ファイルライタ” なんです。

超一流のテレパスならば動画/音声のどちらで保存したいのかを”察し”てくれる可能性もありますが、普通は指示してやらないと判りません。
Visual C++
    // 「WM ASF Writer」フィルタ作成
    CComPtr<IBaseFilter> muxFilter;
    muxFilter.CoCreateInstance(CLSID_WMAsfWriter));

    // IConfigAsfWriter を取得して保存フォーマットを設定
    CComQIPtr<IConfigAsfWriter> config(muxFilter);
    config->ConfigureFilterUsingProfileGuid(WMProfile_V80_64StereoAudio); // streo 44.1KHz 64kbps 音声形式で保存する様に設定
”IConfigAsfWriter” てのは「WM ASF Writer」が独自に用意したインターフェイスです。”IFileSinkFilter” と違って他の ”ファイルライタ” は基本持っていません。
この独自インターフェイスを介してコンバートするフォーマットを指定します。上記コードだと ”64Bitステレオ音声形式” にコンバートする様に指示してます。
古い環境で開発している人は 「IConfigAsfWriter が無い」 とコンパイラに文句言われるかもしれません。↑でも書いた通り、 ”IConfigAsfWriter” は独自仕様で ”DirectShow” の仕様内のモノでは無いので。もし文句云われた人は「windows media format sdk」を入れてください。VC++6.0 とか使ってると多分引っ掛かります。「Windows SDK」を利用している環境なら大丈夫です。

CComQIPtr についてもチョイ書いとこう。簡単に言うと、CComQIPtr ってのは ”QueryInterface を自動でやってくれるクラス” です。
Visual C++
//  CComQIPtr<IConfigAsfWriter> config(muxFilter);
    CComPtr<IConfigAsfWriter> config
    muxFilter.QueryInterface(&config);
こう書いてるのと一緒です。


下準備如きに回数を取られるワケにはいかないんだが…微妙に長くなったなぁ。
「公約なんて守らなくても良い」 と現時点の日本国家与党様も言ってる事だし…次回に引こう ^-^





2011/02/30


『DirectShowを探究しよー! 6』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


下準備の残りをこなしていきます。

  1. 録音した音声ファイル保存先パスの指定
  2. 各フィルタの必要に応じた初期化処理
  3. 登録した各フィルタを繋ぐ

これは ”ファイルライタ” フィルタに対して指示する事になります。今回の場合だと「WM ASF Writer」が対象になります。
Visual C++
    // 保存先パス
    const WCHAR * const filepath = L"c:\\record.wma";

    // 「WM ASF Writer」フィルタ作成
    CComPtr<IBaseFilter> muxFilter;
    muxFilter.CoCreateInstance(CLSID_WMAsfWriter));

    // IFileSinkFilter を取得してファイルパスを設定
    CComPtr<IFileSinkFilter> sinkFilter;
    muxFilter->QueryInterface(IID_IFileSinkFilter, reinterpret_cast<void**>(&sinkFilter));
    sinkFilter->SetFileName(filepath, NULL)
QueryInterface の説明要りますかね?誤解上等でも要りますかね?やっといた方がよいですか。左様ですか。

「お前(今回は muxFilter)懐に良いモン持ってんじゃねぇか。ちょっとその●●●●(今回は ”IID_IFileSinkFilter” )って名前のクラス借してくれ」

というタカリ行為を可能とする関数です。


ちょっと話がズレますが、QueryInterface は以下の様にしても同じ結果です。
Visual C++
//  muxFilter->QueryInterface(IID_IFileSinkFilter, reinterpret_cast<void**>(&sinkFilter));
    muxFilter.QueryInterface(&sinkFilter);
「何が違うの?」 と興味が出た人は違いについて調べて見て下さい。「はぁ?」 と思った人はスルーしましょう。混乱するだけですから。
簡単に説明しとくと ”IBaseFilter::QueryInterface” を呼んでいるか ”CComPtr<????>::QueryInterface” を呼んでいるかの違いです。


DirectShow は ”仕様” として以下の取り決めを行っています。
  1. ”ファイルライタ” にファイルの保存先を指示したい場合、”IFileSinkFilter” を借りて其れ経由で指示しろ
  2. ”ファイルライタ””IFileSinkFilter” を持っておく事。持って無い場合は即刻死刑
この取り決めに因って、仮に「WM ASF Writer」以外(aviやmp4の動画やwav、mp3といった他の音声形式用)の ”ファイルライタ” で在っても
"IFileSinkFilter"を借りてそれに対して保存先パスを指示する」
という手順を踏む事でファイル保存先パスの指定が可能です。


さてと…詰め込んでも見難いだけだろうから次回に引きましょう。




2011/02/25


『DirectShowを探究しよー! 5』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


  1. 録音するので音声入力デバイスから音声を取得する機能が必要 (キャプチャソース
  2. vistaのサウンドレコーダーはWMA形式で音声を保存するので、WMA形式に変換する機能が必要 (コンプレッションフィルタ
  3. 音声ファイルとして保存する必要があるので、ファイルに書き出す機能が必要 (ファイルライタ
『DirectShowを探究しよー! 3』 でリストアップした3つのフィルタですが、必要な情報は全て揃いました。次は ”フィルタグラフ” にこれらのフィルタを組み込みます。
Visual C++
#include <dshow.h>
#include <atlbase.h>

#pragma comment(lib, "strmiids.lib")

bool initialize() {
    // フィルタグラフ作成
    CComPtr<IGraphBuilder> graph;
    graph.CoCreateInstance(CLSID_FilterGraph);

    // キャプチャソースフィルタを取得してフィルタグラフに登録
    CComPtr<IBaseFilter> audioCapture;
    getAudioDevice(&audioCapture);
    graph->AddFilter(audioCapture, L"audio capture source"));

    // コンプレッションフィルタ兼ファイルライタである「WM ASF Writer」を取得してフィルタグラフに登録
    CComPtr<IBaseFilter> muxFilter;
    muxFilter.CoCreateInstance(CLSID_WMAsfWriter));
    graph->AddFilter(muxFilter, L"audio multiplexer"));
    return true;
}
これだけです。エラー処理は一切無視してますが実際には真似しない様に。
AddFilter の第2引数で指定している文字列は unique であれば何でも良いです。適当な名前をつけてください。

これで下準備第一段階が終わりました。
以前に例で挙げた自作マシン組立ての工程で言うと、マザーボードにCPU、メモリといったパーツをハメ込んだトコでしょうか。これから配線やらに取り掛かっていく段階です。

今後やる事を具体的に挙げてみます。
  1. 録音した音声ファイル保存先パスの指定 (超一流のテレパス相手ならば一々指示するまでも無いのでしょうが、多くの場合は指示してやらないと判ってくれません)
  2. 各フィルタの必要に応じた初期化処理 (フィルタに依っては個別の初期化処理が必要なモノもあります)
  3. 登録した各フィルタを繋ぐ (『DirectShowを探究しよー! 2』 で紹介したgrapheditの画面を思い出してください。各フィルタが ”線” で繋がってますね)
上記をやり遂げれば下準備は完了です。





2011/02/20


『DirectShowを探究しよー! 4』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


後は ”キャプチャソース” フィルタを手に入れれば、必要なフィルタは全て揃う段階まできました。

しかし…実は ”キャプチャソース” は画一に 「コレを使えば良い」 と決められる類のフィルタでは無いのです。
外部からの入力デバイスは ”ハードウェア” です。音声入力デバイスと言っても様々なモノ(マイク、エレキピアノ、エレキギター、etc...)があります。また、同じ種のデバイスでも安物と高価なデバイスでは性能差も激しくなるでしょう。

と、これらの事もあり万能で汎用的な ”キャプチャソースフィルタ” は通常用意されません。
基本的に ”ハードウェア” を担当した側の手で ”入力デバイス” 専用のDirectShowフィルタが用意されます。

”入力デバイス” 専用ですので、それこそ ”入力デバイス” の数だけ ”キャプチャソースフィルタ” が存在しえます。
また、マシン環境により使用できる(インストールされている) ”キャプチャソースフィルタ” も変わってきます。

前回の『WM Asf Writer』の様にプログラムを組んだ段階で 「君に決めた!」 とはできません。
なので、 ”キャプチャソース”マシン環境にインストールされている ”キャプチャソース” フィルタを動的に探す事になります。
Visual C++
#include <dshow.h>
#include <atlbase.h>

#pragma comment(lib, "strmiids.lib")

bool getAudioDevice(IBaseFilter** audioDevice)
{
    assert(*audioDevice == NULL);
    // Windows に「デバイス列挙機能を備えたクラス呉れ」と要求する
    CComPtr<ICreateDevEnum> deviceEnum;
    if (FAILED(deviceEnum.CoCreateInstance(CLSID_SystemDeviceEnum))) {
        return false;
    }
    // 「音声入力デバイスを列挙してくれ」と具体的に要求
    CComPtr<IEnumMoniker> enumMoniker;
    if (FAILED(deviceEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory, &enumMoniker, 0))) {
        return false;
    }
    // (メンドクサイので)列挙された内の最初の音声入力デバイスを対象デバイスとする
    enumMoniker->Reset();
    CComPtr<IMoniker> moniker;
    if (FAILED(enumMoniker->Next(1, &moniker, NULL))) {
        return false; // 音声入力デバイスは1つも存在しないってよ…
    }
    // 「見つかったデバイス用のキャプチャフィルタ呉れ」と要求
    if (FAILED(moniker->BindToObject(0, 0, IID_IBaseFilter, reinterpret_cast<void**>(audioDevice)))) {
        return false;
    }
    // 「ありがとう」とお礼を言って去る
    return true;
}
コード内で CComPtr とか出てますが、このクラスは COM インターフェイスの扱いを助けてくれるクラスです。詳しくはググってくだせい。
難しい事はやってませんね。大したコード量では無いから MSDN 等を参照して読み解けば難しくないでしょう。





2011/02/15


『DirectShowを探究しよー! 3』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


「vistaのサウンドレコーダーを作ってみなイカ?」


多分皆忘れてるだろうから改めて言ってみました。これが最終目的です。


じゃ今回からプログラミングに入っていきます。が、何故サウンドレコーダー?って点について説明しとく。
  1. 音声入力デバイスは殆どのパソコンに備わっている (キャプチャボードなんてなくてもマイク等の音声入力機器を買って来る程度で環境が整う)
  2. 再生を扱うよりは入力をキャプチャして録音する方が楽しそう (主観)
  3. vistaサウンドレコーダーはシンプル過ぎるインターフェイスなので簡単に真似が可能 (形の無いモノを追うよりは既存のモノを再現する方が楽に理解が進む気がする)
適当に決めたワケでは無くそれなりに考えてるんですよん。

DirectShow では ”フィルタグラフ” というベースになるモノが存在します。このフィルタグラフに対してフィルタを登録し、次に登録したフィルタを繋げていきます。
他のモノに例えて言うと、パソコンのマザーボード(M/B)みたいなものだと思っても良いでしょう。M/Bにメモリ、HDD等のパーツを装着し、次に装着したパーツの配線を行っていく。
似ていません?


まず、”フィルタグラフ” を用意しましょう。
Visual C++
#include <dshow.h>

#pragma comment(lib, "strmiids.lib")

IGraphBuilder* graph;
::CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&graph);
”IGraphBuilder”、コイツが所謂 ”フィルタグラフ” です。
このコードを真に理解しようとすると ”COM” に関する知識が必要となるのですが…当然その辺は飛ばします。
C++的観点から誤解上等!で云うと
「COM = 便利なクラスをOSで管理して皆で使えるようにして幸せになろう」
て感じのモノです。

”CLSID_FilterGraph” という識別子で OS に登録されている ”IID_IGraphBuilder” って名前のクラスを一つ呉れよ!」
って感じに、居酒屋で注文するノリをプログラム化したモノが上記の CoCreateInstance です。


次に使用するフィルタを登録していきます。

「vistaのサウンドレコーダーを作ってみなイカ?」
という事なので必要な機能をリストアップしていきましょう。
  1. 録音するので音声入力デバイスから音声を取得する機能が必要 (キャプチャソース
  2. vistaのサウンドレコーダーはWMA形式で音声を保存するので、WMA形式に変換する機能が必要 (コンプレッションフィルタ
  3. 音声ファイルとして保存する必要があるので、ファイルに書き出す機能が必要 (ファイルライタ
はい。最低限必要だと思われる機能のリストアップが完了しました。後は上記機能を持った DirectShow フィルタを ”探せば良い” だけです。
えぇ… ”誰かが作ってくれているであろう” フィルタを頑張って探しましょう。自分で作っちゃうのも当然アリですけど。


とりあえず今回必要なフィルタは Microsoft 様が用意してくれてました。

『WM Asf Writer』(今時の Windows には標準インストールされてるはず)

このフィルタは ”コンプレッションフィルタ””ファイルライタ” が一緒になったフィルタで、asf(WMA)形式への変換とファイルへの書き出し機能の両方を備えています。
”コンプレッションフィルタ””ファイルライタ” はこれで用意できました。


今回はココまで。
次回は ”キャプチャソース” を探す旅に出ます。





2011/02/10


『DirectShowを探究しよー! 2』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


ぶっちゃけ DirectShow って何?
はい。wikipedia でも見て下さい。

一応シンプルに説明しとくと、Windows OS 上で動画や音声を扱える技術の事です。
他の人達が用意してくれた ”パーツ” を組み合わせる事で簡単に動画等の再生、録画、効果付け等が可能となる、楽して益を得たい人は大喜びの代物です。

基本的な事(動画再生とか録画ね)をするだけなら、 ”パーツ” を繋ぎ合せるだけで可能。
プログラムを組む側としては、この ”パーツ” をどう繋げるかを指示するだけで大方の仕事は完了です。

言葉で言ってもピンとこないと思うのでイメージ図で見てみましょう。MSが提供してくれている graphedit (Windows SDK に含まれています。本来禁止のはずですが、単体配布しているサイトもあります)というツールの画面イメージです。
プログラムでは無く、視覚的に ”パーツ” の繋ぎ合わせを実行できる DirectShow を扱う上では欠かすことの出来ない便利ツールです。

avi再生 フィルタ連結図
これは、とあるaviファイルを再生する際に ”フィルタ” がどのように繋がって動画再生が行われるかの図です。
此処までで散々 ”パーツ” ”パーツ” 言ってきましたが、正しくは ”DirectShow フィルタ” (以後 ”フィルタ” と呼称)って言います。

詳しい解説は他サイトで得られると思うので、各 ”フィルタ” がどのような役割をしているかの簡単な説明だけしときます。

ソースフィルタファイルからデータを読み込む
パーサフィルタ読み込まれたデータを解析して画像と音声に分割、再生フレーム単位に分割等する
デコンプレッションフィルタ(画像)圧縮された画像データを展開する上記例だと「Microsoft Video 1(CRAM)」⇒「RGB24」
オーディオレンダラ音を鳴らす
エフェクトフィルタ渡されたデータを加工する上記例だと「RGB24」⇒「RGB32」に色深度を変換(この時のマシンの画面色深度が32bitなので)
ビデオレンダラ画像表示

それぞれのフィルタが其々の役割をこなす事で動画の再生が実現されています。
この流れはどんな形式の動画ファイルであっても基本的に変わりません。
今回は avi 形式の動画でしたが、仮に mp4 形式の動画を再生する場合でも②パーサフィルタ、③デコンプレッションフィルタ(画像、音声)辺りを mp4 形式用のモノに変えてやれば ”論理上” 再生されます。
上図では使用されてませんが音声用のデコンプレッションフィルタ(今回のaviで使用されていた音声は元々無圧縮wavなので必要無い)もある。加えて言えば、動画保存時に使用されるコンプレッションフィルタ(画像、音声)や動画、音声キャプチャ用のキャプチャソース(入力デバイスからデータをloadするソースフィルタの亜種?みたいなもの)、メディアストリームをファイルに書き出す(保存する)ファイルライタとかもある。

最初の方でも言いましたが、これらのフィルタを組み合わせてやれば良いんです。
各動画フォーマットに精通していなくても動画、音声を簡単に扱う事ができちゃいます。ん~簡単だぜ!!





2011/02/05


『DirectShowを探究しよー! 1』


「DirectShowを探究しよー!」(CV:森永佳奈) >>挨拶


気付いたら2010年も終わってました。気付いたら7ヶ月更新してませんでした。
という事で急遽雑記を書く事にしました。

が…当然の事ながら特にネタが無い。在ったらとっくに書いてます。

敢えて採り上げるとするならば、また日本”ド腐れ低能”ユニセフ様が個人情報満載の嫌がらせDMを送りつけてきた事程度でしょうか。


驚くべき事に、今回届けられた送付物の内容は2年前と一切違いがありませんでした。
ホント呆れました、というかバカなんでしょうか?。
てめえら様はマジで”何”を活動してるのでしょうか?

此方が教えた覚えも無いのに…その住所当てにDMを送りつけたら興味を持ってくれるとでも思ってるの?
同封された印刷物に俺の名前をフンダンに散らしとけば親近感を持ってくれるとでも思ってるの?
俺の住所と氏名を勝手に印刷したシール10セットを送りつけたら喜んでくれるとでも思ってるの?
振込み先と此方の住所氏名を勝手に記入した振込み書を同封しとけば何とかなると思ってるの?

改めて言う。バカじゃねぇの?
後始末に困るって言ってんだろが。こんな事に使う金があるならソレをお前らが”必要”と言ってる場所に回せよ。


という様に、ネタとして採り上げても不毛な内容にしかならない事は明明白白なので、コレ以上は触ってやりません。


で、話は最初に戻りますが、ネタがありません。でもネタが無い事には雑記を書けません。となるとネタを探さないといけません。でもネタが(ry

とまぁ…永久回廊を漂っていても前に進めませんので、苦心の末に何とか捻り出したネタが今回のタイトルです。
大雑把な予定としては

「vistaのサウンドレコーダーを作ってみなイカ?」

という筋で行くつもりです。


地文は最後までできてるので多分更新間隔は維持できるハズ? (html化が面倒なので遅れる事も在っちゃうかなぁ…)





2010/07/16


『SG 最悪選挙戦王決定戦 in 大阪 part5』


靡く赤旗
赤井最終マーク。鮮やかな逆転劇でしたね。
あさひああ。休日の8:15から街宣車活動で眠っている住民を叩き起こす。去ったと思い2度寝した30分後に再登場。そこから15分以上に渡る街頭演説。
…圧倒的だった。
赤井その街頭演説の内容も
『消費税はダメだ。そう思うならば共産グループ』
『アメリカの犬は嫌だ。そう思うならば共産グループ』
のループで15分以上ですからね。
あさひ加えて、街頭演説が終了し静かになったと思わせた10分後に颯爽と街宣車で再再登場するサプライズ、そして騒音と共に再び去る。止めとしては十分だ。
赤井清水の前では岡部の休日8:45からの街宣車活動なんて可愛く思えます。
あさひここまでされればもう眼が冴えて二度寝はできない。寝ようと粘ってみても時間の無駄にしかならん。
あさひ10:15に桂禁止が街宣車で来た時には『睡眠時間確保を諦めるきっかけを呉れてありがとう』て感じだったからな。
この事からも清水…というか共産党グループの凄さが判る。
赤井その清水がウィニングランで聴衆に応えています。清水は初優勝、共産グループは前回レースに続いての連続優勝です。
赤井見事でした清水。そして、共産グループの底力を見せ付けたレースでした。
あさひそういえば…5号艇は何してたんだ?
⑤ 北川組織票主体で追い風にも乗った地味な活動してましたから…このレースでの見せ場なんてありませんでした。



”普通に書いても面白くない”、”08:15からの共産党の愚行によって起こされた後、友人と住之江に観戦に行ったから”
という短絡的思考で競艇で例えてみたんだけど…必要以上に間延びしてしまった。

主旨は『共産党大阪支部は選挙活動のやり方を考え直せよ』て事です。
平日は未だしも、国家が定めた休日の早朝に大騒音を撒き散らす事がどのような結果を生むか想定すらできない低能なの?

毎度毎度懲りもせずに迷惑行為を犯して…あんたらの目的は何なの?選挙に当選する事?人に嫌がらせ行為を行う事?
前者だというならアンタ達は確実に間違っている。
後者だというのなら…アンタ達の行動は正しかったよ。ただ、人として間違った正しさだ。




2010/07/15


『SG 最悪選挙戦王決定戦 in 大阪 part4』


3周 1マーク
赤井3週目。トップ2号艇、4挺身離れて1号艇。このまま最後までいくのか!?
② 岡部プライドに掛けても当選しなきゃいけない。ココで勝負を掛けるわよ!
② 岡部必殺!! 休日の8:45から街宣車活動~!
② 岡部!?
赤井あぁっと!ターンミス!? 2号艇大きくふくらんだぁ!!
あさひ素人が調子に乗るから…他人が不快感じない事を”普通”にヤってりゃ良いのによぉ…
あさひ休日早朝に叩き起こされた奴らが持っていた浮動票は諦めるしかないな。
赤井大混戦! 勝負は最終2マークまでもつれた!!
赤井2号艇先マイ!
赤井1号艇はインに突っ込んだ!!
② 岡部ナイトスクープ!ナイトスクープ!!ナイトスクープゥゥゥ!!!
① 清水・・・共産グループ伝統の”勝負賭け”を見せてやる!!
!?
赤井あぁッ!!
あさひこれは…強烈だ。
赤井1号艇逆転した? 逆転だ!! 半挺身前に出た!!
赤井清水今ゴールイン!! 続いて2号艇岡部が入りました。
赤井『SG 最悪選挙戦王決定戦 in 大阪』を制したのは共産グループの清水隆史でした!
126435




2010/07/14


『SG 最悪選挙戦王決定戦 in 大阪 part3』


2週 1マーク
⑥ 桂岡部尾立が暴走して荒れればとは思っていたが…本当にチャンスが回って来たわい!
⑥ 桂このまま共産野郎も抜いてやるか。元松竹など…元吉本の敵では無い!
赤井2週1マーク。6号艇外に振った! 差しだ!!
⑥ 桂『小さなことからコツコツとぅ~~!』
① 清水!? 他人の褌で勝負してんじゃねぇよ!!!
赤井決まるか!!!
赤井未だだ! 1号艇耐えた!!
⑥ 桂シツコイのぅ。お笑い100万票は吉本のモンなんじゃ。諦めい。
① 清水馬鹿か。民主グループに所属している時点でお笑いだろうとなんだろうとお前の敵だよ!
⑥ 桂フン、そんな事は疾うに判っておる。だから…ワシは既に”民主”である事は隠しとるわ!!
⑥ 桂小さなことからコツコツとぅ。今回比例代表で出馬した桂禁止です。桂禁止をよろしくお願いしますぅ!
① 清水!? 所属グループ名言えよ! ”民主から”比例代表で出馬した』 と誠実に伝えろよ!!
赤井2週バックストレート。6号艇が半挺身前に出た! 直線の伸びは6号艇が上だ!!
あさひ準優までとは伸びが違う。コイツ、グループ仲間から貰ったエースペラを温存してやがったな。
赤井2週2マーク。6号艇はイン。
赤井1号艇は…外! ツケマイだ!!
⑥ 桂小さなことからコツコツとぅ~。西川きょしの志を継いだ桂禁止をよろしくお願いしますゥ!
① 清水他人の威光で勝とうとする様な奴に…
① 清水所属グループに誇りを持っていない様な奴に…
① 清水真っ赤に染まった俺が敗けるかよ!!!
⑥ 桂なんだと!?
赤井強烈! 1号艇のツケマイが決まったァ!!
216435




2010/07/13


『SG 最悪選挙戦王決定戦 in 大阪 part2』


スタートライン
赤井さあ、発走の合図と共に6選手がピットから飛び出します。
赤井各艇が一斉に飛び出した。全艇ピット離れは良好。これはどのような進入となるか。
赤井おっと。スタート展示とは異なり2号艇が枠主張している! これはこのまま入りそうだ。
赤井6号艇が後方に引っ張った。それに続いて5号艇。
赤井進入は枠なりです。隊形は2、2、2。インコースから枠なり進入でスタートです。
赤井6号艇からしぶきが舞った。5号艇少し起こし遅れた!
赤井続けてイン各艇からも水柱が上がった!
赤井4号艇、6号艇が好スタート!
赤井1周1マーク、4号艇一気にマクリにいく! 1号艇突っ張る!
赤井2号艇は差しに廻る! 3号艇は…呑まれたか!!
④ 尾立厳しい選挙区なのに2人も立てるんじゃねぇ! バカオzaワァァァァ!!
① 清水お前らミンスは3人で共喰いでもしてろ!!
② 岡部ナァイトスクープゥゥゥ!!!
③ 石川信心が無い人達は余裕が無さ過ぎですね…折伏してあげましょうか?
赤井4号艇外に流れた! 1号艇は残った!!
赤井2号艇差してトップ! まくり差した6号艇が最内3番手!
赤井上位大勢は2番、1番、6番。スタートは正常。このまま2週目に入ります。
216435




2010/07/12


『SG 最悪選挙戦王決定戦 in 大阪 part1』


展示
赤井さあ、未だ梅雨も空けず鬱陶しい天気の元、今回も最悪選挙戦王決定戦が此処大阪で開かれる事となりました。
赤井このレースは選挙戦でどれだけ迷惑行為・迷走行為を働いたかを競い合うレースです。
あさひ誰も開催を望んでねぇんだがな…
赤井実況は赤井邦道。解説はあさひ梅克でお送りします。では先程行われた周回展示を振り返ってみましょう。
赤井1号艇、清水隆史共産グループ所属。スタート展示では1枠インに入りました。
あさひ元お笑いの元大阪市議。お笑い100万票で”先生”になった類かね。ただ、国政選挙で共産グループが入り込む余地は大阪には無いからなあ。
形振り構わず無茶してくるだろう。優勝の最右翼だ。
赤井2号艇、岡部マイ民主グループ所属。スタート展示では4枠カドを取りました。
あさひ”知名度は一番在る”というプライドが邪魔してか、必死な姿を公衆に晒したくない感じだな。知名度があるから何とかなると思ってんだろう。
赤井3号艇、石川崇宏公明グループ所属。スタート展示では2枠に入りました。
あさひ組織票を大量に持ってるから無難に流してレースを終えるだけだろう。政教分離糞喰らえって感じだな。2枠に入ったのもガツガツしていたのでは無く成行きで入っただけだ。
赤井4号艇、尾立基行民主グループ所属。スタート展示では3枠に入りました。
あさひスタート展示なのに3号艇に落とされた岡部を内に入れなかったな。同じ民主グループなのに岡部を完全に敵視している。
赤井5号艇、北川二成自民グループ所属。
あさひ組織票は纏まっているし今回は追い風も吹いている。大きなミスさえしなければ結果を出せるレースだ。賭けに出ず、枠なりにレースをするだけだろう。
赤井6号艇、桂禁止民主グループ比例代表派所属。大外6枠です。
あさひ大外の6号艇、ペラも合っていないし整備で調整できる術も無い。お笑い100万票と民主グループの力が頼みだな。
赤井以上、6名によって争われます。さあ、まもなくレース開始です。




2010/06/27


『LGPL』


少し昔、或るオープンソースのライブラリで提供されている機能を利用したい状況となった。
ただライブラリを解説をして呉れていた(非公式)ページによると、其のライブラリはLGPL(ver2.1)ライセンスで提供されているためソース公開の義務が発生するとの事だった。
もう少し詳しく言うと、動的リンクした場合はソース公開の必要は無いが、静的リンクした場合は要ソース公開という内容だった。(その時の背景を言うと、俺が開発対象とした環境は動的リンク機構が提供されていなかったので静的リンクしか不可だった)

開発しようとしていたプログラムは商用利用が前提のものだったので、ソース公開が必要となるこのライブラリは利用できない。
そのため無償ライブラリは諦め、有償で提供されている他ライブラリを利用しようという結論となった。

で、其の有償ライブラリの提供元から色々と話を聞いたのだが、なんと有償ライブラリで提供されていた(こちらが必要としている)機能は上記で挙げたオープンソースライブラリを用いて実装している事が判った。

「そのオープンソースライブラリを利用するとソース公開の義務が発生するのでは?」

と尋ねたところ、

「そんな事は無いですよ。元のライブラリを改変等しなければ(オリジナルのままリンクするだけならば)大丈夫です」

という回答を頂いた。

だが、LGPLの利用許諾契約書(日本語訳)を当たってみた所…貰った回答は正しく無い気がする。
その有償ライブラリは俺が開発対象としている環境専用のライブラリだ。(つまり静的リンク専門のライブラリ)
となると、(↓で紹介する)LGPL第5、6項で引っ掛かってるのだが…


まぁ、上記の事があって改めてLGPL(Ver2.1)契約書を自分で読んでみた。
だが、GPLと比較すると難解な契約書だったので

「何を言ってんの、お前?」
「何の事を言ってんの、お前?」
「もうチョイ解り易く言えよ、お前!」

と愚痴りながら、繋がらない部分は俺解釈で紡ぎつつ何とか解きほぐしていった。


その時得た結論を今回公開する事にした。何故かというと…他に話のネタが無いから。
以下俺流解釈。

LGPL(Ver2.1)ライセンスで提供されている著作物を利用したければ、以下の事を守れ。
  1. 利用するLGPL著作物の著作権を表示しろ。後、問題が起きても責任は無い旨も宣言しろ。
  2. LGPL著作物のソースを改変したいならば以下を守れ
    1. 改変したものは”ライブラリ”として提供しろ。”アプリ”とかではダメだ。
    2. 元ライブラリで提供されていた機能は改変されたライブラリからも利用できるようにしろ。勝手に利用不可にすんな。
  3. 改変されたライブラリのライセンスをLGPLからGPLに変更する事は許可するよ。その他のライセンスへの変更は却下だ。
  4. ”ライブラリ”はソースも公開しろ。
  5. LGPL著作物(=ライブラリね)を単にLink(=動的リンク)しているだけの実行ファイルはLGPLの制限を受けない。別ライセンスで頒布する事も可だ。
    だけど、LGPL著作物を内包(=静的リンク)して作成した実行ファイルは別の話だ。詳しくは 6 項見ろ。
  6. LGPL著作物を内包して作成した実行ファイル自体は好きな条件(ライセンス)で頒布しても良い。が、条件がある。
    まず、リバースエンジニアリングを許可しろ。「逆コンパイル禁止!」とかほざくなよ。
    後、内包しているLGPL著作物について告知しろ。LGPL自体がどういうモノなのかもユーザに判る様に案内しろ。
    もうひとつ、ユーザ側でその実行ファイルを作成できる状況を用意しろ。
    例を挙げ説明すると、
    『内包しているLGPL著作物(ライブラリ)がバージョンアップ等された際、実行ファイルに内包されているライブラリ部をユーザ側でアップデート(して実行ファイルを作り直す事が)できる』
    ようであれば条件を満たしているハズだ。
    これを満たすにはソース公開が簡単な方法だろうが、上記が可能な手段が提供されているならソース公開は必須ではない。
  7. LGPL著作物と他ライセンス制限下のライブラリを混合して作成したライブラリを頒布したければ以下を守れ。
    1. オリジナルのライブラリも一緒に頒布しろ。
    2. オリジナルライブラリの著作権表示をして、オリジナルの入手先も示せ。
  8. この契約書で許可されている形以外での頒布は不可だ。勝手に解釈すんなよ。
  9. LGPL著作物を扱った時点でライセンス条項を受け入れたと見做す。後で文句言うなよ。
  10. LGPL制限下の著作物に対して新たな制限を加える事はできない。「これはLGPLライセンスで頒布する。だが通常のLGPLに加えて、”長門は作者の嫁と認めない者は利用不可”という制限も追加してる。守れよ」とか勝手に言っても無駄だ。
  11. 法的判断によってLGPLを否定する判断をお前が得たとしてもLGPL制限下からは逃れられない。LGPLを守らないと云うなら使うな!
  12. 独裁国家等の不条理が通る環境で運用される可能性もあり、其れを考えるとLGPLを守れない可能性がある、とか言うなら、その国を頒布対象外と宣言すれば問題無し。
  13. LGPLのバージョンが指定されていた場合は、其のバージョンのLGPL制限を受ける。未指定ならばお前が望むバージョンを対象として良い。
  14. LGPLと矛盾するライセンス著作物と結合したいのならば、該当作者と話し合って特例を貰う事で解決するのはアリだ。俺らは其処まで頑なじゃ無い。
  15. LGPL著作物によって損害が出たとしても保障とか無いからな。
  16. もちろん責任とかも負わないから。これだけ前もって言ってるのだから裁判とかしても無駄だかんな!

幾つか「俺に関係無いや」って感じで無視して省いた項目もあったハズ。
なので、本気で確認したい人は原文を見るように。
後、これはLGPL(Ver2.1)についての解釈。今はLGPL(Ver3)も存在する。

原文(Ver2.1)の日本語訳ページ
GNU 劣等一般公衆利用許諾契約書(日本誤訳)




2010/04/03


『tweet』




2010/03/27


『5D状態に陥った事は3年間隠せ』(2年待った程度ではダメそうな感じですよ…(ノTДT)ノ)


また「わいどにょ」の御話。

窟Lv191において、とうとう召還石、宝箱双方から仲間にできる全キャラクターが揃った。
まぁ、”全”とは言っても”召還石 + 宝箱”の”全”てであって女の子モンスターはまだまだ残ってるんですが…

ちなみに、現在仲間にしている女の子モンスターは36匹中の11。後25体も残っている。
最近は、大体窟を5Lv進む毎に1回異空窟に挑戦している。で、異空窟を平均2回(確率的には運が良い方かな)クリアする度に女の子モンスターを1体仲間にできている。
単純計算すると、窟を10Lv進む毎に女の子モンスター1体。つまり、後(最短で)250Lv程窟を潜ったら全員仲間にできる。

130時間以上プレイして窟Lv191まで進んできたが、まだ折り返し地点にすら達していないって事かぁ。どれだけやり込ませるつもりなんだ…
というか、アイテムも”唯一無二”全15種の内の8種しかまだ入手していないし…当分やり込み要素が尽きる事は無さそうだ。




前回雑記で書いた「わいどにょ」のユーティリティ「にょ」の公開。
にょ
にょ
してます。

通常再起動すると5秒程度掛かるのだが、この機能を使えば1秒程で再起動可能。ターン開始時の敵の沸き数が少ないと即再起動している俺にとってはかなり快適になった。
加速の方は一応組み込んだが、「わいどにょ」は基本スムーズにストレス無くゲーム進行していくので速度変更するメリットは無い気がする。
「わいどにょ」の速度変更のため少々怪しいと勘違いされかねない事をやってるので、チェック過剰なアンチウィルスソフト等は警告出してくるかもしれない。まぁ、其処は信用してもらうしか無いか。


もう一つ、おまけとして前回雑記で書いた加速器っぽいツールも公開。
加速器
加速器

◎使い方
  1. リストに表示されている対象プロセスをダブルクリックして加速対象に登録
  2. 対象プロセスを選択している状態で、スライダーで速度指定

タイマでフレームレートを管理しているアプリケーションにのみ有効。
vsyncでフレームレートを管理しているアプリケーションには効かない。アプリやグラボによっては設定等からvsyncを切る事も可能だったりするので、vsyncを切れば期待通りに動く可能性があるかもしれない。



2010/03/21


『ルナプレイヤー?あぁ都市伝説の話ね』


にょの再起動の機能だけど、最初のムービーも自動で飛ばせねえの?」
「ん?あぁできるでしょ。多分」
「じゃ、ソレやってくれよ」

という友人との話がきっかけで、にょの「再起動」機能で最初のムービーも飛ばせるように対応した。(多分、後日まともな形になったら公開するハズ)

「ムービー飛ばせるのは良いんだけど、ムービー切り替わり時の画面フェードで時間が掛かる(ムービー表示時、ムービー非表示時に其々1秒程)のどうにかならない?」
「其処はわいどにょにLockされてるんでどうにもならんわ」
「加速すれば一瞬で飛ばせたりしないか?」
「加速??」

ゲームの処理速度を変更できるツール(加速器というらしい)があるらしい。で、ソレと同じような事をすれば「画面フェード部分を飛ばせるだろ」と言いたかった様だ。

調べてみたところ、”加速”を実現する方法の中には簡単に実装できそうな方法もあった。
なので、いきなりにょに組み込むのではなく、とりあえず簡易”加速器”と言えるものを組んでみた。

だが、その”加速器”を友人の方で試してもらったところ、旨く機能するゲームもあればダメなものもあった。
でも、その事は「わいどにょでは旨く機能しているので構わないや」で片付けた。





実は↑は前振りで、ここからが本題。


「旨くいかないゲームがあるからお前も来い」と言われたので友人宅へ行った。

なのに、何故か『東方紅魔郷』のルナティックモードをやらされる事になった。
確か 『東方紅魔郷』は、フルスクリーンだとダメだがウィンドウモードだと加速できる」 とかいう話をしていたハズなんだが…


俺は、遥か昔に『東方永夜抄』のノーマルを一回だけ(攻略法を頭に入れた上でギリギリ)クリアした事が有る。
東方プレイ暦はそれだけだ。他はROMって話の粗筋程度を知ってるだけ。攻略関係の情報なんて全く頭に入っていない。

『東方紅魔郷』のプレイなんて初見の初見。そんな奴にルナティックとかjk的に無茶振り過ぎる。


が、俺は逃げなかった。
この挑戦を受ける事にした。
漢として戦わずして退く事などできぬ!


「やってやろうじゃないか!」


俺は(何故かわざわざ奴に背を向けた後)背中越しに言い放った。


「やってやろうじゃないか!」

「”加速器”で処理速度を通常の20%に落とした状態でな!!」


その時の俺はかなり格好良かったはずだ。もしかしたら後光が射していたかもしれない。





…何も言うな。てか、前振りの内容からこの展開は予想できたでしょ。


プレイ内容を実況等しても意味無いから結果だけ言うと…

コンティニュー3回使ってもクリアできませんでした (゚∇^*) テヘ♪



ちゃうねん。

改めて言うが初見だぜ。東方って覚えゲーなのに初見でルナだぜ。ルナ全てを気合い避けで勝負していくんだぜ。
それに(変なプライド見せて…)霊夢Bだぜ。気合い避けするなら回避に全力を掛けなきゃならんのに、ホーミングしないバスウェイジョンニードルだぜ。
後、確かにクリアできなかったケド…ゲーム進行率は97.60%までいったんだぜ。レミリア最後のスペル「レッドマジック」さえ凌げばクリアだったんだぜ。

いや…ちゃうねん。自機数さえ…、オプション設定で初期自機数を3機では無く5機に変更さえしていたら、クリアできてたんだって!


だから、俺に向かって「ダメ男w」って言うんじゃねぇ!!

つーか、”ルナティックをコンティニュー無しでクリアする程度の能力”なんて都市伝説だろ!
そんなん信じちまって「ルナティックはクリア可能」って思っている奴の方が「ダメ男w」なんですよ m9(^Д^)


ナニイッテルノ。ツヨガリナンカジャナイデスyo。ヤメテモラエマセンカッ。ソウイウコトワ…



・速度20%でやってみた感想
速度20%にすると確かに弾は良く見える。が故に、通常ならボム使うような状況でも無理やり避けようとしてしまう。針の穴を通そうとしてしまう。結果、ボム抱え死してしまう。全ボム抱え死を2回しちまったからなぁ…「ダメ男w」言われても仕方ないかも。
後、喰らいボムの判定が厳し過ぎる事を改めて感じた。速度20%の状態でも3~4割程度しか発動できなかった。



2010/03/07


『にょがあれば後2年は戦える!』(2年あればランスⅧ出る…よね)


今回は「わいどにょ」というゲームを紹介する。
わいどにょ
このゲームは少し特殊なゲームである。
まず、ストーリー性というものがほぼ存在しない。
そして、エンディングというものも存在しない。「終わりが無いのが終わり」「諦めたら、そこが終了ですよ」なゲームである。

趣旨としては「トルネコの大冒険」「風来のシレン」のような”やりこみ系”のゲームに近いのだろうか。
…いや、これらのゲームには一応ストーリーとエンディングが存在し、其れらは大きなファクターを占めているので少し違うか?

ストーリー性に重点が置かれていないゲームで”やりこみ系”となると…何が当てはまるのだろうか?
条件だけで見ると「シムシティ」とかが当て嵌まりそうだが、「わいどにょ」とは90°ぐらい別方向を向いている気がする。

残念ながら、俺の浅いゲーム知識内においては該当する物が見当たらない。
とりあえず、”「トルネコ」「シレン」からストーリー性を取り去って永久回廊化したゲーム”という紹介で良いだろうか?
只管ダンジョンを潜り続けるだけのゲームだ。うん、大体そんな感じだ。



だがこの結論だと、とてもじゃないが面白いゲームとは思えないな…

というか、ストーリー性というのは大きな武器だ。紙芝居ゲームなんてのもある位にソレだけでも勝負できる。
なのにソレを放棄するなんて意味不明だ。面白くないのは当然の事だ。今時のスタンドアローン用ゲームとしてありえないだろ…






となるハズなのだが、昨日、とうとう俺は「わいどにょ」のゲームプレイ時間100時間超えを達成してしまった。


「わいどにょ」でやることなんて限られている
  1. 仲間キャラを集める
  2. アイテムを集める
  3. 敵を倒す
  4. 溜まった経験値でキャラLvアップ
  5. 溜まった金でアイテム強化
  6. 窟を進む
これの繰り返し。本当に単純作業だ。ストーリーなんて皆無だ。

「わいどにょ」の大部分を占める戦闘部分が凄まじく面白いというワケでも無い。
  1. キャラを戦場に出す
  2. 攻撃を実行するキャラを選択
  3. 攻撃対象の敵キャラを選択
ただのクリックゲーだ。テクニックも糞も無い。

でも止められない。”飽き”が来ない。不思議な事だが”飽き”ない。
今年の 1/4 に「わいどにょ」に手を出してしまってからずっと嵌っている。


全てを悟ったフリをして”飽き”ない理由を語ってみる。


”やりこみ系”ゲームは、性質上、やり込めばやり込む程に達成感を得られる。
だが、”やりこみ系”ゲームには何れ”飽き”がくる。其れが何時くるのかというと、恐らく”バランス”が崩壊した時だと思う。

「ファイアーエムブレム トラキア776」というゲームを遊んでいた。
このゲームはシリーズ物の続編で、今までの同シリーズと比較すると難易度の高いゲームだった。
”ヤワイ”よりは”ムズイ”方を好む俺としては大歓迎で、完璧プレイを目指してやり込んでいた。
そして、「闘技場」(金を払って戦闘をして、勝利すれば経験値と賞金が得られるシステム)がある面で普段使わない2軍キャラ全員をLvMaxまで育てようと意気込み、実際に実行した。
その結果「ファイアーエムブレム トラキア776」の”バランス”は崩壊した。
それと共に俺は”飽き”てしまった。ストーリーの先を見たいという思いはあったのだが、途中で止めてしまった。


「わいどにょ」は”バランス”が秀逸なんだと思う。
「わいどにょ」はやり込めばやり込んだ分だけ達成感を得られると同時に”新たなやり込み要素”が適度に生じる。だが、その事に対する少々の不達成感も生じる。
その不達成感が許容範囲外のものであれば其処で”飽き”てしまう。
だが、「わいどにょ」はそんな事態を起こさない。故に、行っている内容は単純作業であるにも関わらず中々”飽き”させない。


”映画”みたいなゲームを作ろうと躍起になっている連中が主流(え、一部だけだって?)な中、流れに逆行しているゲームだ。
でも、「わいどにょ」は素晴らしくゲームとして”遊べる”作品だ。
恐らく5月のGWがきても俺は「わいどにょ」を遊んでいるだろう。下手をすれば、盆が明けてもやっているかもしれない。気付いたら雪が降っている可能性も否定できない。
それ程に長く遊べるであろうゲームだ。

という事で「遊ぶゲームが無い」と嘆いている人がいるならこのゲームをやってみてはどうだろう。
ただ、初回生産分だけで追加生産はしないらしいので、現在だと新品を見つけるのは難しいかもしれないが。


おまけ
「わいどにょ」というゲームは、在る程度数値を管理しておく事でより楽しくスムーズに遊べる。そのための簡易ツールを紹介。
にょ
簡易ツール
今回の公開に合わせてセーブデータの管理機能も付けてみたのだが、これに因ってセーブデータが破損したとしても自己責任で。大丈夫だとは思うけどね。



2009/12/22


雑記『ばとコマ!』更新



2009/11/28


ソフト公開第四弾



『窓位置設定』



窓位置設定


実行ファイル



2年振りに復活したソフト公開シリーズ第四弾。


自分は Window の位置を結構気にします。

Microsoft の Windows のエクスプローラを例に上げると、
  1. Windowの上端、右端はスクリーンの端にキッチリと合わす
  2. Windowの下端はタスクバー位置ギリギリに合わす
  3. Windowの左端はタスクバーの「クイック起動」の領域の右端に合わす
という様に。イメージで示すとこんな感じ。
explorer

こうなっていないと何か気持ちが悪いんです。集中できません。


ただ、この配置を手動で行おうとすると難しい。1Pixel単位を調整しようとしてる内に血管切れる。

血管が切れるのは勘弁なので、楽するために作成したツールが『窓位置設定』です。

自分用のツールなので名前は適当、というか直球で考無…



4つのモードが有って、窓位置を弄れます。以下概要。


聞く所によると、1月ほど前に一般販売も行われたWindows7には↑の領域指定と同じ様な機能が標準で載ったそうだ。

俺も欲しいと思ったから機能を実装したのだけど、やはりこんな機能が欲しいと思った人が他にも沢山居たのだろうね。


Windows8が2012年にもリリースされるという情報も有って、Windows7をスルーしようかと考えていた。

だけど、Windows7が"俺が好む"方向(ex. 領域指定や軽量化等)に進んでいるならば買っても良いかなぁと思い出した。

というか、sp1が当たったパッケージが出廻りだしたら多分買うかな?Windows8がどんな代物になるかも判んないしね。



ん?話が色々飛んだ気がするが…纏める気は無し!どうせ今回は月一更新を維持する為の雑記だから。

本当はとあるレトロゲームの攻略雑記をアップする予定だったのだけど、11月中に間に合わなかった。


というワケで次回からは、とあるレトロゲームの攻略雑記をアップしていきます。



2009/10/23


『Windows Vista 2』

Windows7の正式リリース日も決まった今日この頃、Vistaをちょっと見つめ直してみる。

仕事の開発環境(というか動作テスト環境)として回ってきたノートPCで俺はVistaとの初接触を果たした。
Vistaが発売されてからさして時間は経っていなかったが、この時既に世ではVistaに対する悪評が蔓延していた。

「遅い!重過ぎる!!糞過ぎるでござる!!!」
「UIが変!使い難い!!カス過ぎるでござる!!!」

ネット上のVista批評の場ではこのような言葉が乱舞していた。
UIというかエクスプローラについては俺も糞だと思った。酷かった。使い難いというかバグってた
ただ、重いという点については特に感じなかった。以外にも全く感じなかった(←大して深く使っていなかったからかも知れないが)


初めてVistaの画面を見た時、Z軸前面Windowにおいて、背面に在るWindowの内容が透けて見える事に驚いた。
綺麗とかそういう点に対して驚いたのでは無く、この機能を実現するために払ったであろうコストに対して驚いた。
リアルタイムでWindow間の半透明処理を実行。こんな事を実現しようとするならば(XPでの表示処理と比較して)CPUリソース、memoryリソースを大量に消費しなければ無理であろう事は想像できる。
実際、Vistaでは機能の実現に大量のmemoryリソースを費やしている。
今までは、
「アンタの順番きたよ。キャンバス(ディスプレイスクリーン)のココにあんたの絵(画面イメージ)書いてくれる。」
と必要に応じて頼んでいたのが、
「アンタ専用のキャンバス(画用紙)用意しとくから絵(画面イメージ)書いといて。必要に応じてコピー貰いにくるから」
と変わったのだ。

XPまでは1枚のキャンバスを皆で順番に回してスクリーンイメージを描き上げていた。
用意するキャンバスは1枚で済むので物的資源は節約できる。だが、毎回各Windowに画面を描いて貰わないといけないので人的資源は掛かっていた。
Vistaでは、各Windowに配布した個別キャンバスに自身の画面イメージを描かせる。そして、OSはそれを張り合わせてスクリーンイメージを作り上げる。
ディスプレイスクリーン以外にWindowの数だけキャンバスを用意するので物的資源は掛かる。でも、毎回絵を書き直す必要は無くなるので人的資源は節約できる。
「これからディスプレイスクリーンイメージ作るよ~」
とOSから伝達が来た時には(Window側は)作り置きしてた絵を提供するだけで良いので楽だ。
それにWindow個別の元絵があればグラフィック効果を付加する事等も容易いので色々(半透明処理とか)できる。(絵描く時に、パーツ単位でレイヤー分けすると色々楽になるのと似たようなモン…かな)
Vistaのこれら機能を実現/管理しているのがDWM(Desktop Window Manager)

こうして見るとVistaのやっている事は悪くない。物資を消費して作業を効率化させただけ。物的資源を失う代わりに人的資源の消費を節約できるのでtotalで見るとeven-evenだ。

と言いたいが…その実はそうではない。何故なら、今のWindows人(中の人)は優秀な人ばかりだからだ。
Windows人は(地球人と比較すると)超仕事が速い。実は絵程度ならば瞬間で描き上げる事ができる。仕事を振ったそのコンマ○秒後には作業を完了し「次の仕事くれ~」と作業指示待ち状態に入ってしまうようなヤツラなのだ。
何を云いたいのかというと、(Windows人が優秀過ぎる故)実は人的資源は過多であるケースが多い。”余って”いる。つまり、”節約”する必要性が低い資源なのだ。
> totalで見るとeven-evenだ
必要性の低いものを確保するために労力を払った事になるので、残念ながら”even-even”とは言い難い。
展開的に人的資源の節約って方面に話を持ってきたケド、DWMは人的資源の節約が目的の機能じゃない。グラフィック効果の強化が主目的だと思うので、実は↑の話は言い掛かりに近かったりする…

人的資源は余っている。じゃ物的資源はどうなのか?
実は人的資源とは逆に、多くのケース(一般的なPC)において枯渇状態である事が多い。
「コピー機独占してるの何処のアホだ。俺も使いてぇんだよ!」
「絵を描こうと思ってアトリエ借りにきたけど…空室無いじゃねぇか。誰か早く空けてくれ」
「これから出掛けなきゃならないのに足(車)が無ぇ…誰が乗ってったんだよ!」
て感じに物的資源の奪い合いが起きる。そして物的資源が得られるまで待たされる。基本的に皆順番待ち状態だ。手は空いているのに作業を進められない。

つまり、Vistaにとって人的資源/物的資源の重要度は50:50では無い。人的資源より物的資源の方が遥かに重要なのだ。


突然だが話を戻す。何処に戻すかというと”重いという点については特に感じなかった。以外にも全く感じなかった”まで戻す。
この時のVistaマシンはDellのノートPCだった。性能的にはVista発売直後の一般的ノートPCと同程度で高性能では無かった。
だが、世で言われているようなVistaの糞重さを俺は感じなかった。(速度に対して)不快を感じる事無く使用していた。
なので、俺は「Vistaの重さ」に関しては世と共感を分つ事はできていなかった。

2008年。DellPCのリース期間が過ぎたため新たなVistaマシンが与えられた。「ThinkPad R500(2714A21)」だ。
CPUCore 2 Duo P8400
メモリ1GB
性能的にはDellPCよりかなり上の代物だ。
早速、リカバリーCDからVistaをインストールした後(2714A21の初期OSはXPなので入換)アンチウィルスソフトを入れた。
セットアップ作業はこれで終わり。動作確認用マシンなので基本素のVistaのままだ。

ThinkPad R500 は基本素のままのVistaで、余計なアプリを新たにインストールするような事はしていない。
なのに…起動してみると重い。糞重い。初回起動時だけの話ではなく2回目以降も重い。

PCを起動してデスクトップ画面が表示される。しかし、まだ起ち上がりきってはいない。HDからガリガリ音が鳴っている。
デスクトップが表示され始めてから2分近く経った時点でアンチウィルスソフトのスプラッシュウィンドウが表示される。いぁ…この2分間何してたの?あんたは真っ先に起ち上がる類のアプリでしょ?
その後もハードディスクアクセスランプは点滅しっ放し。タスクトレイに起ち上げた覚えの無いアプリケーションが登録されていく。起動しているプロセスの数も増えていく。
もう何がなんだか判らない。
相も変わらずハードディスクアクセスランプは点滅しっ放し。起動から10分経っても点滅しっ放しなので無視して操作を開始せざるを得ない。

「うん…遅い。重過ぎる。糞過ぎるでござる!!!」

2008年。俺はやっと周りに追いつけた。

XPまでは多少非効率でも物的資源節約/機能美を優先していたように思う。だが、Vistaからは大きく方向を変えた。DWMなんて最たる物。コレは”魅せる”ための機能だ。
その弊害として今までより重いOSになった。”機能”と”軽さ”は天秤で釣り合うモノ。両方で最高の結果を得る事は容易くないのでコレは仕方が無い。

「遅い!重過ぎる!!糞過ぎるでござる!!!」
Vistaはこのように言われる。でもね…俺はこの評価は真では無いとも思っている。
確かに以前のOSと比較すれば重い。でも周りの状況(ハードウェア的進歩等)も考慮すれば何とか許容可能範囲な出来だったハズ。実際、俺はDellPCにおいて重さ的不満をVistaに抱いていなかったのだから。

Vistaは重い。物的資源を大量に必要とする。
それは自明の事なのだから対策を取ってやれば良いだけなんだ。ハードウェア的、運用的に。
そうすりゃそれなりに遣り繰りできるハズだ。

でもね…本来ソレを率先して行ってやるべき人達はソレをやっていたのだろうか?
Vistaは重い。それを解っていながら、目先の低価格優先で低性能糞パーツで糞PCを構成した糞PCメーカー。
Vistaは物的資源に乏しい。それを解っていながら、ユーティリティやらなんやら理由をつけて無用アプリを初期状態で大量にブっ込む糞PCメーカー。

ThinkPad R500を触った結果、諸悪の根源はコイツラであると俺は思うようになった。



2009/10/22


『Windows Vista 1』

最初に、この雑記を書こうと思った動機を書いておこう。
  1. Windows7リリース日決定の話を聞いた時、Vistaに関する在る出来事↓を思い出す
    1. 旧世代PCでVistaを動かした時は速度的に不満を感じなかったのに、性能が↑なハズの「ThinkPad R500」のVistaは重かった
    2. これは、「ThinkPad R500」に初期状態でインストールされてたアプリの所為だな
    3. Vistaが重いの判ってるクセに、初期状態で要らんアプリ入れるPCメーカーってアホだよね?
  2. 阿呆メーカーに罵詈呉れてやる!

次に、雑記を書くに当たってどう展開するか考えた初期構想を晒しとこう。
  1. Vistaが重いのは在る程度必然という状況説明(例として Vista の DWM のお話)
  2. だが、Windows7 では幾らか改善される(DWM がどう改善されたかのお話)力尽きて書いてません…
  3. Vistaが重くなってるのはMS以外に黒幕が居る。初期状態で要らんアプリ入れるPCメーカーくたばれ
1、2でWindowsに関する悪印象を和らげ、3の結論に繋げようという構成だった。

で、実際書いてみたのだが…誇張・空想・虚実が微妙に入り混じった酷い文章が出来上がった。流石にこのまま公開するのは躊躇われたのでその時は没とした。


それから3ヶ月程経過した。Windows7が発売されたというニュースが入ってきた。そしてこの雑記の事を思い出した。
改めて読むとやはり酷い。だが…ページ更新間隔が空き、加えて次回の雑記更新ネタも無い現状況において在る言葉が頭を過ぎる。

「ネタと宣言した上での雑記公開ならアリじゃない?せっかく文書いたのに徒労で終わるのは嫌だろ?」


というわけで、次回「Windows Vista 2」をお送りします。

「Windows Vista 2」は”フィクション”です。また、1度は捨てた文章なので何時も以上に推敲していないし、ありきの結論に強引に話を繋げたりもしている箇所も見受けられます。そういう類の文章です。



2009/06/08


雑記『東方翼』更新