2019年8月2日金曜日

XAudio2SamplesのDirectX::LoadWAVAudioFromFileExを読み解く

入門XAudio2を読んでいて,45ページ目から48行目でサラッと流されているWAVファイルの読み込み処理,DirectX::LoadWAVAudioFromFileExについて気になったので読み解いてみようかと.

Qiitaに書くのは何か違うかな,と思ったのでこっちで.

RIFFについては知っている前提で.

RIFF(Resource Interchange File Format)についてはこちらを参照(Microsoft Docs)

DirectX::LoadWAVAudioFromFileEx関数で読み取った結果は,次のようなDirectX::WAVData構造体に保存されます.

namespace DirectX
{
    struct WAVData
    {
        const WAVEFORMATEX* wfx;
        const uint8_t* startAudio;
        uint32_t audioBytes;
        uint32_t loopStart;
        uint32_t loopLength;
        const uint32_t* seek;
        uint32_t seekCount;
    };
}

で,そのDirectX::LoadWAVAudioFromFileEx関数の宣言は次のような感じです.

namespace DirectX
{
    HRESULT LoadWAVAudioFromFileEx(
        _In_z_ const wchar_t* szFileName,
        _Inout std::unique_ptr& wavData,
        _Out_ WavData& result
    );
}

この,_In_z_とか_Inout_は,ソースコード注釈言語(Source-code Annotation Language, SAL)です.詳しくは,Microsoft Docsを検索してください.簡単に言うと,Visual Studioがコードの使い方に問題がないかチェックするための付加情報です.

szFileNameはファイルパス,wavDataにはファイルのデータ全てが格納されて,WAVData::startAudioはその中の音データの開始位置を指します.

さて,中を見ていきましょう.最初の数行は,ファイルパスへのポインタがNULLだったらエラー,出力先を0初期化,というだけなので飛ばします.



DWORD bytesRead = 0;
HRESULT hr = LoadAudioFromFile(szFileName, wavData, &bytesRead);

早速新しい関数が出てきました.


static HRESULT LoadAudioFromFile(
    _In_z_ const wchar_t* szFileName,
    _Inout_ std::unique_ptr& wavData,
    _Out_ DWORD* bytesRead
);

単純に,szFileNameのパスにあるファイルを開いて,wavDataにファイルの内容を書き込み,bytesReadに読み取ったバイト数を書き込む,というだけの関数です.

もう少し詳しく見てみると,Windows 8以降の場合は,CreateFile2を使ってファイルを開き,それより前の場合はCreateFileWを使ってファイルを開くようになっています.ちょっと調べてみたんですが,敢えて使い分ける理由がサッとは見つかりませんでした.

そして,Vista以降ならGetFileInformationByHandleExを使い,それより前の場合はGetFileSizeExを使ってファイルサイズを取得しています.これも,わざわざ使い分ける理由がいまいち不明です.GetFileInformationByHandleExの場合,エラー処理が入っているので,何らかの理由でファイルサイズがちゃんと取得できないケースがあるのかもしれません.

そして,このファイルサイズが32bitで表現できる範囲を超える場合や,WAVファイルとして最低限必要なRIFFチャンクと"fmt "チャンクのサイズ以下の場合,エラーとしています.

ここまで来ると,あとはwavDataに必要なサイズ分のメモリを割り当て,ファイルから読み取るだけです.この関数内で勝手にメモリを割り当てるからunique_ptrにしているんでしょうね.

さて,DirectX::LoadWAVAudioFromFileExに戻ります.


bool dpds, seek;
hr = WaveFindFormatAndData(
    wavData.get(),
    bytesRead,
    &result.wfx,
    &result.startAudio,
    &result.audioBytes,
    dpds,
    seek
);

またまた新しい関数です.


static HRESULT WaveFindFormatAndData(
    _In_reads_bytes_(wavDataSize) const uint8_t* wavData,
    _In_ size_t wavDataSize,
    _Outptr_ const WAVEFORMATEX** pwfx,
    _Outptr_ const uint8_t** pdata,
    _Out_ uint32_t* dataSize,
    _Out_ bool& dpds,
    _Out_ bool& seek
);


  • wavData
    • WAVファイルのデータを渡す
  • wavDataSize
    • wavDataのサイズを渡す
  • pwfx
    • wavData中のfmtチャンクから,WAVEFORMATEXの位置へのポインタを返す
  • pdata
    • wavData中のdataチャンクから,音データの開始位置へのポインタを返す
  • dataSize
    • pdataのサイズを返す
  • dpds
    • dpdsチャンクの有無を返す.dpdsチャンクはxWMAフォーマットの場合に存在する.
  • seek
    • Xbox OneでサポートされているXMA2フォーマットの場合にtrueになる,とコメントを読み解くと書いてある.

さて,この中で最初に呼ばれているFindChunkという関数は,次のような関数です.


static const RIFFChunk* FindChunk(
    _In_reads_bytes_(sizeBytes) const uint8_t* data,
    _In_ size_t sizeBytes,
    _In_ uint32_t tag
)

dataの先頭がRIFFのヘッダ(4バイトのfourccと4バイトのチャンクサイズ)と仮定して,sizeBytes先までにtagと一致するfourccを持つチャンクを探してくれるだけの関数です.

この関数を使って,まずはRIFFチャンクを探します.このRIFFチャンクがなかったり,RIFFチャンクのRIFFヘッダの直後のfourccがWAVEまたはXWMAで無い場合は正しいファイルでない,とエラーを返しています.

RIFFチャンクからRIFFヘッダとfourcc分のバイト数を進めた位置からサブチャンクが始まります.次に,このサブチャンクからfmtチャンクを探します.fmtチャンクには,少なくともWAVEFORMAT構造体のデータが含まれています.

このWAVEFORMAT構造体にはwFormatTagという音データの形式が格納されていて,PCM(pulse code modulation)形式や,ADPCMフォーマットである,といったことを確認し,その上でfmtチャンクのサイズに問題がないか,dpdsやseekは設定できるか,といったことをチェックしています.

fmtチャンクが終わったら,チャンクの終端に進み,dataチャンクを探し,サイズが渡されたデータを超えていないかチェックをしたら,dataチャンクのデータ部分の先頭をpdataに設定して,dataチャンクのサイズをdataSizeに設定して,この関数は終了です.

DirectX::LoadWAVAudioFromFileExに戻ると,次はWaveFindLoopInfo関数です.



static HRESULT WaveFindLoopInfo(
    _In_reads_bytes_(wavDataSize) const uint8_t* wavData,
    _In_ size_t wavDataSize,
    _Out_ uint32_t* pLoopStart,
    _Out_ uint32_t* pLoopLength
);

wavData,wavDataSizeはWaveFindFormatAndDataと同じなのでスキップします.この関数は,WAVファイルからループの開始サンプル数とループの長さのサンプル数を,それぞれpLoopStartとpLoopLengthが指す先に格納してくれます.

この中では,RIFFチャンクがWAVEかどうか確認しています.xWMAはループ情報を含まないようです.そして,DLSチャンク(fourccはwsmp)またはSampleチャンク(fourccはsmpl)からループ情報を取り出しています.

DLSは,Downloadable Soundの略っぽいですが,該当するデータが手元に無いので詳細は不明です.

smplチャンクは,コード中ではMIDIチャンクと書いているので,MIDIで作成した音しかループ情報を含んでいないのでしょうか?

このループ情報の取り出しは,場合によってはスキップしても良いかもしれません.

DirectX::LoadWAVAudioFromFileExに戻ると,dpdsやseekがtrueかどうかで次のような関数を呼び出して,必要なデータを取り出しています.


static HRESULT WaveFindTable(
    _In_reads_bytes_(wavDataSize) const uint8_t* wavData,
    _In_ size_t wavDataSize,
    _In_ uint32_t tag,
    _Outptr_result_maybenull_ const uint32_t** pData,
    _Out_ uint32_t* dataCount
);


  • wavData,wavDataSize
    • おなじみWAVファイルの中身とサイズ
  • tag
    • RIFFチャンクのサブチャンクから抜き出すチャンクのfourcc
    • dpdsの場合,dpdsチャンク,seekの場合seekチャンクを指定
  • pData
    • tagで指定したチャンクのデータ部分へのポインタを返す
    • 4バイトのデータの配列になっている
  • dataCount
    • tagで指定したチャンク内にある4バイトのデータの数
これでようやく終わりです.

この関数は,ファイルデータをすべてメモリに載せた上での処理になっています.しかし実際には,効果音などの短い音であればメモリに載せられても,BGMなどのようにある程度のサイズがあるデータは,必要な部分を徐々に読み取って再生するのが一般的だと思うので,そういった場合は,FindChunkのように都度必要なチャンクを探して回るのではなく,チャンクを発見する度に必要な情報を埋めていって,といった形にする必要があるかもしれません.

2019年3月22日金曜日

d3d11.hからd3d11_4.hまでの変更点

d3d11_1.h

インターフェース

  • ID3D11BlendState1
    • GetDesc1を追加
  • ID3D11Device1
    • CreateBlendState1を追加
    • CreateDeferredContext1を追加
    • CreateDeviceContextStateを追加
    • CreateRasterizerState1を追加
    • GetImmediateContext1を追加
    • OpenSharedResource1を追加
    • OpenSharedResourceByNameを追加
  • ID3D11DeviceContext1
    • ClearViewを追加
    • CopySubresourceRegion1を追加
      • D3D11_COPY_FLAGS型のフラグを最後に追加
    • CSGetConstantBuffers1を追加
    • CSSetConstantBuffers1を追加
    • DiscardResourceを追加
    • DiscardViewを追加
    • DiscardView1を追加
    • DSGetConstantBuffers1を追加
    • DSSetConstantBuffers1を追加
    • GSGetConstantBuffers1を追加
    • GSSetConstantBuffers1を追加
    • HSGetConstantBuffers1を追加
    • HSSetConstantBuffers1を追加
    • PSGetConstantBuffers1を追加
    • PSSetConstantBuffers1を追加
    • SwapDeviceContextStateを追加
    • UpdateSubresource1を追加
      • D3D11_COPY_FLAGS型のフラグを最後に追加
    • VSGetConstantBuffers1を追加
    • VSSetConstantBuffers1を追加
  • ID3D11RasterizerState1
    • GetDesc1を追加
  • ID3D11VideoContext1
    • CheckCryptoSessionStatusを追加
    • DecoderEnableDownsamplingを追加
    • DecoderUpdateDownsamplingを追加
    • GetDataForNewHardwareKeyを追加
    • SubmitDecoderBuffers1を追加
    • VideoProcessorGetBehaviorHintsを追加
    • VideoProcessorGetOutputColorSpace1を追加
    • VideoProcessorGetOutputShaderUsageを追加
    • VideoProcessorGetStreamColorSpace1を追加
    • VideoProcessorGetStreamMirrorを追加
    • VideoProcessorSetOutputColorSpace1を追加
    • VideoProcessorSetOutputShaderUsageを追加
    • VideoProcessorSetStreamColorSpace1を追加
    • VideoProcessorSetStreamMirrorを追加
  • ID3D11VideoDevice1
    • CheckVideoDecoderDownsamplingを追加
    • GetCryptoSessionPrivateDataSizeを追加
    • GetVideoDecoderCapsを追加
    • RecommendVideoDecoderDownsampleParametersを追加
  • ID3D11VideoProcessorEnumerator1
    • CheckVideoProcessorFormatConversionを追加
  • ID3D11DeviceContextState
  • ID3DUserDefinedAnnotation

構造体

  • D3D11_BLEND_DESC1
    • RenderTargetがD3D11_RENDER_TARGET_BLEND_DESCからD3D11_RENDER_TARGET_BLEND_DESC1に変化
  • D3D11_KEY_EXCHANGE_HW_PROTECTION_DATA
  • D3D11_KEY_EXCHANGE_HW_PROTECTION_INPUT_DATA
  • D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA
  • D3D11_RASTERIZER_DESC1
    • ForcedSampleCountを追加
  • D3D11_RENDER_TARGET_BLEND_DESC1
    • LogicOpEnableを追加
    • LogicOpを追加
  • D3D11_VIDEO_DECODER_BEGIN_FRAME_CRYPTO_SESSION
  • D3D11_VIDEO_DECODER_BUFFER_DESC1
  • D3D11_VIDEO_DECODER_SUB_SAMPLE_MAPPING_BLOCK
  • D3D11_VIDEO_PROCESSOR_STREAM_BEHAVIOR_HINT
  • D3D11_VIDEO_SAMPLE_DESC

列挙型

  • D3D11_1_CREATE_DEVICE_CONTEXT_STATE_FLAG
  • D3D11_COPY_FLAGS
  • D3D11_CRYPTO_SESSION_STATUS
  • D3D11_LOGIC_OP
  • D3D11_VIDEO_DECODER_CAPS
  • D3D11_VIDEO_PROCESSOR_BEHAVIOR_HINTS

d3d11_2.h

インターフェース

  • ID3D11Device2
    • CheckMultisampleQualityLevels1を追加
      • D3D11_CHECK_MULTISAMPLE_QUALITY_LEVELS_FLAGを受け取るようになっている
    • CreateDeferredContext2を追加
    • GetImmediateContext2を追加
    • GetResourceTilingを追加
  • ID3D11DeviceContext2
    • BeginEventIntを追加
    • CopyTileMappingsを追加
    • CopyTilesを追加
    • EndEventを追加
    • IsAnnotationEnabledを追加
    • ResizeTilePoolを追加
    • SetMarkerIntを追加
    • TiledResourceBarrierを追加
    • UpdateTileMappingsを追加
    • UpdateTilesを追加

構造体

  • D3D11_PACKED_MIP_DESC
  • D3D11_SUBRESOURCE_TILING
  • D3D11_TILE_REGION_SIZE
  • D3D11_TILE_SHAPE
  • D3D11_TILED_RESOURCE_COORDINATE

  • D3D11_CHECK_MULTISAMPLE_QUALITY_LEVELS_FLAG
  • D3D11_TILE_COPY_FLAG
  • D3D11_TILE_MAPPING_FLAG
  • D3D11_TILE_RANGE_FLAG

d3d11_3.h

インターフェース

  • ID3D11Device3
    • CreateDeferredContext3を追加
    • CreateQuery1を追加
    • CreateRasterizerState2を追加
    • CreateRenderTargetView1を追加
    • CreateShaderResourceView1を追加
    • CreateTexture2D1を追加
    • CreateTexture3D1を追加
    • CreateunorderedAccessView1を追加
    • GetImmediateContext3を追加
    • ReadFromSubresourceを追加
    • WriteToSubresourceを追加
  • ID3D11DeviceContext3
    • Flush1を追加
      • D3D11_CONTEXT_TYPEおよびイベントハンドルを受け取るようになっている.
    • GetHardwareProtectionStateを追加
    • SetHardwareProtectionStateを追加
  • ID3D11DeviceContext4
    • Signalを追加
    • Waitを追加
  • ID3D11Fence
  • ID3D11Query1
    • GetDesc1を追加
  • ID3D11RasterizerState2
    • GetDesc2を追加
  • ID3D11RenderTargetView1
    • GetDesc1を追加
  • ID3D11ShaderResourceView1
    • GetDesc1を追加
  • ID3D11Texture2D1
    • GetDesc1を追加
  • ID3D11Texture3D1
    • GetDesc1を追加
  • ID3D11UnorderedAccessView1
    • GetDesc1を追加

構造体

  • D3D11_QUERY_DESC1
    • D3D11_CONTEXT_TYPE型のContextTypeを最後に追加
  • D3D11_RASTERIZER_DESC2
    • D3D11_CONSERVATIVE_RASTERIZATION_MODE型のConservativeRasterを最後に追加
  • D3D11_RENDER_TARGET_VIEW_DESC1
    • Texture2DがD3D11_TEX2D_RTV1型に変化
    • Texture2DArrayがD3D11_TEX2D_ARRAY_RTV1型に変化
  • D3D11_SHADER_RESOURCE_VIEW_DESC1
    • Texture2DがD3D11_TEX2D_SRV1型に変化
    • Texture2DArrayがD3D11_TEX2D_ARRAY_SRV1型に変化
  • D3D11_TEX2D_ARRAY_RTV1
    • PlaneSliceを最後に追加
  • D3D11_TEX2D_ARRAY_SRV1
    • PlaneSliceを最後に追加
  • D3D11_TEX2D_ARRAY_UAV1
    • PlaneSliceを最後に追加
  • D3D11_TEX2D_RTV1
    • PlaneSliceを最後に追加
  • D3D11_TEX2D_SRV1
    • PlaneSliceを最後に追加
  • D3D11_TEX2D_UAV1
    • PlaneSliceを最後に追加
  • D3D11_TEXTURE2D_DESC1
    • D3D11_TEXTURE_LAYOUT型のTextureLayoutを最後に追加
  • D3D11_TEXTURE3D_DESC1
    • D3D11_TEXTURE_LAYOUT型のTextureLayoutを最後に追加
  • D3D11_UNORDERED_ACCESS_VIEW_DESC1
    • Texture2DがD3D11_TEX2D_UAV1型に変化
    • Texture2DArrayがD3D11_TEX2D_ARRAY_UAV1型に変化

列挙型

  • D3D11_CONSERVATIVE_RASTERIZATION_MODE
  • D3D11_CONTEXT_TYPE
  • D3D11_FENCE_FLAG
  • D3D11_TEXTURE_LAYOUT

d3d11_4.h

インターフェース

  • ID3D11Device4
    • RegisterDeviceRemovedEventを追加
    • UnregisterDeviceremovedを追加
  • IDeD11Device5
    • CreateFenceを追加
    • OpenSharedFenceを追加
  • ID3D11Multithread
  • ID3D11VideoContext2
    • VideoProcessorGetOutputHDRMetaDataを追加
    • VideoProcessorGetStreamHDRMetaDataを追加
    • VideoProcessorSetOutputHDRMetaDataを追加
    • VideoProcessorSetStreamHDRMetaDataを追加

構造体

  • D3D11_FEATURE_DATA_D3D11_OPTIONS4

2019年2月22日金曜日

プログラムの設計をする際に考えていること

ふと思うところがあり,自分がプログラムを設計する際に考えていることを言葉にしてみようと思う.

まず,最初に考えるのは,最終的に用意したいデータは何なのか,ということである.

例えば,最終的に予算の合計が知りたい,だったり,最終的に画像ファイルが生成されていて欲しい,だったり,何らかのキーワードの有無が知りたい,であったりだ.

これがはっきりしていない場合,まずはこれをはっきりさせるために,情報収集を行う.例えば,発注者に話を聞いたり,ゲームの場合,仮組みをして動きや見た目を提示して,想定と合っているかの確認を行う.

次に,手元にあるデータから,最終的に必要なデータまでの加工手順を考える.ここで手順が思いつかない場合,最終的なデータを直接生成できるデータを考える.そのデータを生成できるデータを考えて,と逆算を行っていく.どうしても辿り着けない場合,何らかの情報が欠けていると思われるので,欠けている情報を明確にする.

例えば,画像を良い感じに加工して,と言われても,その良い感じがなんなのか分からないので,熱い感じなのか,寒い感じなのか,明るい感じなのか,暗い感じなのか,といったことを追加で入力してもらう必要があるかもしれない.
(そもそもそんな指示を出すのはどうかという話は置いておく)

また,今後追加で拡張などをして欲しい,といった要望があることも考えて,どういった拡張が考えられるだろう,といったことも考えておく.

ここまで来て,ようやくプログラムの設計に入るのだけれど,ここでいきなりクラスやアーキテクチャなどの設計に入るわけではない.

まずは,途中で必要となる要素の実験を行う.例えば,ファイルが作られた日が必要だとして,対象としている環境すべてで取得可能か確認を行う.もし不可能な環境があれば,他の方法を検討する必要がある.

他にも,想定している機能拡張がある場合にやりやすい方法は何だろう,と考える.なるべくシンプルな機能に分解しておいて,それらを組み合わせて実装するようにしておくと,後々組み合わせるだけで済み,追加部分だけのテストで良いこともある.

いくつかの実験を行い,一通りのパーツが揃ったら,ようやくクラスやアーキテクチャの設計に入る.

この段階で考えるのは,用意したプログラムを他のプログラマが利用するかどうか,ということ.

利用しない場合,ぶっちゃけ多少適当に作ったとしても,他に影響は無いので,サクッと作って素早く提供できる方が良い.

問題になるのは,他のプログラマが利用する場合だ.

下手にgetterやsetterを作ろうものなら,思わぬ使い方をされて,下手に修正できなくなる.

他のプログラマがどういう風に利用するかを考え,想定する使い方をする場合は使いやすく,想定しない使い方をしようとすると,途端に面倒になるような形が望ましい.

これは,ある程度利用するプログラミング言語に関する知識が必要となる.

例えば,C++であれば,あるクラスのオブジェクトはコピーできないようにしておく,getterは値のコピーを返し,オブジェクトが持っている値そのものは,簡単には変更できないようにする,などが考えられる.

C++の場合,アドレスが分かれば無理矢理書き換えもできるが,そこまでやる相手のことを考え始めると,実装速度や処理速度が犠牲になるのでやらない.

他にも,インターフェースである関数名や仮引数名を分かりやすいものにする,などで利用するプログラマが勘違いしたりして変な使い方をしないようにする.

例えば,音量を指定する機能を実装する場合に,それを0から1の浮動小数点で指定するのか,0から100のパーセンテージを表す整数で指定するのか,はたまた実際にスピーカーに渡せる最大値と0の範囲で指定するのか,と色々考えられるが,これがvolume,としか書いていなければ,ドキュメントを信じるか,実装を読み解くしか無い.

ちなみに,同一システム内に上記が混在しているのを見たことがある.

また,統一感,というのも重要になる.例えば,セーブロードの機能が,全体的にsaveとloadという名前で実装されているところに,storeとrestoreを導入する,というのは統一感が取れていない.

統一感を出すには,実装しているシステム中で一般的に使われている単語の用法から外れないように単語を選択していく必要がある.

この辺は,驚きを最小にする,ということを意識するようにしている.

また,実装しているシステムだけでなく,利用しているAPIやプログラミング言語の公式ドキュメント,世に溢れるオープンソースから類似の実装を読み,よく使われている名前を検討する.

よく使われている名前を使っておくと,検索する際にも似たような情報を拾いやすくなる.また,意味の取り違えも減る.

ここまでやって,ようやく多少は間違った使い方をされにくい設計ができる.

後は,考えたことをコードに落とし込んでいくだけである.

書いてみたけど,若干とりとめが無い,ポエムになってしまった感があるなぁ・・・・・・

2017年10月3日火曜日

Direct3D 11の学習 twitterまとめ









2016年12月13日火曜日

Chrome上でJavaScriptからSIMDを利用する方法

さすがstackoverflow.

How can I try out SIMD instructions in Chrome?

ChromeでSIMDを利用するには,ショートカットなどのChrome.exeに--js-flags="--harmony-simd"(ハイフン ハイフン js ハイフン flags = " ハイフン ハイフン harmony ハイフン simd")を指定すれば良いとのこと.

試してみたら,確かにSIMDというオブジェクトが増えていました.

2016年9月1日木曜日

Windows 10で起動したApache HTTPdサーバとPHPを連携させる

PHPのダウンロード


PHPのサイトからPHPをダウンロードします.トップのDownloadをクリックします.



Windows downloadsをクリックします.



Apache HTTPdサーバが32bit版ならx86を,64bit版ならx64のスレッドセーフまたは非スレッドセーフなものを選び,Zipをダウンロードします.ここでは,PHP 7.0.10のx64スレッドセーフ版をダウンロードしています.



ダウンロードしたZipの中身は,環境変数USERPROFILEで取得できるフォルダにphpという名前で解凍したものとします.

php.iniの用意

解凍したphpフォルダに,php.ini-developmentというファイルがあるので,php.iniにファイル名を変更しておきます.

Apache HTTPdの設定ファイル編集


Apache HTTPdとPHPを連携させるために,httpd/conf/httpd.confを編集します.

まず,次のようにPHPを読み込むようにLoadModuleを追加します.
LoadModule php7_module "${USERPROFILE}/php/php7apache2_4.dll"

次に,PHPファイルがphpとして処理されるように設定します.
<IfModule php7_module>
PHPIniDir "${USERPROFILE}/php"
<FilesMatch "\.php">
SetHandler application/x-httpd-php
</FilesMatch>
</IfModule>

最後に,URLとしてフォルダが指定された場合に表示するファイルとして,phpのファイルも使えるようにします.
DirectoryIndex index.html index.php

動作確認


httpd/htdocsフォルダにあるindex.htmlを消して,次の内容のindex.phpを用意します.

<?php phpinfo();?>

これで,localhostにアクセスした際に,PHPの設定が確認できれば成功です.
上手くいかない場合,F5を押すなどして再読込をしてみると,キャッシュが消えて上手くいく場合があります.

2016年8月31日水曜日

Windows 10でApache HTTPdサーバーを実行する

事前条件

Visual Studio 2015 のVisual C++ 再頒布可能パッケージをインストールしておく必要があります.
再頒布可能パッケージは,Microsoftのダウンロードセンターからダウンロードできます.

Apache HTTPdサーバのダウンロード


Apache HTTPdサーバのページの左メニューから,Download!のFrom A Mirrorを選び,Files for Microsoft Windowsを選びます.


Apache HTTPdサーバのWindows向けバイナリを配布しているサイトがいくつかありますが,ここではApache Loungeを選択します.


Apache HTTPdサーバの本体をダウンロードします.この記事を書いた時点での最新版は,2.4.23です.ここでは,64bit版をダウンロードしていますが,32bit版でも手順は変わりません.
ただし,PHPなどと連携する場合,HTTPdサーバが32bit版ならPHPも32bit版,といったように同じbit数のものを選ぶ必要があります.


ダウンロードしたら,解凍し,中にあるApache24というフォルダを取り出します.ここでは,ユーザーのホームフォルダ(環境変数USERPROFILEで指定されているフォルダ)にhttpdという名前で取り出したこととします.



httpdフォルダの中にconfというフォルダがあり,その中にhttpd.confという設定ファイルがあります.Apache HTTPdサーバを起動するには,最低限,以下の項目を設定する必要があります.

  • ServerRoot
    • "${USERPROFILE}/httpd"に書き換えます.
  • DocumentRoot
    • "${USERPROFILE}/httpd/htdocs"に書き換えます.
    • 直下のDirectoryの部分も同様に書き換えます.
  • ServerName
    • 先頭の#を消してコメントを解除します.
    • localhost:80に書き換えます.
これで準備ができたので,Apache HTTPdサーバを起動します.httpdフォルダの中に,binというフォルダがあり,その中にhttpd.exeがあるのでクリックして起動します.

http://localhostにアクセスして,It Works!と表示されれば起動しています.

先にSkypeが起動していると,Apache HTTPdサーバが起動しない場合があります.Skypeを起動している場合,一度Skypeを終了して,Apache HTTPdサーバを起動した後にSkypeを再起動するようにしてみてください.

あとは,httpdフォルダのhtdocsフォルダを基準として,相対パスを指定すれば,そのファイルをHTTPdサーバ経由で取得することができます.例えば,http://localhost/hello.htmlというパスをウェブブラウザに指定すると,htdocsフォルダのhello.htmlを取得することになります.