ステレオミキサーつきの仮想サウンドデバイスドライバ "vadplus"の完成までをまとめました。
「再生結果をファイルに保存する」という仮想サウンドデバイスドライバ MSVAD が既に用意されています。
まずはビルド。
出来上がってることを確認して、インストール。
インストールできましたか? 動作を確かめてみましょう。
頻繁にドライバを差し替えるので、差し替えもマスターしておきましょう。
再起動を求められたらおとなしく再起動しましょう。
28〜33行目、チャンネル・量子化ビット・サンプリングレートを固定する。
#define MIN_CHANNELS 2 // Min Channels. #define MAX_CHANNELS_PCM 2 // Max Channels. #define MIN_BITS_PER_SAMPLE_PCM 16 // Min Bits Per Sample #define MAX_BITS_PER_SAMPLE_PCM 16 // Max Bits Per Sample #define MIN_SAMPLE_RATE 48000 // Min Sample Rate #define MAX_SAMPLE_RATE 48000 // Max Sample Rate
18行目、コメントアウト。ファイル保存のためのヘッダなんていらない。
// #include "savedata.h"
76行目あたり、CMiniportWaveCyclicMSVADクラスにpublicでインスタンス変数を追加。再生データを録音に回すためのリングバッファです。
public:
PVOID m_pStereoMix; // バッファのポインタ
ULONG m_ulStereoMixSize; // バッファサイズ
ULONG m_ulStereoMixWPos; // バッファ書き込み位置
ULONG m_ulStereoMixRPos; // バッファ読み込み位置
143行目(追加後は148行目)あたり、コメントアウト。CSaveDataクラスの参照はもはや不要。
位置: CMiniportWaveCyclicStreamMSVADクラス
- pprotectedインスタンス変数 - m_SaveData
// CSaveData m_SaveData;
32行目、ラインごと削除。savedata.cppのソース指定は不要。
20行目、26〜27行目、コメントアウト。
// #include "savedata.h"
// PSAVEWORKER_PARAM CSaveData::m_pWorkItems = NULL; // PDEVICE_OBJECT CSaveData::m_pDeviceObject = NULL;
215行目、コメントアウト。
位置: CAdapterCommonクラス - デストラクタ(~CAdapterCommon)
// CSaveData::DestroyWorkItems();
299行目、コメントアウト。
位置: CAdapterCommonクラス - Init関数
// CSaveData::SetDeviceObject(DeviceObject); //device object is needed by CSaveData
170行目あたり。"DestinationとByteCountを参照しない"定義を削除して、処理を盛大に追加。
場所: CMiniportWaveCyclicStreamMSVADクラス
- CopyFrom関数
UNREFERENCED_PARAMETER(Source);
RtlFillMemory((PBYTE)Destination, ByteCount, 0);
/*
書き込み位置の
前4KBにいるとき: 何も処理しない(落とす)
後4KBにいるとき: 半分進める
*/
// 書き込み位置が4→Maxのとき → 通常 (WPos - 4K < x <= WPos)
// 書き込み位置が0→4のとき → 特殊 (x < WPos || x > Size - (4K - WPos))
ULONG rPos, wPos, size;
rPos = (*m_pMiniport).m_ulStereoMixRPos;
wPos = (*m_pMiniport).m_ulStereoMixWPos;
size = (*m_pMiniport).m_ulStereoMixSize;
if (wPos >= 2 * 1024 && wPos - 2 * 1024 < rPos && rPos <= wPos)
{
// 止めます
}
else if (wPos < 2 * 1024 && (rPos < wPos || rPos > size - (2 * 1024 - wPos)))
{
// 止めます
}
else {
// 書き込み位置が0→Max-4のとき → 通常 (WPos < x < WPos + 4K)
// 書き込み位置がMax-4→Maxのとき → 特殊 (WPos < x || x < WPos + 4K - Size)
if ((wPos < size - 2 * 1024 && wPos < rPos && rPos < wPos + 2 * 1024) ||
(size - 2 * 1024 <= wPos && wPos <= size && (wPos < rPos || rPos < wPos + 2 * 1024 - size)))
{
(*m_pMiniport).m_ulStereoMixRPos = (rPos + size / 2) % size;
rPos = (*m_pMiniport).m_ulStereoMixRPos;
}
if (rPos + ByteCount > size)
{
// 読みきれない.
ULONG readSize;
ULONG restSize;
readSize = size - rPos;
restSize = ByteCount - readSize;
// まずはうしろまで.
RtlCopyMemory(
Destination,
(PBYTE)((*m_pMiniport).m_pStereoMix) + rPos,
readSize
);
// んで頭から.
RtlCopyMemory(
(PBYTE)Destination + readSize,
(*m_pMiniport).m_pStereoMix,
restSize
);
// なおす.
(*m_pMiniport).m_ulStereoMixRPos = restSize;
}
else
{
// 読みきれる.
RtlCopyMemory(
Destination,
(PBYTE)((*m_pMiniport).m_pStereoMix) + rPos,
ByteCount
);
(*m_pMiniport).m_ulStereoMixRPos += ByteCount;
}
}
207行目あたり(追加後不明)、とにかくCopyTo関数んとこ。m_SaveDataのくだりはもちろん削除で。
場所: CMiniportWaveCyclicStreamMSVADクラス
- CopyTo関数
// オリジナル
if ((*m_pMiniport).m_ulStereoMixWPos + ByteCount >
(*m_pMiniport).m_ulStereoMixSize)
{
// 書ききれない.
ULONG writeSize;
ULONG restSize;
writeSize = (*m_pMiniport).m_ulStereoMixSize
- (*m_pMiniport).m_ulStereoMixWPos;
restSize = ByteCount - writeSize;
// まずはうしろまで.
RtlCopyMemory(
(PBYTE)((*m_pMiniport).m_pStereoMix) + (*m_pMiniport).m_ulStereoMixWPos,
Source,
writeSize
);
// んで頭から.
RtlCopyMemory(
(*m_pMiniport).m_pStereoMix,
(PBYTE)Source + writeSize,
restSize
);
(*m_pMiniport).m_ulStereoMixWPos = restSize;
}
else
{
// 書ききれる.
RtlCopyMemory(
(PBYTE)((*m_pMiniport).m_pStereoMix) + (*m_pMiniport).m_ulStereoMixWPos,
Source,
ByteCount
);
(*m_pMiniport).m_ulStereoMixWPos += ByteCount;
}
// m_SaveData.WriteData((PBYTE) Source, ByteCount);
69行目あたり、変数の初期化を追加。
場所: CMiniportWaveCyclicMSVADクラス -
コンストラクタ(CMiniportWaveCyclicMSVAD)
m_ulStereoMixSize = 32 * 1024;
m_pStereoMix = ExAllocatePoolWithTag(
NonPagedPool, m_ulStereoMixSize, MSVAD_POOLTAG
);
m_ulStereoMixWPos = 0;
m_ulStereoMixRPos = 0;
106行目(追加後は112行目)あたり、処理追加。
場所: CMiniportWaveCyclicMSVADクラス -
デストラクタ(~CMiniportWaveCyclicMSVAD)
if (m_pStereoMix)
{
ExFreePoolWithTag(m_pStereoMix, MSVAD_POOLTAG);
}
565行目(追加後は575行目)あたり、m_pMiniportにMiniport_の参照を代入したあとに処理を追加。
場所: CMiniportWaveCyclicStreamMSVADクラス
- Init関数
if (Capture_)
{
(*m_pMiniport).m_ulStereoMixRPos =
((*m_pMiniport).m_ulStereoMixWPos +
(*m_pMiniport).m_ulStereoMixSize / 2)
% (*m_pMiniport).m_ulStereoMixSize;
}
610行目(追加後は627行目)あたり、コメントアウト。CSaveDataにまつわる処理は不要。
場所: CMiniportWaveCyclicStreamMSVADクラス
- Init関数
/*
if (!m_fCapture)
{
(中略)
}
*/
841行目(追加後は860行目)あたり、コメントアウト。同上。
場所: CMiniportWaveCyclicStreamMSVADクラス
- SetFormat関数
/*
if (!m_fCapture)
{
ntStatus = m_SaveData.SetDataFormat(Format);
}
*/
993行目(追加後は1014行目)あたり、コメントアウト。同上。
場所: CMiniportWaveCyclicStreamMSVADクラス
- SetState関数
/*
if (!m_fCapture)
{
m_SaveData.WaitAllWorkItems();
}
*/
何を思ったかmultiのほうをいじったので、"Microsoft Virtual Audio Device (Multiple Streams) (WDM)"をお試しあれ。ベースいじったからほかでも動くと思うけど。
いじってると、CopyTo関数に再生データが飛んできて、CopyFrom関数で録音データを飛ばす、ということがなんとなく分かります。ただ、シンプルにCopyTo関数のデータをCopyFrom関数で飛ばそうとしても、すんなりいきません。
CMiniportWaveCyclicMSVADクラスとCMiniportWaveCyclicStreamMSVADクラスは、だいたい次のような関係になっています。

なので、CMiniportWaveCyclicStreamMSVADだけでデータを回しても、何にも起こるわけがないんです。。
ってことで、共通しているCMiniportWaveCyclicMSVADにバッファを作って、それを読み書きするようにしました。

めでたし、めでたし。
読み書きしているバッファの位置が重なると音がへんになってしまうので、適当に調整しています。
ライセンス的な意味で。パッチなら勘弁してもらえるだろうか: msvad2vadplus_diff.zip - 当て方は Google: diff patch とかで。
たいぷらいたー メール: takuyaxp@gmail.com ニコニコ動画: user/308238 GitHub: typewriter その他: にゃみかん