合計 昨日 今日

WASAPIを使ったPC音録音ソフト

[このトピックは解決済みです]

フォーラムルール
フォーラムルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Name: larkpia
[URL]
入門者(4,545 ポイント)
Date: 2017年1月05日(木) 13:38
No: 1
(OFFLINE)

 WASAPIを使ったPC音録音ソフト

window7でC言語を使い、PC内の音(スピーカーから出力される音)を取得するソフトを作ろうとしています。

フリーソフトaudacityでWASAPIを使えばサウンドミキサーを使うことなく、PC音を取得できることが分かり、WASAPIの導入を決めたのですが、PC音の取得の方法が分かりません。
WASAPIを使って再生・録音(マイクから)はできました。

何か、方法をご存知の方はヒントでもいいので教えてください。

初の質問版で分かりにくい部分があると思いますが、なにとぞよろしくお願いします。

Name: Math
[URL]
Date: 2017年1月05日(木) 19:53
No: 2
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

私はAudacityを何年も愛用しています。よくPCの音を録音します。
LAME v3.95もいれています。
http://csi.nisinippon.com/aud.png
コントロールパネルのサウンドがこうなってると思うのでここ
を参考にステレオミキサーを有効にするといいと思います。
https://www18.atwiki.jp/live2ch/pages/227.html

Name: larkpia
[URL]
入門者(4,545 ポイント)
Date: 2017年1月05日(木) 20:36
No: 3
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

早速の返信ありがとうございます。

audacityにおいてオーディオホストをMMEからWindowWASAPIに変更し、録音デバイスをスピーカーに変更してやるとサウンドミキサーが無効のままでもPC音を取得できます。
これと同じことを自作のプログラムで実装したく試行錯誤してるのですが、とっかかりもつかめません。 もし何かご存知であれば教えてください!

Name: Math
[URL]
Date: 2017年1月05日(木) 21:21
No: 4
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

あ、本当だ。Audacityは無効のまま出来ている。ややこしい問題ですね!

Name: Math
[URL]
Date: 2017年1月05日(木) 21:41
No: 5
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

ここにhttp://audacity-mp3.xyz/download-install/
[ホスト
「MME」「Windows DirectSound」「Windows WASAPI」の3つの中から選びます。
それぞれ違うのは、動作速度の違いだけです。(動画速度と言うのは、録音した際の遅延の事)
性能の差としては「Windows WASAPI > Windows DirectSound > MME」の順で、「Windows WASAPI」が一番優れています]
とあるけど「ルール」http://dixq.net/board/board.htmlに則って詳しく説明してください。(また、何が目的でなぜそうしたいのか他人にもよくわかるようにしてください。)

Name: larkpia
[URL]
入門者(4,545 ポイント)
Date: 2017年1月05日(木) 22:15
No: 6
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

失礼しました。ルールがあったんですね。以下に書き直します。

[1] 質問文
 [1.1] 自分が今行いたい事は何か
   スピーカーから出力される音をWASAPIを使用して取得すること

 [1.2] どのように取り組んだか(プログラムコードがある場合記載)
   WASAPIを使用した再生・録音(マイク)プログラムを作成し、これを改良することでスピーカーからの音を取得しようとしている。
   スピーカーデバイスをキャプチャーモードでオープンできないかを試行錯誤している。

 [1.3] どのようなエラーやトラブルで困っているか(エラーメッセージが解る場合は記載)

 [1.4] 今何がわからないのか、知りたいのか
   「スピーカーから出力される音をサウンドミキサーを使用せずに取得することはできない。」と挫折したが、フリーソフト”audacity” ではオーディオホストにWASAPIを指定した場合サウンドミキサーを使用せずに録音できることに気が付き、これと同じようにプログラムを実装したい。しかし、その方法が分からない。

[2] 環境  
 [2.1] OS : Windows7 64bit
 [2.2] コンパイラ名 : VisualStudio2010 Express

Name: Math
[URL]
Date: 2017年1月06日(金) 09:07
No: 7
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

なんの目的か書かれてない。作っている側は分かって他人への説明にはなってない。レイテンシがすくないとか劣化が気になるのか?WASAPIには排他モードと共有モードの2種類があり、実際にレイテンシが少なくなるのは排他モードの方です。WASAPIはCOMを使ったライブラリです。プログラムをつくろうとするとWASAPI.COMの知識が必要です。
WASAPIを使用した再生・録音(マイク)プログラムを作成とあるがコードを一行も示せないのは大変疑わしい。基本部分を提示するここと。「ルール」をよく読むこと。まず基本設計を他人に分かるように説明する事が肝要です。

Name: larkpia
[URL]
入門者(4,545 ポイント)
Date: 2017年1月06日(金) 11:18
No: 8
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

Mathさん、ご指摘ありがとうございます。少しは分かりやすくなってるといいのですが…

[目的] 
パソコン2台をLANケーブルでつなぎ、一方のパソコンAの音をもう一方のパソコンBに送るプログラムを作成しています。パソコンAにはステレオミキサーが入っていないためWASAPIを使用し、パソコンの音を取得しようと考えています。

[コード]
エラー処理は除いた、WASAPIの初期化部分とイベント処理部分です。

コード[C]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
 
#include <Windows.h>
#include <stdio.h>
#include <process.h>
#include <Audioclient.h>
#include <mmdeviceapi.h>
#include <avrt.h>
 
#pragma comment(lib, "avrt.lib")
 
const WORD nBitrate = 16;
const WORD nFrequency = 44100;
const WORD nChannel = 2;
 
IAudioCaptureClient *CaptureClient = NULL;
IAudioClient *AudioClient = NULL;
IAudioRenderClient *RenderClient = NULL;
IMMDevice *device = NULL;
IMMDeviceCollection *deviceCollection = NULL;
IMMDeviceEnumerator *deviceEnumerator = NULL;
 
HANDLE ShutdownEvent;
HANDLE CaptureThread;
HANDLE AudioSamplesReadyEvent;
 
HWND hwnd;
WAVEFORMATEXTENSIBLE waveFormat;
CRITICAL_SECTION critical_section;
FILE *fp;
 
int InitWasapi()
{
    HRESULT hr;
 
    /////////////////////
    // デバイスの選択
    hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&deviceEnumerator));
    hr = deviceEnumerator->EnumAudioEndpoints(eAll, DEVICE_STATE_ACTIVE, &deviceCollection);
   
    deviceCollection->Item(0, &device); // スピーカーは 0
 
    /////////////////////
    // イベントの作成
    ShutdownEvent = CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
    AudioSamplesReadyEvent = CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
 
    /////////////////////
    // AudioClient の準備
    hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, reinterpret_cast<void **>(&AudioClient));
 
    /////////////////////
    // デバイスのレイテンシの取得
    REFERENCE_TIME DefaultDevicePeriod;
    REFERENCE_TIME MinimumDevicePeriod;
    hr = AudioClient->GetDevicePeriod(&DefaultDevicePeriod, &MinimumDevicePeriod);
 
    /////////////////////
    // waveフォームの作成
    waveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
    waveFormat.Format.nChannels = nChannel;
    waveFormat.Format.nSamplesPerSec = nFrequency;
    waveFormat.Format.wBitsPerSample = nBitrate;
    waveFormat.Format.nBlockAlign = waveFormat.Format.wBitsPerSample/8 * waveFormat.Format.nChannels;
    waveFormat.Format.nAvgBytesPerSec = waveFormat.Format.nSamplesPerSec * waveFormat.Format.nBlockAlign;
    waveFormat.Format.cbSize = 22;
    waveFormat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
    waveFormat.Samples.wValidBitsPerSample = waveFormat.Format.wBitsPerSample;
    waveFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
 
    //////////////////////
    // AudioClientの初期化
    hr = AudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE,
            AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
            DefaultDevicePeriod, DefaultDevicePeriod, (WAVEFORMATEX *)&waveFormat, NULL);
    UINT32 frame;
    if(FAILED(hr)){
        if(hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED){
            hr = AudioClient->GetBufferSize(&frame);
            DefaultDevicePeriod = (REFERENCE_TIME)(10000.0*1000*frame/waveFormat.Format.nSamplesPerSec+0.5);
            SafeRelease(&AudioClient);
            hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, reinterpret_cast<void **>(&AudioClient));
            if(FAILED(hr)){
                MessageBox(hwnd, "AudioClientの2度目の準備に失敗", "err", MB_ICONINFORMATION);
                return FALSE;
            }
            hr = AudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE,
                    AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
                    DefaultDevicePeriod, DefaultDevicePeriod, (WAVEFORMATEX *)&waveFormat, NULL);
        }
 
        if(FAILED(hr)){
            MessageBox(hwnd, "AudioClientの初期化に失敗", "err", MB_ICONINFORMATION);
            return FALSE;
        }
    }
 
    //////////////////////
    // イベントをセット
    hr = AudioClient->SetEventHandle(AudioSamplesReadyEvent);
    hr = AudioClient->GetService(IID_PPV_ARGS(&CaptureClient));
    /****************************
        サービスの取得に失敗する
    *****************************/
    if(FAILED(hr)){
        MessageBox(hwnd, "サービスの取得に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    /////////////////////
    // クリティカルセクション初期化
    InitializeCriticalSection(&critical_section);  
   
    /////////////////////
    // file オープン
    fp = fopen("sample.data", "w+b");
    if(fp==NULL){
        MessageBox(hwnd, "ファイルのオープンに失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    /////////////////////
    // スレッドの作成
    CaptureThread = CreateThread( NULL, 0, WASAPICaptureThread, NULL, 0, NULL);
    if(CaptureThread == NULL){
        MessageBox(hwnd, "スレッドの作成に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    /////////////////////
    // キャプチャの開始
    hr = AudioClient->Start();
    if(FAILED(hr)){
        MessageBox(hwnd, "キャプチャを開始できません", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    return TRUE;
}
 
DWORD WINAPI WASAPICaptureThread(LPVOID Context)
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    if(FAILED(hr)){
        MessageBox(hwnd, "COM初期化に失敗", "err_thread", MB_ICONINFORMATION);
        return hr;
    }
 
    //////////////////////
    // スレッドの優先順位
    DWORD mmcsTaskIndex = 0;
    HANDLE mmcsHandle = AvSetMmThreadCharacteristics("Audio", &mmcsTaskIndex);
    if(mmcsHandle == NULL){
        MessageBox(hwnd, "スレッドの優先順位変更に失敗", "err_thread", MB_ICONINFORMATION);
    }
 
    HANDLE waitArray[2] = {ShutdownEvent, AudioSamplesReadyEvent};
 
    bool stillPlaying = true;
    while(stillPlaying){
 
        DWORD waitResult = WaitForMultipleObjects(2, waitArray, FALSE, INFINITE);
        switch(waitResult)
        {
        case WAIT_OBJECT_0 + 0: // ShutdownEvent
            stillPlaying = false;
 
        case WAIT_OBJECT_0 + 1: // AudioSamplesReadyEvent
            DWORD *pData;
            DWORD *zeroData;
            ULONGLONG adjsData;
            UINT32 framesAvailable;
            DWORD  flags;
       
            hr = CaptureClient->GetBuffer((BYTE **)&pData, &framesAvailable, &flags, NULL, NULL);
            if(SUCCEEDED(hr))
            {
                EnterCriticalSection(&critical_section);
                if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
                {
                    zeroData = (DWORD *)calloc(framesAvailable, sizeof(DWORD));
                    fwrite(zeroData, sizeof(DWORD),framesAvailable, fp);
                    free(zeroData);
                }
                else
                {
                    fwrite(pData, sizeof(DWORD),framesAvailable,fp);
                }
 
                LeaveCriticalSection(&critical_section);
 
                hr = CaptureClient->ReleaseBuffer(framesAvailable);
                if(FAILED(hr)){
                    MessageBox(hwnd, "リリースに失敗", "err_thread", MB_ICONINFORMATION);
                }
            }
            break;
        }
   
    }
 
    return 0;
}

Name: ポニョ
[URL]
Date: 2017年1月06日(金) 13:24
No: 9
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

どうせこんなことだろうと思ってみてたけど10年前のVistaにもOSの機能としてはいってるよ。7にないわけないだろ?だから初めからの投稿をよく読めばわかるのに。
http://pcgenki.com/soft3/vista_rokuon.htm

Name: larkpia
[URL]
入門者(4,545 ポイント)
Date: 2017年1月06日(金) 14:38
No: 10
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

ポニョさん返信ありがとうございます。
録音デバイスのところで 「無効なデバイスを表示する」 としても表示されなかったのですが、最新のデバイスをダウンロードすると表示されるようになりました。拙い説明を読み解いていただきありがとうございました。

ただ、Audacityではステレオミキサーを使用しない録音を実装しているので、その方法も、もう少し考えてみたいと思います。

Name: Math
[URL]
Date: 2017年1月06日(金) 19:56
No: 11
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

私のWindows10のマシンではまだ細かい制御ができるようです。http://csi.nisinippon.com/aud2.png富士通に問い合わせたがそこまでは知らないといわれた。このサンプルは20行程のエラーがでる。詳し事はわから無かったが。私が試したなかでエラーがでず正常にビルドできた例は
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
///////////////////////////////////////////////////////////////////////////////////
// Main : WASAPI再生サンプル v1.00                                               //
//                                                                               //
// このソースコードは自由に改変して使用可能です。                                //
// また商用利用も可能ですが、すべての環境で正しく動作する保障はありません。      //
//                          <!-- m --><a class=\"postlink\" href=\"http://www.charatsoft.com/\">http://www.charatsoft.com/</a><!-- m -->                           //
///////////////////////////////////////////////////////////////////////////////////
#include "CWindow.h"
#include "CWav.h"
 
#define DEBUGMODE               // デバッグ出力しない場合はコメント化する
#include "DEBUG.H"
 
// マクロ
#define SAFE_RELEASE(x)     { if( x ) { x->Release(); x=NULL; } }
#define SAFE_FREE(x)        { if( x ) { free(x); x=NULL; } }
 
 
//////////////////////////////////////////////////////////////////////////////////////
// WASAPI関連
//////////////////////////////////////////////////////////////////////////////////////
 
// ヘッダ
#include <mmdeviceapi.h>
#include <Audioclient.h>
#include <audiopolicy.h>
#include <endpointvolume.h>
#include <FunctionDiscoveryKeys_devpkey.h>
 
// ライブラリ
#pragma comment(lib, "Avrt.lib")
#pragma comment(lib, "winmm.lib")
 
// インターフェースのGUIDの実体(プロジェクト内のCファイルに必ず1つ必要)
const CLSID CLSID_MMDeviceEnumerator    = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator       = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient              = __uuidof(IAudioClient);
const IID IID_IAudioClock               = __uuidof(IAudioClock);
const IID IID_IAudioRenderClient        = __uuidof(IAudioRenderClient);
 
 
 
//////////////////////////////////////////////////////////////////////////////////////
// グローバル変数
//////////////////////////////////////////////////////////////////////////////////////
CWindow                 win;                            // ウィンドウ
CWav                    wav;                            // WAVファイル
int                     iAddr = 0;                      // WAVファイルの再生位置
 
IMMDeviceEnumerator     *pDeviceEnumerator  = NULL;     // マルチメディアデバイス列挙インターフェース
IMMDevice               *pDevice            = NULL;     // デバイスインターフェース
IAudioClient            *pAudioClient       = NULL;     // オーディオクライアントインターフェース
IAudioRenderClient      *pRenderClient      = NULL;     // レンダークライアントインターフェース
int                     iFrameSize          = 0;        // 1サンプル分のバッファサイズ
 
HANDLE                  hEvent              = NULL;     // イベントハンドル
HANDLE                  hThread             = NULL;     // スレッドハンドル
BOOL                    bThreadLoop         = FALSE;    // スレッド処理中か
 
 
//////////////////////////////////////////////////////////////////////////////////////
// 再生スレッド
//////////////////////////////////////////////////////////////////////////////////////
DWORD CALLBACK PlayThread( LPVOID param )
{
    DEBUG( "スレッド開始\n" );
 
    while( bThreadLoop ) {
        // 次のバッファ取得が必要になるまで待機
        DWORD retval = WaitForSingleObject( hEvent,2000 );
        if( retval!=WAIT_OBJECT_0 ) {
            DEBUG( "タイムアウト\n" );
            pAudioClient->Stop();
            break;
        }
 
        // 今回必要なフレーム数を取得
        UINT32 frame_count;
        HRESULT ret = pAudioClient->GetBufferSize( &frame_count );
//      ODS( "フレーム数    : %d\n",frame_count );
 
        // フレーム数からバッファサイズを算出
        int buf_size = frame_count * iFrameSize;
//      ODS( "バッファサイズ : %dbyte\n",buf_size );
 
 
        // 出力バッファのポインタを取得
        LPBYTE dst;
        ret = pRenderClient->GetBuffer( frame_count,&dst );
        if( SUCCEEDED(ret) ) {
            // ソースバッファのポインタを取得
            LPBYTE src = wav.GetBuffer();
 
            // 現在のカーソルから次に必要なバッファサイズを加算したときにWAVバッファのサイズを超えるか
            if( iAddr+buf_size>wav.GetBufferSize() ) {
                ////////////////////////////////////////////////////////////////////////////////////////
                // 超える場合はまず現在のカーソルから最後までをコピーし、次に残った分を先頭から補充する
                ////////////////////////////////////////////////////////////////////////////////////////
 
                // WAVバッファサイズから現在のカーソル位置を差し引いたものが最後までのバッファサイズ
                int last_size = wav.GetBufferSize() - iAddr;
 
                // 現在のカーソルから最後までのバッファを出力バッファの先頭にコピーする
                memcpy( &dst[0],&src[iAddr],last_size );
 
                // 今回必要なサイズから今コピーしたサイズを引いたものが先頭から補充するバッファサイズ
                int begin_size = buf_size - last_size;
 
                // WAVバッファの先頭から補充サイズ分を出力バッファに追加コピーする
                memcpy( &dst[last_size],&src[0],begin_size );
 
                // 補充した分のサイズを現在のカーソルとする
                iAddr = begin_size;
 
                ODS( "LOOP OK\n" );
            } else {
                ////////////////////////////////////////////////////////////////////////////////////////
                // 超えない場合は現在のカーソルから必要なバッファ分をコピーしてカーソルを進める
                ////////////////////////////////////////////////////////////////////////////////////////
 
                // WAVバッファの現在のカーソルから出力バッファに指定サイズ分コピーする
                memcpy( &dst[0],&src[iAddr],buf_size );
 
                // カーソルをコピーした分だけ進める
                iAddr += buf_size;
            }
 
            // バッファを開放
            pRenderClient->ReleaseBuffer( frame_count,0 );
        }
 
    }
 
    DEBUG( "スレッド終了\n" );
    return 0;
}
 
 
//////////////////////////////////////////////////////////////////////////////////////
// WASAPIの初期化
//////////////////////////////////////////////////////////////////////////////////////
BOOL InitWasapi( int latency )
{
    HRESULT ret;
 
    // マルチメディアデバイス列挙子
    ret = CoCreateInstance( CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator );
    if( FAILED(ret) ) {
        DEBUG( "CLSID_MMDeviceEnumeratorエラー\n" );
        return FALSE;
    }
 
    // デフォルトのデバイスを選択
    ret = pDeviceEnumerator->GetDefaultAudioEndpoint( eRender,eConsole,&pDevice );
    if( FAILED(ret) ) {
        DEBUG( "GetDefaultAudioEndpointエラー\n" );
        return FALSE;
    }
 
    // オーディオクライアント
    ret = pDevice->Activate( IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient );
    if( FAILED(ret) ) {
        DEBUG( "オーディオクライアント取得失敗\n" );
        return FALSE;
    }
 
    // フォーマットの構築
    WAVEFORMATEXTENSIBLE wf;
    ZeroMemory( &wf,sizeof(wf) );
    wf.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE);
    wf.Format.wFormatTag            = WAVE_FORMAT_EXTENSIBLE;
    wf.Format.nChannels             = 2;
    wf.Format.nSamplesPerSec        = 44100;
    wf.Format.wBitsPerSample        = 16;
    wf.Format.nBlockAlign           = wf.Format.nChannels * wf.Format.wBitsPerSample / 8;
    wf.Format.nAvgBytesPerSec       = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign;
    wf.Samples.wValidBitsPerSample  = wf.Format.wBitsPerSample;
    wf.dwChannelMask                = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
    wf.SubFormat                    = KSDATAFORMAT_SUBTYPE_PCM;
 
    // 1サンプルのサイズを保存(16bitステレオなら4byte)
    iFrameSize = wf.Format.nBlockAlign;
 
    // フォーマットのサポートチェック
    ret = pAudioClient->IsFormatSupported( AUDCLNT_SHAREMODE_EXCLUSIVE,(WAVEFORMATEX*)&wf,NULL );
    if( FAILED(ret) ) {
        DEBUG( "未サポートのフォーマット\n" );
        return FALSE;
    }
 
    // レイテンシ設定
    REFERENCE_TIME default_device_period = 0;
    REFERENCE_TIME minimum_device_period = 0;
 
    if( latency!=0 ) {
        default_device_period = (REFERENCE_TIME)latency * 10000LL;      // デフォルトデバイスピリオドとしてセット
        DEBUG( "レイテンシ指定             : %I64d (%fミリ秒)\n",default_device_period,default_device_period/10000.0 );
    } else {
        ret = pAudioClient->GetDevicePeriod( &default_device_period,&minimum_device_period );
        DEBUG( "デフォルトデバイスピリオド : %I64d (%fミリ秒)\n",default_device_period,default_device_period/10000.0 );
        DEBUG( "最小デバイスピリオド       : %I64d (%fミリ秒)\n",minimum_device_period,minimum_device_period/10000.0 );
    }
 
    // 初期化
    UINT32 frame = 0;
    ret = pAudioClient->Initialize( AUDCLNT_SHAREMODE_EXCLUSIVE,
                                    AUDCLNT_STREAMFLAGS_NOPERSIST | AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
                                    default_device_period,              // デフォルトデバイスピリオド値をセット
                                    default_device_period,              // デフォルトデバイスピリオド値をセット
                                    (WAVEFORMATEX*)&wf,
                                    NULL );
    if( FAILED(ret) ) {
        if( ret==AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ) {
            DEBUG( "バッファサイズアライメントエラーのため修正する\n" );
 
            // 修正後のフレーム数を取得
            ret = pAudioClient->GetBufferSize( &frame );
            DEBUG( "修正後のフレーム数         : %d\n",frame );
            default_device_period = (REFERENCE_TIME)( 10000.0 *                     // (REFERENCE_TIME(100ns) / ms) *
                                                      1000 *                        // (ms / s) *
                                                      frame /                       // frames /
                                                      wf.Format.nSamplesPerSec +    // (frames / s)
                                                      0.5);                         // 四捨五入?
            DEBUG( "修正後のレイテンシ         : %I64d (%fミリ秒)\n",default_device_period,default_device_period/10000.0  );
 
            // 一度破棄してオーディオクライアントを再生成
            SAFE_RELEASE( pAudioClient );
            ret = pDevice->Activate( IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient );
            if( FAILED(ret) ) {
                DEBUG( "オーディオクライアント再取得失敗\n" );
                return FALSE;
            }
 
            // 再挑戦
            ret = pAudioClient->Initialize( AUDCLNT_SHAREMODE_EXCLUSIVE,
                                            AUDCLNT_STREAMFLAGS_NOPERSIST | AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
                                            default_device_period,
                                            default_device_period,
                                            (WAVEFORMATEX*)&wf,
                                            NULL );
        }
 
        if( FAILED(ret) ) {
            DEBUG( "初期化失敗 : 0x%08X\n",ret );
            switch( ret )
            {
            case AUDCLNT_E_NOT_INITIALIZED:                 DEBUG( "AUDCLNT_E_NOT_INITIALIZED\n" );                 break;
            case AUDCLNT_E_ALREADY_INITIALIZED:             DEBUG( "AUDCLNT_E_ALREADY_INITIALIZED\n" );             break;
            case AUDCLNT_E_WRONG_ENDPOINT_TYPE:             DEBUG( "AUDCLNT_E_WRONG_ENDPOINT_TYPE\n" );             break;
            case AUDCLNT_E_DEVICE_INVALIDATED:              DEBUG( "AUDCLNT_E_DEVICE_INVALIDATED\n" );              break;
            case AUDCLNT_E_NOT_STOPPED:                     DEBUG( "AUDCLNT_E_NOT_STOPPED\n" );                     break;
            case AUDCLNT_E_BUFFER_TOO_LARGE:                DEBUG( "AUDCLNT_E_BUFFER_TOO_LARGE\n" );                break;
            case AUDCLNT_E_OUT_OF_ORDER:                    DEBUG( "AUDCLNT_E_OUT_OF_ORDER\n" );                    break;
            case AUDCLNT_E_UNSUPPORTED_FORMAT:              DEBUG( "AUDCLNT_E_UNSUPPORTED_FORMAT\n" );              break;
            case AUDCLNT_E_INVALID_SIZE:                    DEBUG( "AUDCLNT_E_INVALID_SIZE\n" );                    break;
            case AUDCLNT_E_DEVICE_IN_USE:                   DEBUG( "AUDCLNT_E_DEVICE_IN_USE\n" );                   break;
            case AUDCLNT_E_BUFFER_OPERATION_PENDING:        DEBUG( "AUDCLNT_E_BUFFER_OPERATION_PENDING\n" );        break;
            case AUDCLNT_E_THREAD_NOT_REGISTERED:           DEBUG( "AUDCLNT_E_THREAD_NOT_REGISTERED\n" );           break;
//          case AUDCLNT_E_NO_SINGLE_PROCESS:               DEBUG( "AUDCLNT_E_NO_SINGLE_PROCESS\n" );               break;  // VC2010では未定義?
            case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED:      DEBUG( "AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED\n" );      break;
            case AUDCLNT_E_ENDPOINT_CREATE_FAILED:          DEBUG( "AUDCLNT_E_ENDPOINT_CREATE_FAILED\n" );          break;
            case AUDCLNT_E_SERVICE_NOT_RUNNING:             DEBUG( "AUDCLNT_E_SERVICE_NOT_RUNNING\n" );             break;
            case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED:        DEBUG( "AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED\n" );        break;
            case AUDCLNT_E_EXCLUSIVE_MODE_ONLY:             DEBUG( "AUDCLNT_E_EXCLUSIVE_MODE_ONLY\n" );             break;
            case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL:    DEBUG( "AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL\n" );    break;
            case AUDCLNT_E_EVENTHANDLE_NOT_SET:             DEBUG( "AUDCLNT_E_EVENTHANDLE_NOT_SET\n" );             break;
            case AUDCLNT_E_INCORRECT_BUFFER_SIZE:           DEBUG( "AUDCLNT_E_INCORRECT_BUFFER_SIZE\n" );           break;
            case AUDCLNT_E_BUFFER_SIZE_ERROR:               DEBUG( "AUDCLNT_E_BUFFER_SIZE_ERROR\n" );               break;
            case AUDCLNT_E_CPUUSAGE_EXCEEDED:               DEBUG( "AUDCLNT_E_CPUUSAGE_EXCEEDED\n" );               break;
            default:                                        DEBUG( "UNKNOWN\n" );                                   break;
            }
            return FALSE;
        }
    }
 
    // イベント生成
    hEvent = CreateEvent( NULL,FALSE,FALSE,NULL );
    if( !hEvent ) {
        DEBUG( "イベントオブジェクト作成失敗\n" );
        return FALSE;
    }
 
    // イベントのセット
    ret = pAudioClient->SetEventHandle( hEvent );
    if( FAILED(ret) ) {
        DEBUG( "イベントオブジェクト設定失敗\n" );
        return FALSE;
    }
 
    // レンダラーの取得
    ret = pAudioClient->GetService( IID_IAudioRenderClient,(void**)&pRenderClient );
    if( FAILED(ret) ) {
        DEBUG( "レンダラー取得エラー\n" );
        return FALSE;
    }
 
    // WASAPI情報取得
    ret = pAudioClient->GetBufferSize( &frame );
    DEBUG( "設定されたフレーム数       : %d\n",frame );
 
    UINT32 size = frame * iFrameSize;
    DEBUG( "設定されたバッファサイズ   : %dbyte\n",size );
    DEBUG( "1サンプルの時間            : %f秒\n",(float)size/wf.Format.nSamplesPerSec );
 
    // ゼロクリアをしてイベントをリセット
    LPBYTE pData;
    ret = pRenderClient->GetBuffer( frame,&pData );
    if( SUCCEEDED(ret) ) {
        ZeroMemory( pData,size );
        pRenderClient->ReleaseBuffer( frame,0 );
    }
 
    // スレッドループフラグを立てる
    bThreadLoop = TRUE;
 
    // 再生スレッド起動
    DWORD dwThread;
    hThread = CreateThread( NULL,0,PlayThread,NULL,0,&dwThread );
    if( !hThread ) {
        // 失敗
        return FALSE;
    }
 
    // 再生開始
    pAudioClient->Start();
 
    DEBUG( "WASAPI初期化完了\n" );
    return TRUE;
}
 
 
//////////////////////////////////////////////////////////////////////////////////////
// WASAPIの終了
//////////////////////////////////////////////////////////////////////////////////////
void ExitWasapi()
{
    // スレッドループフラグを落とす
    bThreadLoop = FALSE;
 
    // スレッド終了処理
    if( hThread ) {
        // スレッドが完全に終了するまで待機
        WaitForSingleObject( hThread,INFINITE );
        CloseHandle( hThread );
        hThread = NULL;
    }
 
    // イベントを開放処理
    if( hEvent ) {
        CloseHandle( hEvent );
        hEvent = NULL;
    }
 
    // インターフェース開放
    SAFE_RELEASE( pRenderClient );
    SAFE_RELEASE( pAudioClient );
    SAFE_RELEASE( pDevice );
    SAFE_RELEASE( pDeviceEnumerator );
 
    DEBUG( "WASAPI終了\n" );
}
 
 
//////////////////////////////////////////////////////////////////////////////////////
// メインルーチン
//////////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain( HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszCmdParam,int nCmdShow )
{
    // デバッグ定義
    INITDEBUG( "DEBUG.TXT" );
    CLEARDEBUG;
 
    // ウィンドウ生成
    if( !win.Create(hInstance,L"TestWasapi") ) {
        DEBUG( "ウィンドウ生成エラー\n" );
        return -1;
    }
 
    // 日本語入力の無効化
    ImmAssociateContext( win.hWnd, NULL );
 
    // WAVをロード
    if( !wav.Load("loop.wav") ) {
        win.Delete();
        return -1;
    }
 
    // COMの初期化
    CoInitialize( NULL );
 
    // WASAPI初期化
    if( !InitWasapi(0) ) {          // 0ならデフォルトデバイスピリオドを使用
        // エラーなら終了する
        MessageBoxA( win.hWnd,"WASAPI初期化失敗","エラー",MB_OK|MB_ICONHAND );
        ExitWasapi();
        CoUninitialize();
        win.Delete();
        return -1;
    }
 
    // ウィンドウループ
    while(1) {
        MSG msg;
        int ret = GetMessage( &msg,NULL,0,0 );      // メッセージが登録されるまでブロック
        if( ret==0 || ret==-1 ) {
            // 終了コードなら抜ける
            break;
        }
        // メッセージを処理する
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
 
    // WASAPI終了
    ExitWasapi();
 
    // COMの終了
    CoUninitialize();
 
    // ウィンドウ削除
    win.Delete();
 
    return 0;
}

Name: Math
[URL]
Date: 2017年1月07日(土) 00:04
No: 12
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

久し振りにハードウェアの勉強ができて面白かったです。WASAPIの詳細は不明です。Audacityのforumで質問してみて下さい。http://forum.audacityteam.org/

Name: Math
[URL]
Date: 2017年1月07日(土) 07:57
No: 13
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

WASAPI-sample-全コード[Main.cpp]前に書いたもの
[CWav.cpp]
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#pragma warning( disable : 4996 )
#include "CWav.h"
#include <io.h>
#include <fcntl.h>
 
//#define DEBUGMODE
#include "DEBUG.H"
 
#define FREE(x)     { if(x) free(x); x=NULL; }
 
CWav::CWav()
{
    fp = NULL;
    mBuf = NULL;
    iBuf = 0;
    iStreamPoint = 0;
    dwMode = WAVMODE_STATIC;
    ZeroMemory( &mHead,sizeof(mHead) );
}
 
 
CWav::~CWav()
{
    Close();
    FREE( mBuf );
}
 
BOOL CWav::Load( const char *file,DWORD mode )
{
    Close();
 
    fp = fopen( file,"rb" );
    if( !fp )
        return FALSE;
 
    fread( &mHead,sizeof(WAVHEADER),1,fp );
    if( strnicmp(mHead.mRiff,"RIFF",4)!=0 ) {
        DEBUG( "RIFFフォーマットエラー\n","" );
        return FALSE;
    }
 
    if( strnicmp(mHead.mWave,"WAVE",4)!=0 ) {
        DEBUG( "WAVチャンクが見つからない\n","" );
        return FALSE;
    }
 
    if( strnicmp(mHead.mFmt,"fmt ",4)!=0 ) {
        DEBUG( "フォーマットチャンクが見つからない\n","" );
        return FALSE;
    }
 
 
    DEBUG( "フォーマットID     = %d\n",mHead.wFormatID );
    DEBUG( "チャンクサイズ     = %d\n",mHead.dwChuncSize );
    DEBUG( "チャンネル数       = %d\n",mHead.wChannel );
    DEBUG( "ビット数           = %d\n",mHead.wBitrate );
    DEBUG( "サンプリング周波数 = %d\n",mHead.dwSample );
 
    // dataチャンクを探す
    DATACHUNC data;
    while(1) {
        // EOFなら終了
        if( feof(fp) ) {
            DEBUG( "dataチャンクが見つからなかった\n","" );
            fclose( fp );
            return FALSE;
        }
        fread( &data,sizeof(DATACHUNC),1,fp );
 
        // dataチャンクなら抜ける
        if( strnicmp(data.mData,"data",4)==0 )
            break;
        // 次のチャンクへ
        if( !fseek( fp,data.dwSize,SEEK_CUR ) ) {
            DEBUG( "seekエラー\n","" );
            fclose( fp );
            return FALSE;
        }
    }
 
    // データサイズの取得
    iBuf = data.dwSize;
    DEBUG( "データサイズ       = %d\n",iBuf );
 
    // モード記憶
    dwMode = mode;
 
    switch( dwMode )
    {
    case WAVMODE_STATIC:
        mBuf = (LPBYTE)realloc( mBuf,iBuf );
        if( !mBuf ) {
            DEBUG( "メモリ確保エラー\n","" );
            return FALSE;
        }
 
        fread( mBuf,iBuf,1,fp );
        break;
    }
    return TRUE;
}
 
BOOL CWav::Close( void )
{
    if( fp ) {
        fclose( fp );
        fp = NULL;
        DEBUG( "CLOSE OK\n","" );
    }
    dwMode = WAVMODE_STATIC;
    iStreamPoint = 0;
    return TRUE;
}
 
BOOL CWav::Create( const char *file,int ch,int bit,int samp )
{
    Close();
 
    iBuf = 0;
    memcpy( &mHead.mRiff,"RIFF",4 );
    mHead.dwFileSize = sizeof(WAVHEADER) + sizeof(DATACHUNC);
    memcpy( &mHead.mWave,"WAVE",4 );
    memcpy( &mHead.mFmt,"fmt ",4 );
    mHead.dwChuncSize = 16;     // 16固定
    mHead.wFormatID = 1;
    mHead.wChannel = ch;
    mHead.dwSample = samp;
    mHead.wBitrate = bit;
    mHead.dwBytePerSec = mHead.wChannel * mHead.dwSample * mHead.wBitrate / 8;
    mHead.wBlockSize = mHead.wChannel * mHead.wBitrate / 8;
 
    memcpy( &mData.mData,"data",4 );
    mData.dwSize = 0;
 
    strcpy( mFile,file );
    fp = fopen( mFile,"wb" );
    if( !fp )
        return FALSE;
 
    fwrite( &mHead,sizeof(WAVHEADER),1,fp );
    fwrite( &mData,sizeof(DATACHUNC),1,fp );
 
    return TRUE;
}
 
BOOL CWav::Write( const LPVOID buf,int size )
{
    if( dwMode!=WAVMODE_STATIC )
        return FALSE;
 
/*  mBuf = (LPBYTE)realloc( mBuf,iBuf+size );
    if( !mBuf )
        return FALSE;
 
    memcpy( &mBuf[iBuf],buf,size );/**/
 
    // データ書き込み先
//  DEBUG( "書き込み先 %08X\n",sizeof(WAVHEADER) + sizeof(DATACHUNC)+iBuf );
    fseek( fp,sizeof(WAVHEADER) + sizeof(DATACHUNC)+iBuf,SEEK_SET );
    fwrite( buf,size,1,fp );
 
    // ヘッダの更新
    mHead.dwFileSize += size;
    mData.dwSize += size;
    fseek( fp,0,SEEK_SET );
    fwrite( &mHead,sizeof(WAVHEADER),1,fp );
    fwrite( &mData,sizeof(DATACHUNC),1,fp );
   
    iBuf += size;
 
    return TRUE;
}
 
BOOL CWav::Read( char *buf,int *size )
{
    if( dwMode!=WAVMODE_STREAM ) {
        DEBUG( "ストリームモードではない\n","" );
        return FALSE;
    }
 
    // ポインタを移動
    fseek( fp,sizeof(WAVHEADER) + sizeof(DATACHUNC)+iStreamPoint,SEEK_SET );
 
    // 最大値のチェック
    int read = *size;
    if( read+iStreamPoint>iBuf )
        read = iBuf - iStreamPoint;
 
    *size = read;
 
    iStreamPoint += read;
 
    if( read<1 ) {
        DEBUG( "ロード終了\n","" );
        return FALSE;
    }
 
//  DEBUG( "ロードサイズ %d\n",read );
 
    fread( buf,read,1,fp );
 
    return TRUE;
}

「CWindow.cpp]
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
#pragma warning( disable : 4996 )
#include "CWindow.h"
 
//#define DEBUGMODE
#include "DEBUG.H"
 
///////////////////////////////////////////////////////////////////////////////////
// スタティック変数
///////////////////////////////////////////////////////////////////////////////////
HINSTANCE           CWindow::hInstance      = NULL;
HWND                CWindow::hWnd           = NULL;
BOOL                CWindow::bActive        = TRUE;
 
WCHAR               CWindow::mName[256]     = L"";
const WCHAR*        CWindow::cIconID        = IDI_APPLICATION;      // デフォルトのアイコン
HMENU               CWindow::hMenu          = NULL;
DWORD               CWindow::dwStyle        = WS_POPUP|WS_SYSMENU|WS_CAPTION|WS_MINIMIZEBOX;
DWORD               CWindow::dwExStyle      = 0;
 
LPONMSG             CWindow::mMsg           = NULL;
int                 CWindow::iMsg           = 0;
 
 
///////////////////////////////////////////////////////////////////////////////////
// コンストラクタ
///////////////////////////////////////////////////////////////////////////////////
CWindow::CWindow(void)
{
}
 
///////////////////////////////////////////////////////////////////////////////////
// デストラクタ
///////////////////////////////////////////////////////////////////////////////////
CWindow::~CWindow()
{
}
 
///////////////////////////////////////////////////////////////////////////////////
// メインウインドウのイベントハンドラ
///////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK CWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static CWindow *win = NULL;
 
    switch( uMsg )
    {
    case WM_CREATE:
        win = (CWindow*)lParam;
        break;
    case WM_ACTIVATE:
        bActive = LOWORD(wParam)?TRUE:FALSE;                // アクティブ状態変更
        break;
    case WM_DESTROY:                                        // ALT+F4が押されたら
        PostQuitMessage( 0 );
        break;
    case WM_MOUSEMOVE:
        break;
    case WM_SYSCOMMAND:
        switch( wParam )
        {
        case SC_CLOSE:
            PostQuitMessage(0);
            return 0;
        }
        break;
    case WM_IME_NOTIFY:
        switch( wParam )
        {
        case IMN_SETOPENSTATUS:
            HIMC hImc = ImmGetContext( hWnd );
            ImmSetOpenStatus( hImc,FALSE );
            break;
        }
        break;
    }
 
    // 特殊メッセージ処理
    int i;
    for( i=0;i<iMsg;i++ ) {
        if( uMsg==mMsg[i].uiMsg ) {
            return mMsg[i].cmdProc( hWnd,wParam,lParam );   // 特殊メッセージ操作完了なら
        }
    }
 
    return DefWindowProc( hWnd,uMsg,wParam,lParam );        // デフォルトを返す
}
 
///////////////////////////////////////////////////////////////////////////////////
// ウインドウを生成する
///////////////////////////////////////////////////////////////////////////////////
BOOL CWindow::Create( HINSTANCE hInst,const WCHAR *appName,BOOL show,DWORD w,DWORD h,HWND parent )
{
    WNDCLASS wc;
    DEVMODE dmMode;
 
    // 画面解像度をチェック
    EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&dmMode);
    // 16bit以上の解像度じゃないと起動できない
    if( dmMode.dmBitsPerPel<16 ) {
        MessageBoxW( GetDesktopWindow(),L"16Bit以上の解像度にしてください",L"起動できません",MB_OK );
        return FALSE;
    }
 
    // セット
    hInstance = hInst;
    wcscpy( mName,appName );
 
    // ウインドウクラス登録
    wc.style            = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNCLIENT;
    wc.lpfnWndProc      = WindowProc;
    wc.cbClsExtra       = 0;
    wc.cbWndExtra       = sizeof(DWORD);
    wc.hInstance        = hInstance;
    wc.hIcon            = LoadIcon(hInstance, cIconID );
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground    = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName     = MAKEINTRESOURCE( hMenu );
    wc.lpszClassName    = mName;
    if ( !RegisterClass(&wc) )
        return FALSE;
 
    // ウインドウ生成
    hWnd = CreateWindowExW(
        dwExStyle,
        wc.lpszClassName,       // Class
        mName,                  // Title bar
        dwStyle,                // Style
        GetSystemMetrics(SM_CXSCREEN)/2-w/2,
        GetSystemMetrics(SM_CYSCREEN)/2-h/2,
        w,                      // Init. x pos
        h,                      // Init. y pos
        parent,                 // Parent window
        NULL,                   // Menu handle
        hInstance,              // Program handle
        this                    // Create parms
    );
    if( !hWnd )
        return FALSE;           // 生成に失敗
 
    // フォントの設定
    HDC hdc = GetDC( hWnd );
    if( hdc ) {
        SetBkMode( hdc,TRANSPARENT );
        ReleaseDC( hWnd,hdc );
    }
 
    MoveClientWindowCenter( w,h );
    // ウインドウを表示
    if( show )
        ::ShowWindow( hWnd,SW_SHOW );
 
    return TRUE;
}
 
///////////////////////////////////////////////////////////////////////////////////
// 明示的にウィンドウを削除する
///////////////////////////////////////////////////////////////////////////////////
void CWindow::Delete( void )
{
    if( hWnd ) {
        // 通常のウィンドウなら
        ::DestroyWindow( hWnd );
        // 登録したクラス名を解除
        UnregisterClassW( mName,hInstance );
        ZeroMemory( &mName,sizeof(mName) );
        hWnd = NULL;
        hInstance = NULL;
    }
}
 
///////////////////////////////////////////////////////////////////////////////////
// カーソルの表示・非表示
///////////////////////////////////////////////////////////////////////////////////
void CWindow::ShowCursor( BOOL bShow )
{
    if( bShow )
        while(::ShowCursor(TRUE)<0);
    else
        while(::ShowCursor(FALSE)>=0);
}
 
///////////////////////////////////////////////////////////////////////////////////
// ウインドウの表示・非表示
///////////////////////////////////////////////////////////////////////////////////
void CWindow::ShowWindow( BOOL bShow )
{
    // ウインドウの表示
    if( bShow )
        ::ShowWindow( hWnd,SW_SHOW );
    else
        ::ShowWindow( hWnd,SW_HIDE );
}
 
///////////////////////////////////////////////////////////////////////////////////
// アプリケーションのアイコンの変更
///////////////////////////////////////////////////////////////////////////////////
void CWindow::SetIcon( const WCHAR *icon )
{
    cIconID = icon;
}
 
 
// 特殊メッセージの追加
BOOL CWindow::AddMsgProc( UINT msg,ONCOMMAND proc )
{
    int i;
    // 既に存在していないかチェック
    for( i=0;i<iMsg;i++ ) {
        if( mMsg[i].uiMsg==msg ) {
            // あれば新しいアドレスに更新
            mMsg[i].cmdProc = proc;
            return TRUE;
        }
    }
 
    // 追加
    iMsg++;
    mMsg = (LPONMSG)realloc( mMsg,sizeof(ONMSG)*iMsg );
    ZeroMemory( &mMsg[iMsg-1],sizeof(ONMSG) );
    mMsg[iMsg-1].uiMsg = msg;
    mMsg[iMsg-1].cmdProc = proc;
    return TRUE;
}
 
// ウインドウスタイルの変更(動的に変更も可能)
BOOL CWindow::SetWindowStyle( DWORD style )
{
    dwStyle = style;
    if( hWnd ) {
        // すでにウインドウが存在する場合は即反映
        ::SetWindowLong( hWnd,GWL_STYLE,style );
        ::SetWindowPos( hWnd,0,0,0,0,0,SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOSIZE|SWP_NOZORDER );
    }
    return TRUE;
}
 
// ウインドウの移動
void CWindow::Move( int x,int y )
{
    SetWindowPos( hWnd,0,x,y,0,0,SWP_NOSIZE|SWP_NOZORDER );
}
 
// ウインドウの移動(幅と高さも同時に変更)
void CWindow::Move( int x,int y,int w,int h )
{
    MoveWindow( hWnd,x,y,w,h,TRUE );
}
 
// 指定サイズがクライアント領域になるようにウインドウを配置
BOOL CWindow::MoveClientWindowCenter( int w,int h )
{
    RECT Win,Cli;
    GetWindowRect( hWnd,&Win );                             // ウインドウの左上を取得
    GetClientRect( hWnd,&Cli );                             // ウインドウ内のクライアント座標を取得
    int frame_w = (Win.right - Win.left) - Cli.right;       // フレームの幅
    int frame_h = (Win.bottom - Win.top) - Cli.bottom;      // フレームの高さ
    int scr_w   = GetSystemMetrics( SM_CXSCREEN );          // スクリーンの幅
    int scr_h   = GetSystemMetrics( SM_CYSCREEN );          // スクリーンの高さ
    SetWindowPos( hWnd,0,( scr_w - (frame_w/2+w) ) / 2,( scr_h - (frame_h/2+h) ) / 2,w+frame_w,h+frame_h,SWP_NOZORDER );
 
    return TRUE;
}
 
// メニューアイテムの変更
BOOL CWindow::SetMenuItem(int menuid,BOOL check,BOOL gray )
{
    HMENU menu = GetMenu( hWnd );
    if( menu ) {
        MENUITEMINFO miinfo;
        ZeroMemory( &miinfo,sizeof(miinfo) );
        miinfo.cbSize = sizeof(miinfo);
        miinfo.fMask = MIIM_STATE;
        if( check )
            miinfo.fState |= MFS_CHECKED;
        else
            miinfo.fState |= MFS_UNCHECKED;
        if( gray )
            miinfo.fState |= MFS_GRAYED;
        else
            miinfo.fState |= MFS_ENABLED;
        return SetMenuItemInfo( menu,menuid,FALSE,&miinfo );
    }
    return TRUE;
}
 
BOOL CWindow::TextOutW( int x,int y,const WCHAR *str,COLORREF col )
{
    HDC hdc = GetDC( hWnd );
    if( hdc ) {
        SetTextColor( hdc,col );
        ::TextOutW( hdc,x,y,str,wcslen(str) );
        ReleaseDC( hWnd,hdc );
    }
    return TRUE;
}
 
///////////////////////////////////////////////////////////////////////////////////
// メニューバーの変更(Createの前に必要)
///////////////////////////////////////////////////////////////////////////////////
void CWindow::SetMenu( HMENU menu )
{
    hMenu = menu;
}

Name: Math
[URL]
Date: 2017年1月07日(土) 08:00
No: 14
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

[DEBUG.cpp]
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#pragma warning( disable : 4996 )
#include "DEBUG.H"
#include <stdio.h>
 
 
// 同期オブジェクト
class CDbgCritialSection {
    CRITICAL_SECTION mCS;
public:
    CDbgCritialSection() {
        InitializeCriticalSection( &mCS );
    }
    virtual ~CDbgCritialSection() {
        DeleteCriticalSection( &mCS );
    }
    void Enter( void ) {
        EnterCriticalSection( &mCS );
    }
    void Leave( void ) {
        LeaveCriticalSection( &mCS );
    }
};
 
 
 
static CDbgCritialSection   cs;
static BOOL                 bInit = FALSE;
static char                 mFileName[MAX_PATH] = "";
 
 
 
 
// 現在の日時を文字列で返す(同期必須)
const char *GetNow( void )
{
    static char mNowStr[256] = "";
    SYSTEMTIME  st;
    GetLocalTime( &st );
    sprintf( mNowStr,"%04d/%02d/%02d %02d:%02d:%02d.%03d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond,st.wMilliseconds );
    return mNowStr;
}
 
 
void dbgInitA( const char *file )
{
    cs.Enter();
    if( !bInit ) {
        // 未初期化なら初期化
        strcpy( mFileName,file );
        DeleteFileA( mFileName );
        bInit = TRUE;
    }
    cs.Leave();
}
 
void dbgDebugA( const char *s,... )
{
    cs.Enter();
 
    if( bInit ) {
        char *str = NULL;
        va_list ap;
        va_start( ap,s );
 
        int len = _vscprintf( s,ap );
        str = (CHAR*)malloc( sizeof(CHAR)*(len+1) );
        if( str ) {
            vsprintf( str,s,ap );
            // ファイル書き出し
            FILE *fp;
            fp = fopen( mFileName,"a" );
            if( fp ) {
                fprintf( fp,"[%s] %s",GetNow(),str );
                fclose( fp );
            }
            free( str );
        }
        va_end( ap );
    }
 
    cs.Leave();
}
 
 
void dbgClear( void )
{
    cs.Enter();
 
    if( bInit ) {
        char s[1024];
        ZeroMemory( s,sizeof(s) );
        sprintf( s,"[%s] << Log Clear >>\n",GetNow() );
 
        // ファイル書き出し
        FILE *fp;
        fp = fopen( mFileName,"a" );
        if( fp ) {
            fputs( s,fp );
            fclose( fp );
        }
    }
 
    cs.Leave();
}
 
void dbgOutputDebugStringA( const char *s,... )
{
    cs.Enter();
 
    va_list ap;
    va_start( ap,s );
 
    int len = _vscprintf( s,ap );
    char *str = (CHAR*)malloc( sizeof(char)*(len+1) );
    if( str ) {
        vsprintf( str,s,ap );
        size_t len2 = strlen(str);
        if( len2>=DEBUG_MAXTEXTBUF ) {
            str[DEBUG_MAXTEXTBUF-4] = NULL;
            strcat( str,"...\n" );
        }
        OutputDebugStringA( str );
        free( str );
    }
    va_end( ap );
 
    cs.Leave();
}

Name: Math
[URL]
Date: 2017年1月07日(土) 08:04
No: 15
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

[CWav.h]
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#ifndef _CWAV_H
#define _CWAV_H
///////////////////////////////////////////////////////////////////////////////////
// CWav : WAV管理クラス v1.01                                                    //
//                                                                               //
// このソースコードは自由に改変して使用可能です。                                //
// また商用利用も可能ですが、すべての環境で正しく動作する保障はありません。      //
//                          <!-- m --><a class=\"postlink\" href=\"http://www.charatsoft.com/\">http://www.charatsoft.com/</a><!-- m -->                           //
///////////////////////////////////////////////////////////////////////////////////
#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>
 
// 読み込みモード
#define WAVMODE_STATIC      0x00000000          // メモリ上にすべてのWAVをロードする(スタティック)
#define WAVMODE_STREAM      0x00000001          // ファイルから任意のサイズを読み取って使用する(ストリーム)
 
// WAVヘッダ
typedef struct _WAVHEADER {
    char    mRiff[4];               // 'RIFF'
    DWORD   dwFileSize;             // ファイルサイズ
    char    mWave[4];               // 'WAVE'
    char    mFmt[4];                // 'fmt '
    DWORD   dwChuncSize;            // チャンクサイズ(PCMなら16)
    WORD    wFormatID;              // フォーマットID(PCMなら1)
    WORD    wChannel;               // チャンネル数
    DWORD   dwSample;               // サンプリングレート
    DWORD   dwBytePerSec;           // 秒間のバイト数(wChannel*dwSample*wBitrate )
    WORD    wBlockSize;             // 1つのWAVデータサイズ(wChannel*wBitrate)
    WORD    wBitrate;               // サンプリングビット値(8bit/16bit)
} WAVHEADER,*LPWAVHEADER;
 
// チャンク構造体
typedef struct _DATACHUNC {
    char    mData[4];               // 'data'
    DWORD   dwSize;                 // データサイズ
} DATACHUNC,*LPDATACHUNC;
 
 
 
class CWav {
    DWORD       dwMode;             // ストリームか
    WAVHEADER   mHead;              // WAVヘッダ
    DATACHUNC   mData;              // DATAチャンク
    LPBYTE      mBuf;               // データ部
    int         iBuf;               // データ数
    int         iStreamPoint;       // ストリーム時のデータポインタ
    char        mFile[MAX_PATH];    // ファイル名
    FILE        *fp;
public:
    CWav();
    virtual ~CWav();
    BOOL Create( const char *file,int ch=2,int bit=16,int samp=44100 );     // WAVデータの新規定義
    BOOL Write( const LPVOID buf,int size );                                // ファイルへの書き込み
    BOOL Close( void );                                                     // ファイルを閉じる
    BOOL Load( const char *file,DWORD mode=WAVMODE_STATIC );                // 読み込みモードを指定してファイルをオープン
    BOOL Read( char *buf,int *size );                                       // ストリームモードでのロード
public:
    inline const LPBYTE GetBuffer( void ) { return mBuf; }
    inline int GetBufferSize( void ) { return iBuf; }
    inline int GetChannel( void ) { return (int)mHead.wChannel; }
    inline int GetSampleRate( void ) { return (int)mHead.dwSample; }
    inline int GetBitrate( void ) { return (int)mHead.wBitrate; }
};
 
#endif

Name: Math
[URL]
Date: 2017年1月07日(土) 08:06
No: 16
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

[CWindow.h]
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#ifndef __CWINDOW_H
#define __CWINDOW_H
///////////////////////////////////////////////////////////////////////////////////
// CWindow : ウィンドウ管理 v5.00(UNICODE)                                       //
//                                                                               //
// このソースコードは自由に改変して使用可能です。                                //
// また商用利用も可能ですが、すべての環境で正しく動作する保障はありません。      //
//                          <!-- m --><a class=\"postlink\" href=\"http://www.charatsoft.com/\">http://www.charatsoft.com/</a><!-- m -->                           //
///////////////////////////////////////////////////////////////////////////////////
#include <WinSock2.h>
#include <Windows.h>
 
#pragma comment(lib,"imm32.lib")
 
 
///////////////////////////////////////////////////////////////////////////////////
// コールバック関数定義
///////////////////////////////////////////////////////////////////////////////////
typedef LRESULT (CALLBACK *ONCOMMAND)( HWND hWnd,WPARAM wParam,LPARAM lParam );
typedef struct _ONMSG {
    UINT        uiMsg;                                  // メッセージ番号
    ONCOMMAND   cmdProc;                                // コールバックアドレス
} ONMSG,*LPONMSG;
 
 
///////////////////////////////////////////////////////////////////////////////////
// ウインドウクラス
///////////////////////////////////////////////////////////////////////////////////
class CWindow {
 
public:
    // 使いやすいように外へ出す
    static HINSTANCE    hInstance;                      // メインウインドウのインスタンス
    static HWND         hWnd;                           // メインウインドウのハンドル
    static BOOL         bActive;                        // アクティブか
 
protected:
    // ウインドウ関係
    static WCHAR        mName[256];                     // アプリケーション名(クラス名としても使用)
    static const WCHAR  *cIconID;                       // アイコンの種類
    static HMENU        hMenu;                          // メニューハンドル
    static DWORD        dwStyle;                        // ウインドウスタイル
    static DWORD        dwExStyle;                      // 拡張スタイル
    static LPONMSG      mMsg;                           // 追加メッセージデータ
    static int          iMsg;                           // 追加メッセージの数
 
private:
    // ウインドウメッセージ処理コールバック
    static LRESULT CALLBACK WindowProc( HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam );
 
public:
    CWindow();
    virtual ~CWindow();
    void SetIcon( const WCHAR *icon );                                                                              // アイコンの変更(Createの前に必要)
    BOOL SetWindowStyle( DWORD style );                                                                             // ウインドウのスタイルの変更(動的に変更も可能)
    void SetMenu( HMENU menu );
    BOOL Create( HINSTANCE hInst,const WCHAR *appName,BOOL show=TRUE,DWORD w=640,DWORD h=480,HWND parent=NULL );    // 内部で管理するウインドウを生成する
    void Delete( void );                                                                                            // 明示的にウィンドウを削除する
 
    BOOL AddMsgProc( UINT msg,ONCOMMAND proc );                                                                     // 特殊メッセージの追加
    void ShowCursor( BOOL bShow );                                                                                  // マウスカーソルの表示、非表示
    void ShowWindow( BOOL bShow );                                                                                  // ウインドウの表示、非表示
    void Move( int x,int y );                                                                                       // ウインドウの移動
    void Move( int x,int y,int w,int h );                                                                           // ウインドウの移動(幅と高さも同時に変更)
 
    BOOL MoveClientWindowCenter( int w,int h );                                                                     // 指定サイズがクライアント領域になるようにウインドウを配置
    BOOL SetMenuItem( int menuid,BOOL check=FALSE,BOOL gray=FALSE );                                                // メニューアイテムの状態変更
 
    BOOL TextOutW( int x,int y,const WCHAR *str,COLORREF col=0 );                                                   // 現在のフォントでテキスト表示
};
 
#endif

Name: Math
[URL]
Date: 2017年1月07日(土) 08:12
No: 17
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

[DEBUG.h]
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#ifndef __DEBUG_H
#define __DEBUG_H
///////////////////////////////////////////////////////////////////////////////////
// DEBUG : デバッグ出力ライブラリ v2.00                                          //
//                                                                               //
// このソースコードは自由に改変して使用可能です。                                //
// また商用利用も可能ですが、すべての環境で正しく動作する保障はありません。      //
//                          <!-- m --><a class=\"postlink\" href=\"http://www.charatsoft.com/\">http://www.charatsoft.com/</a><!-- m -->                           //
///////////////////////////////////////////////////////////////////////////////////
#include <WinSock2.h>
#include <Windows.h>
 
//#undef DEBUGMODE                      // コメントをはずすと全ての出力をキャンセル
 
#define DEBUG_MAXTEXTBUF        2048    // ODSX()で出力される最大サイズ(これ以上は"..."となって切り捨て)
 
 
#undef DEBUG
 
#ifdef DEBUGMODE
#define INITDEBUG           dbgInitA
#define DEBUG(x,...)        dbgDebugA(x,__VA_ARGS__)
#define ODS(x,...)          dbgOutputDebugStringA(x,__VA_ARGS__)
#define CLEARDEBUG          dbgClear()
#else
#define INITDEBUG
#define DEBUG(x,...)
#define ODS(x,...)
#define CLEARDEBUG
#endif
 
 
 
#ifdef __cplusplus
extern "C" {
#endif
 
// 公開関数
extern void dbgInitA( const char *file="DEBUG.TXT" );
extern void dbgDebugA( const char *s,... );
extern void dbgClear( void );
extern void dbgOutputDebugStringA( const char *s,... );
 
#ifdef __cplusplus
}
#endif
 
#endif

Name: Math
[URL]
Date: 2017年1月07日(土) 08:16
No: 18
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

[projectファイル]
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup Label="ProjectConfigurations">
    <ProjectConfiguration Include="Debug|Win32">
      <Configuration>Debug</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|Win32">
      <Configuration>Release</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
  </ItemGroup>
  <PropertyGroup Label="Globals">
    <ProjectGuid>{D95718F7-6F6D-454C-909C-9996D73412AE}</ProjectGuid>
    <Keyword>Win32Proj</Keyword>
    <RootNamespace>TestWasapi</RootNamespace>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <CharacterSet>Unicode</CharacterSet>
    <PlatformToolset>v140</PlatformToolset>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
    <PlatformToolset>v140</PlatformToolset>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <ImportGroup Label="ExtensionSettings">
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <LinkIncremental>true</LinkIncremental>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <LinkIncremental>false</LinkIncremental>
  </PropertyGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <ClCompile>
      <PrecompiledHeader>
      </PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <Optimization>Disabled</Optimization>
      <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
      <MultiProcessorCompilation>true</MultiProcessorCompilation>
      <MinimalRebuild>false</MinimalRebuild>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <OutputFile>$(TargetName)D$(TargetExt)</OutputFile>
    </Link>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <PrecompiledHeader>
      </PrecompiledHeader>
      <Optimization>MaxSpeed</Optimization>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
      <MultiProcessorCompilation>true</MultiProcessorCompilation>
    </ClCompile>
    <Link>
      <SubSystem>Windows</SubSystem>
      <GenerateDebugInformation>true</GenerateDebugInformation>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
      <OutputFile>$(TargetName)$(TargetExt)</OutputFile>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup>
    <ClCompile Include="CWav.cpp" />
    <ClCompile Include="CWindow.cpp" />
    <ClCompile Include="DEBUG.CPP" />
    <ClCompile Include="Main.cpp" />
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="CWav.h" />
    <ClInclude Include="CWindow.h" />
    <ClInclude Include="DEBUG.H" />
  </ItemGroup>
  <ItemGroup>
    <None Include="DEBUG.TXT" />
  </ItemGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
  <ImportGroup Label="ExtensionTargets">
  </ImportGroup>
</Project>

Name: Math
[URL]
Date: 2017年1月07日(土) 08:19
No: 19
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

[DEBUG.TXT]
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
[2017/01/07 07:42:02.564] << Log Clear >>
[2017/01/07 07:42:02.646] デフォルトデバイスピリオド : 100000 (10.000000ミリ秒)
[2017/01/07 07:42:02.654] 最小デバイスピリオド       : 30000 (3.000000ミリ秒)
[2017/01/07 07:42:03.133] バッファサイズアライメントエラーのため修正する
[2017/01/07 07:42:03.143] 修正後のフレーム数         : 448
[2017/01/07 07:42:03.154] 修正後のレイテンシ         : 101587 (10.158700ミリ秒)
[2017/01/07 07:42:03.177] 設定されたフレーム数       : 448
[2017/01/07 07:42:03.182] 設定されたバッファサイズ   : 1792byte
[2017/01/07 07:42:03.187] 1サンプルの時間            : 0.040635
[2017/01/07 07:42:03.194] スレッド開始
[2017/01/07 07:42:03.200] WASAPI初期化完了


-End-

Name: larkpia
[URL]
入門者(4,545 ポイント)
Date: 2017年1月07日(土) 10:00
No: 20
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

[解決!]

Mathさん
返信が遅くなりすみません。ためになるプログラムをありがとうございます。
ステレオミキサーを使用することでPC音を取得することができるようになりました。ステレオミキサーを使わない方法はまだ解決していませんが、これは今後の課題としてもっと勉強していこうと思います。
今回は拙い質問にもかかわらず、ご指導いただきありがとうございました。 次回質問版を利用する際には、目的と仕様を相手に分かりやすく説明することを心がけます。本当にありがとうございました。
最後に作成した、プログラムのコードを載せておきます。文字セットにマルチバイト文字セットを使用しています。

コード[C]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
#ifndef STRICT
    #define STRICT
#endif
 
 
#include <stdio.h>
#include <wchar.h>
#include <string.h>
#include <stdlib.h>
#include <process.h>
#include <Windows.h>
#include <Audioclient.h>
#include <audiopolicy.h>
#include <Endpointvolume.h>
#include <mmdeviceapi.h>
#include <functiondiscoverykeys_devpkey.h>
#include <strsafe.h>
#include <avrt.h>
 
#pragma comment(lib, "avrt.lib")
#pragma comment(lib, "winmm.lib")
 
#define BUTTON_START 101
 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE ,int);
void ExitWasapi();
int InitWasapi();
DWORD WINAPI WASAPICaptureThread(LPVOID Context);
int writeWaveFile(FILE *fp, WORD wBitsPerSample, LPWAVEFORMATEX lpwf);
void makeWaveForm(WAVEFORMATEX *waveForm);
void ChangeButtonState(int id, BOOL state, char *buf);
int StartRec();
int EndRec();
bool GetDeviceName(IMMDeviceCollection *DeviceCollection, UINT DeviceIndex, LPWSTR deviceName, size_t size);
 
 
char szWindowTitle[128] = "WASAPI録音";
char szClassName[128] = "wasapi_capture";
 
const WORD nBitrate = 16;
const WORD nFrequency = 44100;
const WORD nChannel = 2;
 
struct Rect : RECT
{
    LONG width() { return right - left; }
    LONG height() { return bottom - top; }
};
 
IAudioCaptureClient *CaptureClient = NULL;
IAudioClient *AudioClient = NULL;
IAudioRenderClient *RenderClient = NULL;
IMMDevice *device = NULL;
IMMDeviceCollection *deviceCollection = NULL;
IMMDeviceEnumerator *deviceEnumerator = NULL;
 
HANDLE ShutdownEvent;
HANDLE CaptureThread;
HANDLE AudioSamplesReadyEvent;
 
HWND hwnd;
Rect rcDraw;
HDC hdcDraw;
FILE *fp;
WAVEFORMATEXTENSIBLE waveFormat;
float masterVolume;
 
BOOL bRec=false;
 
CRITICAL_SECTION critical_section;
 
// リングバッファ
int ringbuf_cur = 0;
short *ringbuf;
 
template <class T> inline void SafeRelease(T *ppT)
{
    if (*ppT)
    {
        (*ppT)->Release();
        *ppT = NULL;
    }
}
 
 
// MAIN関数
int WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
    LPSTR lpsCmdLine, int CmdShow)
{
 
    MSG msg;
 
    if(!InitApp(hCurInst))
        return FALSE;
    if(!InitInstance(hCurInst, CmdShow))
        return FALSE;
 
    // COMの初期化
    HRESULT hr =CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    if(FAILED(hr)){
        MessageBox(hwnd, "COMの初期化に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    if(!InitWasapi()){
        MessageBox(hwnd, "Wasapi初期化に失敗", "err", MB_ICONINFORMATION);
        ExitWasapi();
        CoUninitialize();
        DestroyWindow(hwnd);
        return FALSE;
    }
 
    while(GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
 
    SetEvent(ShutdownEvent);
 
    hr = AudioClient->Stop();
    if (FAILED(hr))
    {
        MessageBox(hwnd, "キャプチャを終了できません", "err", MB_ICONINFORMATION);
    }
 
    WaitForSingleObject(CaptureThread, INFINITE);
    if(fp!=NULL)
        fclose(fp);
    CloseHandle(CaptureThread);
    CloseHandle(ShutdownEvent);
    CloseHandle(AudioSamplesReadyEvent);
    DeleteCriticalSection(&critical_section);
    // WASAPIの終了
    ExitWasapi();
 
    // COMの終了
    CoUninitialize();
 
}
// ウェーブフォームの作成
void makeWaveForm(WAVEFORMATEX *waveForm)
{
    waveForm->wFormatTag = WAVE_FORMAT_PCM;
    waveForm->nChannels  = nChannel;
    waveForm->nSamplesPerSec = nFrequency;
    waveForm->wBitsPerSample = nBitrate;
    waveForm->nBlockAlign   = waveForm->wBitsPerSample / 8 * waveForm->nChannels;
    waveForm->nAvgBytesPerSec =waveForm->nSamplesPerSec * waveForm->nBlockAlign;
 
}
 
/*****************************************************************/
//
//  name : writeWaveFile
//  func : ファイルに保存された波形データをwaveファイルに
//        保存しなおす
//
//  param1 : ファイルポインタ
//  param2 : WAVEHDR構造体のポインタ
//  param3 : WAVEFORMATEX構造体のポインタ
//
int writeWaveFile(FILE *fp, WORD wBitsPerSample, LPWAVEFORMATEX lpwf)
{
    const int nPCMBuffSize=256;
    LPSTR lpszFileName = TEXT("sample.wav");
    HMMIO    hmmio;
    MMCKINFO mmckRiff;
    MMCKINFO mmckFmt;
    MMCKINFO mmckData;
    int buffer[nPCMBuffSize];
   
    hmmio = mmioOpen(lpszFileName, NULL, MMIO_CREATE | MMIO_WRITE);
    if (hmmio == NULL)
        return -1;
 
    mmckRiff.fccType = mmioStringToFOURCC(TEXT("WAVE"), 0);
    mmioCreateChunk(hmmio, &mmckRiff, MMIO_CREATERIFF);
 
    mmckFmt.ckid = mmioStringToFOURCC(TEXT("fmt "), 0);
    mmioCreateChunk(hmmio, &mmckFmt, 0);
    mmioWrite(hmmio, (char *)lpwf, sizeof(PCMWAVEFORMAT));
    mmioAscend(hmmio, &mmckFmt, 0);
 
    mmckData.ckid = mmioStringToFOURCC(TEXT("data"), 0);
    mmioCreateChunk(hmmio, &mmckData, 0);
 
 
    // 波形データを保存するバッファが大きすぎないように
    // nPCMBuffSizeずつ保存していく
    // freadはファイルの末尾まで読み込むと読み込んだデータの個数を返す
    fseek(fp, 0, SEEK_SET);
    unsigned int nBuff=0;
    do{
        //memset(buffer, 0, nPCMBuffSize);
        nBuff = fread(buffer, (wBitsPerSample/8), nPCMBuffSize, fp);
        mmioWrite(hmmio, (char*)buffer, nBuff*(wBitsPerSample/8));
    }while(nBuff == nPCMBuffSize);
 
    mmioAscend(hmmio, &mmckData, 0);
 
    mmioAscend(hmmio, &mmckRiff, 0);
    mmioClose(hmmio, 0);
 
    return 0;
}
 
//ウィンドウ・クラスの登録
ATOM InitApp(HINSTANCE hInst)
{
    WNDCLASSEX wc;
   
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = (LPCSTR)szClassName;
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
   
    return (RegisterClassEx(&wc));
 
}
 
//ウィンドウの生成
BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
 
    hwnd = CreateWindow(szClassName,
            szWindowTitle, //タイトルバーにこの名前が表示されます
            WS_OVERLAPPEDWINDOW, //ウィンドウの種類
            CW_USEDEFAULT,    //X座標
            CW_USEDEFAULT,    //Y座標
            CW_USEDEFAULT,    //幅
            CW_USEDEFAULT,    //高さ
            NULL, //親ウィンドウのハンドル、親を作るときはNULL
            NULL, //メニューハンドル、クラスメニューを使うときはNULL
            hInst, //インスタンスハンドル
            NULL);
    if (!hwnd)
        return FALSE;
 
    CreateWindow("BUTTON", "開始", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
                100, 100, 100, 50, hwnd, (HMENU)BUTTON_START, hInst, NULL);
 
 
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    return TRUE;
}
 
LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
    HDC hdc;
    PAINTSTRUCT ps;
 
 
    switch (msg) {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
 
    case WM_CREATE:
        return 0;
 
    case WM_COMMAND:
        switch(LOWORD(wp)){
        case BUTTON_START:
            if(!bRec){
                if(!StartRec()){
                    break;
                }
                ChangeButtonState(BUTTON_START, true, "停止");
            } else {
                if(!EndRec()){
                    break;
                }
                ChangeButtonState(BUTTON_START, true, "開始");
            }
            bRec = !bRec;
            break;
       
        }
        return 0;
 
    case WM_KEYDOWN:
        return 0;
 
    case WM_MOUSEMOVE:
        return 0;
 
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        EndPaint(hwnd, &ps);
        return 0;
    }
    return DefWindowProc(hwnd , msg , wp , lp);
}
 
void ChangeButtonState(int id, BOOL state, char *buf)
{
    HWND hButton;
 
    hButton = GetDlgItem(hwnd, id);
    EnableWindow(hButton, state);
    SetWindowText(hButton, buf);
 
    return ;
 
}
 
DWORD WINAPI WASAPICaptureThread(LPVOID Context)
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    if(FAILED(hr)){
        MessageBox(hwnd, "COM初期化に失敗", "err_thread", MB_ICONINFORMATION);
        return hr;
    }
 
    //////////////////////
    // スレッドの優先順位
    DWORD mmcsTaskIndex = 0;
    HANDLE mmcsHandle = AvSetMmThreadCharacteristics("Audio", &mmcsTaskIndex);
    if(mmcsHandle == NULL){
        MessageBox(hwnd, "スレッドの優先順位変更に失敗", "err_thread", MB_ICONINFORMATION);
    }
 
    HANDLE waitArray[2] = {ShutdownEvent, AudioSamplesReadyEvent};
 
    bool stillPlaying = true;
    while(stillPlaying){
 
        DWORD waitResult = WaitForMultipleObjects(2, waitArray, FALSE, INFINITE);
        switch(waitResult)
        {
        case WAIT_OBJECT_0 + 0: // ShutdownEvent
            stillPlaying = false;
 
        case WAIT_OBJECT_0 + 1: // AudioSamplesReadyEvent
            DWORD *pData;
            DWORD *zeroData;
            ULONGLONG adjsData;
            UINT32 framesAvailable;
            DWORD  flags;
       
            hr = CaptureClient->GetBuffer((BYTE **)&pData, &framesAvailable, &flags, NULL, NULL);
            if(SUCCEEDED(hr))
            {
                EnterCriticalSection(&critical_section);
                if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
                {
                    zeroData = (DWORD *)calloc(framesAvailable, sizeof(DWORD));
                    fwrite(zeroData, sizeof(DWORD),framesAvailable, fp);
                    free(zeroData);
                }
                else
                {
                    fwrite(pData, sizeof(DWORD),framesAvailable,fp);
                }
 
                LeaveCriticalSection(&critical_section);
 
                hr = CaptureClient->ReleaseBuffer(framesAvailable);
                if(FAILED(hr)){
                    MessageBox(hwnd, "リリースに失敗", "err_thread", MB_ICONINFORMATION);
                }
            }
            break;
        }
   
    }
 
    return 0;
}
 
int InitWasapi()
{
    HRESULT hr;
 
    /////////////////////
    // デバイスの選択
    hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&deviceEnumerator));
    if(FAILED(hr)){
        MessageBox(hwnd, "インスタンスの作成に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    hr = deviceEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &deviceCollection);
    if(FAILED(hr)){
        MessageBox(hwnd, "デバイスの取得に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
 
    UINT deviceCount;
    UINT deviceNum;
    wchar_t deviceName[128];
 
    hr = deviceCollection->GetCount(&deviceCount);
    for(deviceNum=0; deviceNum<deviceCount; deviceNum++){
        GetDeviceName(deviceCollection, deviceNum, deviceName, sizeof(deviceName));
        if(wcscmp(L"ステレオ ミキサー (Realtek High Definition Audio)", deviceName)==0)
            break;
    }
    if(deviceNum == deviceCount){
        MessageBox(hwnd, "ステレオミキサーが使用できません", "err", MB_ICONINFORMATION);
        return FALSE;
    }
    hr = deviceCollection->Item(deviceNum, &device);
    if(FAILED(hr)){
        MessageBox(hwnd, "ステレオミキサーが使用できません", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    /////////////////////
    // イベントの作成
    ShutdownEvent = CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
    if(ShutdownEvent == NULL){
        MessageBox(hwnd, "イベントの作成に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    AudioSamplesReadyEvent = CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
    if(AudioSamplesReadyEvent == NULL){
        MessageBox(hwnd, "イベントの作成に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    /////////////////////
    // AudioClient の準備
    hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, reinterpret_cast<void **>(&AudioClient));
    if(FAILED(hr)){
        MessageBox(hwnd, "AudioClientの準備に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    //////////////////////
    // マイクの音量設定
    IAudioEndpointVolume *MicVolume;
    hr = device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, reinterpret_cast<void **>(&MicVolume));
    if(FAILED(hr)){
        MessageBox(hwnd, "sesseionManagerの準備に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    MicVolume->SetMasterVolumeLevelScalar(static_cast<float>(100) / 100, nullptr);
    SafeRelease(&MicVolume);
 
 
    /////////////////////
    // デバイスのレイテンシの取得
    REFERENCE_TIME DefaultDevicePeriod;
    REFERENCE_TIME MinimumDevicePeriod;
    hr = AudioClient->GetDevicePeriod(&DefaultDevicePeriod, &MinimumDevicePeriod);
 
    /////////////////////
    // waveフォームの作成
    //WAVEFORMATEXTENSIBLE waveFormat;
    waveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
    waveFormat.Format.nChannels = nChannel;
    waveFormat.Format.nSamplesPerSec = nFrequency;
    waveFormat.Format.wBitsPerSample = nBitrate;
    waveFormat.Format.nBlockAlign = waveFormat.Format.wBitsPerSample/8 * waveFormat.Format.nChannels;
    waveFormat.Format.nAvgBytesPerSec = waveFormat.Format.nSamplesPerSec * waveFormat.Format.nBlockAlign;
    waveFormat.Format.cbSize = 22;
    waveFormat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
    waveFormat.Samples.wValidBitsPerSample = waveFormat.Format.wBitsPerSample;
    waveFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
 
    //////////////////////
    // AudioClientの初期化
    hr = AudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE,
            AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
            DefaultDevicePeriod, DefaultDevicePeriod, (WAVEFORMATEX *)&waveFormat, NULL);
    UINT32 frame;
    if(FAILED(hr)){
        if(hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED){
            hr = AudioClient->GetBufferSize(&frame);
            DefaultDevicePeriod = (REFERENCE_TIME)(10000.0*1000*frame/waveFormat.Format.nSamplesPerSec+0.5);
            SafeRelease(&AudioClient);
            hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, reinterpret_cast<void **>(&AudioClient));
            if(FAILED(hr)){
                MessageBox(hwnd, "AudioClientの2度目の準備に失敗", "err", MB_ICONINFORMATION);
                return FALSE;
            }
            hr = AudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE,
                    AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
                    DefaultDevicePeriod, DefaultDevicePeriod, (WAVEFORMATEX *)&waveFormat, NULL);
        }
 
        if(FAILED(hr)){
            MessageBox(hwnd, "AudioClientの初期化に失敗", "err", MB_ICONINFORMATION);
            return FALSE;
        }
    }
 
    //////////////////////
    // イベントをセット
    hr = AudioClient->SetEventHandle(AudioSamplesReadyEvent);
    if(FAILED(hr)){
        MessageBox(hwnd, "イベントのセットに失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    hr = AudioClient->GetService(IID_PPV_ARGS(&CaptureClient));
    if(FAILED(hr)){
        MessageBox(hwnd, "サービスの取得に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    /////////////////////
    // クリティカルセクション初期化
    InitializeCriticalSection(&critical_section);
   
    return TRUE;
}
 
int StartRec()
{
    HRESULT hr;
 
    /////////////////////
    // file オープン
    fp = fopen("sample.data", "w+b");
    if(fp==NULL){
        MessageBox(hwnd, "ファイルのオープンに失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    /////////////////////
    // スレッドの作成
    CaptureThread = CreateThread( NULL, 0, WASAPICaptureThread, NULL, 0, NULL);
    if(CaptureThread == NULL){
        MessageBox(hwnd, "スレッドの作成に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    /////////////////////
    // キャプチャの開始
    hr = AudioClient->Start();
    if(FAILED(hr)){
        MessageBox(hwnd, "キャプチャを開始できません", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    return TRUE;
}
 
int EndRec()
{
    HRESULT hr;
    WAVEFORMATEX mixformat;
 
    makeWaveForm(&mixformat);
    writeWaveFile(fp, waveFormat.Format.wBitsPerSample, &mixformat);
    fclose(fp);
    SetEvent(ShutdownEvent);
 
    hr = AudioClient->Stop();
    if (FAILED(hr))
    {
        MessageBox(hwnd, "キャプチャを終了できません", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
 
    return TRUE;
}
 
void ExitWasapi()
{
    SafeRelease(&CaptureClient);
    SafeRelease(&AudioClient);
    SafeRelease(&device);
    SafeRelease(&deviceCollection);
    SafeRelease(&deviceEnumerator);
 
}
 
//
//  Retrieves the device friendly name for a particular device in a device collection.  
//
//  The returned string was allocated using malloc() so it should be freed using free();
//
bool GetDeviceName(IMMDeviceCollection *DeviceCollection, UINT DeviceIndex, LPWSTR deviceName, size_t size)
{
    IMMDevice *device;
    HRESULT hr;
 
    hr = DeviceCollection->Item(DeviceIndex, &device);
    if (FAILED(hr))
    {
        MessageBox(hwnd, "デバイスを取得できません", "err", MB_ICONINFORMATION);
        return false;
    }
 
    IPropertyStore *propertyStore;
    hr = device->OpenPropertyStore(STGM_READ, &propertyStore);
    SafeRelease(&device);
    if (FAILED(hr))
    {
        MessageBox(hwnd, "デバイスのプロパティを開けません", "err", MB_ICONINFORMATION);
        return false;
    }
 
    PROPVARIANT friendlyName;
    PropVariantInit(&friendlyName);
    hr = propertyStore->GetValue(PKEY_Device_FriendlyName, &friendlyName);
    SafeRelease(&propertyStore);
 
    if (FAILED(hr))
    {
        MessageBox(hwnd, "デバイス名を取得できません", "err", MB_ICONINFORMATION);
        return false;
    }
 
    if(friendlyName.vt != VT_LPWSTR){
        swprintf(deviceName, size, L"%s", "Unknown");
    } else {
        swprintf(deviceName, size, L"%s", friendlyName.pwszVal);
    }
 
    PropVariantClear(&friendlyName);
 
    return true;
}

Name: ISLe
[URL]
ハッカー(264,574 ポイント)
Date: 2017年1月07日(土) 18:14
No: 21
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

Offtopic :
AudacityでWASAPIを使うとき、再生デバイス名の後ろにloopbackとあります。
『WASAPI loopback』で検索するとMSDNの記事(英語)やMicrosoft開発者ブログ記事(英語、サンプルコードあり)などが見付かりました。
WASAPIには再生(レンダ)デバイスに対するループバックモードという機能があるそうです。
再生したそのままを取り込めるなら、ステレオミキサーでは避けられない劣化も回避できるのですが、はたして…

Name: larkpia
[URL]
入門者(4,545 ポイント)
Date: 2017年1月07日(土) 18:18
No: 22
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

ISLeさん
情報ありがとうございます! audacityのソースを見つけたので、それと合わせて勉強してみます。

Name: ISLe
[URL]
ハッカー(264,574 ポイント)
Date: 2017年1月08日(日) 00:38
No: 23
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

Offtopic :
せっかくなのでMicrosoft開発者ブログの記事へのリンクをはっておきます。
この記事では再生デバイスをループバックするシンプルなコマンドプログラムを公開しています。
・ブログ記事
Sample – WASAPI loopback capture (record what you hear) – Matthew van Eerde's web log
・ソースファイル
blog/loopback-capture at master · mvaneerde/blog · GitHub

IAudioClientの初期化時にAUDCLNT_STREAMFLAGS_LOOPBACKフラグを指定するところがキモでしょうか。

Name: larkpia
[URL]
入門者(4,545 ポイント)
Date: 2017年1月08日(日) 12:27
No: 24
(OFFLINE)

 Re: WASAPIを使ったPC音録音ソフト

[解決!]

ISLeさん

お陰様で、ループバックを使用しサウンドミキサーを通さないアプリを作ることができました。
主な変更点としては、

・IMMDeviceEnumerator::GetDefaultAudioEndpointで第1引数をeCaptureからeRenderに変更
・IAudioClient::Initializeで第1引数をAUDCLNT_SHAREMODE_SHAREDに、第2引数をAUDCLNT_STREAMFLAGS_LOOPBACKに変更
・イベント駆動からタイマー駆動へ変更

の3点でした。

皆様のおかげで疑問を完全に解消することができました。本当にありがとうございました。
最後にループバックを使用したコードも載せておきます。

[コード]
マルチ文字セットを使用しています。スピーカーは16bit,44100Hz,2channelのものに対応しています。
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
#ifndef STRICT
    #define STRICT
#endif
 
 
#include <stdio.h>
#include <wchar.h>
#include <string.h>
#include <stdlib.h>
#include <process.h>
#include <Windows.h>
#include <Audioclient.h>
#include <audiopolicy.h>
#include <Endpointvolume.h>
#include <mmdeviceapi.h>
#include <functiondiscoverykeys_devpkey.h>
#include <strsafe.h>
#include <avrt.h>
 
#pragma comment(lib, "avrt.lib")
#pragma comment(lib, "winmm.lib")
 
#define BUTTON_START 101
 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE ,int);
void ExitWasapi();
int InitWasapi();
DWORD WINAPI WASAPICaptureThread(LPVOID Context);
int writeWaveFile(FILE *fp, WORD wBitsPerSample, LPWAVEFORMATEX lpwf);
void makeWaveForm(WAVEFORMATEX *waveForm);
void ChangeButtonState(int id, BOOL state, char *buf);
int StartRec();
int EndRec();
bool GetDeviceName(IMMDeviceCollection *DeviceCollection, UINT DeviceIndex, LPWSTR deviceName, size_t size);
void ShowError(HRESULT hr, HWND hwnd);
 
 
char szWindowTitle[128] = "WASAPI_LOOPBACK";
char szClassName[128] = "wasapi_capture";
 
const WORD nBitrate = 16;
const WORD nFrequency = 44100;
const WORD nChannel = 2;
 
struct Rect : RECT
{
    LONG width() { return right - left; }
    LONG height() { return bottom - top; }
};
 
IAudioCaptureClient *CaptureClient = NULL;
IAudioClient *AudioClient = NULL;
IAudioRenderClient *RenderClient = NULL;
IMMDevice *device = NULL;
IMMDeviceCollection *deviceCollection = NULL;
IMMDeviceEnumerator *deviceEnumerator = NULL;
 
HANDLE ShutdownEvent;
HANDLE CaptureThread;
HANDLE AudioSamplesReadyEvent;
 
HWND hwnd;
Rect rcDraw;
HDC hdcDraw;
FILE *fp;
WAVEFORMATEXTENSIBLE waveFormat;
float masterVolume;
REFERENCE_TIME DefaultDevicePeriod;
REFERENCE_TIME MinimumDevicePeriod;
 
BOOL bRec=false;
 
CRITICAL_SECTION critical_section;
 
// リングバッファ
int ringbuf_cur = 0;
short *ringbuf;
 
template <class T> inline void SafeRelease(T *ppT)
{
    if (*ppT)
    {
        (*ppT)->Release();
        *ppT = NULL;
    }
}
 
 
// MAIN関数
int WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
    LPSTR lpsCmdLine, int CmdShow)
{
 
    MSG msg;
 
    if(!InitApp(hCurInst))
        return FALSE;
    if(!InitInstance(hCurInst, CmdShow))
        return FALSE;
 
    // COMの初期化
    HRESULT hr =CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    if(FAILED(hr)){
        MessageBox(hwnd, "COMの初期化に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    if(!InitWasapi()){
        MessageBox(hwnd, "Wasapi初期化に失敗", "err", MB_ICONINFORMATION);
        ExitWasapi();
        CoUninitialize();
        DestroyWindow(hwnd);
        return FALSE;
    }
 
    while(GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
 
    SetEvent(ShutdownEvent);
 
    hr = AudioClient->Stop();
    if (FAILED(hr))
    {
        MessageBox(hwnd, "キャプチャを終了できません", "err", MB_ICONINFORMATION);
    }
 
    WaitForSingleObject(CaptureThread, INFINITE);
    if(fp!=NULL)
        fclose(fp);
    CloseHandle(CaptureThread);
    CloseHandle(ShutdownEvent);
    CloseHandle(AudioSamplesReadyEvent);
    DeleteCriticalSection(&critical_section);
    // WASAPIの終了
    ExitWasapi();
 
    // COMの終了
    CoUninitialize();
 
}
// ウェーブフォームの作成
void makeWaveForm(WAVEFORMATEX *waveForm)
{
    waveForm->wFormatTag = WAVE_FORMAT_PCM;
    waveForm->nChannels  = nChannel;
    waveForm->nSamplesPerSec = nFrequency;
    waveForm->wBitsPerSample = nBitrate;
    waveForm->nBlockAlign   = waveForm->wBitsPerSample / 8 * waveForm->nChannels;
    waveForm->nAvgBytesPerSec =waveForm->nSamplesPerSec * waveForm->nBlockAlign;
 
}
 
/*****************************************************************/
//
//  name : writeWaveFile
//  func : ファイルに保存された波形データをwaveファイルに
//        保存しなおす
//
//  param1 : ファイルポインタ
//  param2 : WAVEHDR構造体のポインタ
//  param3 : WAVEFORMATEX構造体のポインタ
//
int writeWaveFile(FILE *fp, WORD wBitsPerSample, LPWAVEFORMATEX lpwf)
{
    const int nPCMBuffSize=256;
    LPSTR lpszFileName = TEXT("sample.wav");
    HMMIO    hmmio;
    MMCKINFO mmckRiff;
    MMCKINFO mmckFmt;
    MMCKINFO mmckData;
    int buffer[nPCMBuffSize];
   
    hmmio = mmioOpen(lpszFileName, NULL, MMIO_CREATE | MMIO_WRITE);
    if (hmmio == NULL)
        return -1;
 
    mmckRiff.fccType = mmioStringToFOURCC(TEXT("WAVE"), 0);
    mmioCreateChunk(hmmio, &mmckRiff, MMIO_CREATERIFF);
 
    mmckFmt.ckid = mmioStringToFOURCC(TEXT("fmt "), 0);
    mmioCreateChunk(hmmio, &mmckFmt, 0);
    mmioWrite(hmmio, (char *)lpwf, sizeof(PCMWAVEFORMAT));
    mmioAscend(hmmio, &mmckFmt, 0);
 
    mmckData.ckid = mmioStringToFOURCC(TEXT("data"), 0);
    mmioCreateChunk(hmmio, &mmckData, 0);
 
 
    // 波形データを保存するバッファが大きすぎないように
    // nPCMBuffSizeずつ保存していく
    // freadはファイルの末尾まで読み込むと読み込んだデータの個数を返す
    fseek(fp, 0, SEEK_SET);
    unsigned int nBuff=0;
    do{
        //memset(buffer, 0, nPCMBuffSize);
        nBuff = fread(buffer, (wBitsPerSample/8), nPCMBuffSize, fp);
        mmioWrite(hmmio, (char*)buffer, nBuff*(wBitsPerSample/8));
    }while(nBuff == nPCMBuffSize);
 
    mmioAscend(hmmio, &mmckData, 0);
 
    mmioAscend(hmmio, &mmckRiff, 0);
    mmioClose(hmmio, 0);
 
    return 0;
}
 
//ウィンドウ・クラスの登録
ATOM InitApp(HINSTANCE hInst)
{
    WNDCLASSEX wc;
   
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = (LPCSTR)szClassName;
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
   
    return (RegisterClassEx(&wc));
 
}
 
//ウィンドウの生成
BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
 
    hwnd = CreateWindow(szClassName,
            szWindowTitle, //タイトルバーにこの名前が表示されます
            WS_OVERLAPPEDWINDOW, //ウィンドウの種類
            CW_USEDEFAULT,    //X座標
            CW_USEDEFAULT,    //Y座標
            CW_USEDEFAULT,    //幅
            CW_USEDEFAULT,    //高さ
            NULL, //親ウィンドウのハンドル、親を作るときはNULL
            NULL, //メニューハンドル、クラスメニューを使うときはNULL
            hInst, //インスタンスハンドル
            NULL);
    if (!hwnd)
        return FALSE;
 
    CreateWindow("BUTTON", "開始", WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
                100, 100, 100, 50, hwnd, (HMENU)BUTTON_START, hInst, NULL);
 
 
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    return TRUE;
}
 
LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
    HDC hdc;
    PAINTSTRUCT ps;
 
 
    switch (msg) {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
 
    case WM_CREATE:
        return 0;
 
    case WM_COMMAND:
        switch(LOWORD(wp)){
        case BUTTON_START:
            if(!bRec){
                if(!StartRec()){
                    break;
                }
                ChangeButtonState(BUTTON_START, true, "停止");
            } else {
                if(!EndRec()){
                    break;
                }
                ChangeButtonState(BUTTON_START, true, "開始");
            }
            bRec = !bRec;
            break;
       
        }
        return 0;
 
    case WM_KEYDOWN:
        return 0;
 
    case WM_MOUSEMOVE:
        return 0;
 
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        EndPaint(hwnd, &ps);
        return 0;
    }
    return DefWindowProc(hwnd , msg , wp , lp);
}
 
void ChangeButtonState(int id, BOOL state, char *buf)
{
    HWND hButton;
 
    hButton = GetDlgItem(hwnd, id);
    EnableWindow(hButton, state);
    SetWindowText(hButton, buf);
 
    return ;
 
}
 
DWORD WINAPI WASAPICaptureThread(LPVOID Context)
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    if(FAILED(hr)){
        MessageBox(hwnd, "COM初期化に失敗", "err_thread", MB_ICONINFORMATION);
        return hr;
    }
 
    //////////////////////
    // スレッドの優先順位
    DWORD mmcsTaskIndex = 0;
    HANDLE mmcsHandle = AvSetMmThreadCharacteristics("Audio", &mmcsTaskIndex);
    if(mmcsHandle == NULL){
        MessageBox(hwnd, "スレッドの優先順位変更に失敗", "err_thread", MB_ICONINFORMATION);
    }
 
    HANDLE waitArray[2] = {ShutdownEvent, AudioSamplesReadyEvent};
 
    bool stillPlaying = true;
    while(stillPlaying){
 
        DWORD waitResult = WaitForMultipleObjects(2, waitArray, FALSE, INFINITE);
        switch(waitResult)
        {
        case WAIT_OBJECT_0 + 0: // ShutdownEvent
            stillPlaying = false;
 
        case WAIT_OBJECT_0 + 1: // AudioSamplesReadyEvent
            DWORD *pData;
            DWORD *zeroData;
            ULONGLONG adjsData;
            UINT32 framesAvailable;
            DWORD  flags;
       
            hr = CaptureClient->GetBuffer((BYTE **)&pData, &framesAvailable, &flags, NULL, NULL);
            if(SUCCEEDED(hr))
            {
                EnterCriticalSection(&critical_section);
                if(flags & AUDCLNT_BUFFERFLAGS_SILENT)
                {
                    zeroData = (DWORD *)calloc(framesAvailable, sizeof(DWORD));
                    fwrite(zeroData, sizeof(DWORD),framesAvailable, fp);
                    free(zeroData);
                }
                else
                {
                    fwrite(pData, sizeof(DWORD),framesAvailable,fp);
                }
 
                LeaveCriticalSection(&critical_section);
 
                hr = CaptureClient->ReleaseBuffer(framesAvailable);
                if(FAILED(hr)){
                    MessageBox(hwnd, "リリースに失敗", "err_thread", MB_ICONINFORMATION);
                }
            }
            break;
        }
   
    }
 
    return 0;
}
 
int InitWasapi()
{
    HRESULT hr;
 
    /////////////////////
    // デバイスの選択
    hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&deviceEnumerator));
    if(FAILED(hr)){
        MessageBox(hwnd, "インスタンスの作成に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    hr = deviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &deviceCollection);
    if(FAILED(hr)){
        MessageBox(hwnd, "デバイスの取得に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device);
    if(FAILED(hr)){
        MessageBox(hwnd, "ステレオミキサーが使用できません", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    /////////////////////
    // イベントの作成
    ShutdownEvent = CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
    if(ShutdownEvent == NULL){
        MessageBox(hwnd, "イベントの作成に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    AudioSamplesReadyEvent = CreateWaitableTimer(NULL, FALSE, NULL);
    if(AudioSamplesReadyEvent == NULL){
        MessageBox(hwnd, "タイマーの作成に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
 
    /////////////////////
    // AudioClient の準備
    hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, reinterpret_cast<void **>(&AudioClient));
    if(FAILED(hr)){
        MessageBox(hwnd, "AudioClientの準備に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    /////////////////////
    // デバイスのレイテンシの取得
    hr = AudioClient->GetDevicePeriod(&DefaultDevicePeriod, &MinimumDevicePeriod);
 
    /////////////////////
    // waveフォームの作成
    //WAVEFORMATEXTENSIBLE waveFormat;
    waveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
    waveFormat.Format.nChannels = nChannel;
    waveFormat.Format.nSamplesPerSec = nFrequency;
    waveFormat.Format.wBitsPerSample = nBitrate;
    waveFormat.Format.nBlockAlign = waveFormat.Format.wBitsPerSample/8 * waveFormat.Format.nChannels;
    waveFormat.Format.nAvgBytesPerSec = waveFormat.Format.nSamplesPerSec * waveFormat.Format.nBlockAlign;
    waveFormat.Format.cbSize = 22;
    waveFormat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
    waveFormat.Samples.wValidBitsPerSample = waveFormat.Format.wBitsPerSample;
    waveFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
 
    //////////////////////
    // AudioClientの初期化
    hr = AudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
            AUDCLNT_STREAMFLAGS_LOOPBACK,
            0, 0, (WAVEFORMATEX *)&waveFormat, NULL);
    UINT32 frame;
    if(FAILED(hr)){
        if(hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED){
            hr = AudioClient->GetBufferSize(&frame);
            DefaultDevicePeriod = (REFERENCE_TIME)(10000.0*1000*frame/waveFormat.Format.nSamplesPerSec+0.5);
            SafeRelease(&AudioClient);
            hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, reinterpret_cast<void **>(&AudioClient));
            if(FAILED(hr)){
                MessageBox(hwnd, "AudioClientの2度目の準備に失敗", "err", MB_ICONINFORMATION);
                return FALSE;
            }
            hr = AudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
                    AUDCLNT_STREAMFLAGS_LOOPBACK,
                    DefaultDevicePeriod, DefaultDevicePeriod, (WAVEFORMATEX *)&waveFormat, NULL);
        }
 
        ShowError(hr, hwnd);
        if(FAILED(hr)){
            MessageBox(hwnd, "AudioClientの初期化に失敗", "err", MB_ICONINFORMATION);
            return FALSE;
        }
    }
 
    hr = AudioClient->GetService(IID_PPV_ARGS(&CaptureClient));
    if(FAILED(hr)){
        MessageBox(hwnd, "サービスの取得に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    /////////////////////
    // クリティカルセクション初期化
    InitializeCriticalSection(&critical_section);
   
    return TRUE;
}
 
int StartRec()
{
    HRESULT hr;
 
    /////////////////////
    // file オープン
    fp = fopen("sample.data", "w+b");
    if(fp==NULL){
        MessageBox(hwnd, "ファイルのオープンに失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    /////////////////////
    // スレッドの作成
    CaptureThread = CreateThread( NULL, 0, WASAPICaptureThread, NULL, 0, NULL);
    if(CaptureThread == NULL){
        MessageBox(hwnd, "スレッドの作成に失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    /////////////////////
    // タイマーの開始
    LARGE_INTEGER liFirstFire;
    liFirstFire.QuadPart = -DefaultDevicePeriod / 2;
    LONG lTimeBetweenFires = DefaultDevicePeriod / 2 /(1000*10);
    BOOL bOK = SetWaitableTimer(AudioSamplesReadyEvent, &liFirstFire, lTimeBetweenFires,
                                NULL, NULL, FALSE);
    if(!bOK){
        MessageBox(hwnd, "イベントのセットに失敗", "err", MB_ICONINFORMATION);
        return FALSE;
    }
    /////////////////////
    // キャプチャの開始
    hr = AudioClient->Start();
    if(FAILED(hr)){
        MessageBox(hwnd, "キャプチャを開始できません", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    return TRUE;
}
 
int EndRec()
{
    HRESULT hr;
    WAVEFORMATEX mixformat;
 
    //////////////////////////
    // タイマーの停止
    CancelWaitableTimer(AudioSamplesReadyEvent);
 
    //////////////////////////
    // 終了イベント
    SetEvent(ShutdownEvent);
    hr = AudioClient->Stop();
    if (FAILED(hr))
    {
        MessageBox(hwnd, "キャプチャを終了できません", "err", MB_ICONINFORMATION);
        return FALSE;
    }
 
    /////////////////////////
    // waveファイルの作成
    makeWaveForm(&mixformat);
    writeWaveFile(fp, waveFormat.Format.wBitsPerSample, &mixformat);
    fclose(fp);
 
    return TRUE;
}
 
void ExitWasapi()
{
    SafeRelease(&CaptureClient);
    SafeRelease(&AudioClient);
    SafeRelease(&device);
    SafeRelease(&deviceCollection);
    SafeRelease(&deviceEnumerator);
 
}
 
//
//  Retrieves the device friendly name for a particular device in a device collection.  
//
//  The returned string was allocated using malloc() so it should be freed using free();
//
bool GetDeviceName(IMMDeviceCollection *DeviceCollection, UINT DeviceIndex, LPWSTR deviceName, size_t size)
{
    IMMDevice *device;
    HRESULT hr;
 
    hr = DeviceCollection->Item(DeviceIndex, &device);
    if (FAILED(hr))
    {
        MessageBox(hwnd, "デバイスを取得できません", "err", MB_ICONINFORMATION);
        return false;
    }
 
    IPropertyStore *propertyStore;
    hr = device->OpenPropertyStore(STGM_READ, &propertyStore);
    SafeRelease(&device);
    if (FAILED(hr))
    {
        MessageBox(hwnd, "デバイスのプロパティを開けません", "err", MB_ICONINFORMATION);
        return false;
    }
 
    PROPVARIANT friendlyName;
    PropVariantInit(&friendlyName);
    hr = propertyStore->GetValue(PKEY_Device_FriendlyName, &friendlyName);
    SafeRelease(&propertyStore);
 
    if (FAILED(hr))
    {
        MessageBox(hwnd, "デバイス名を取得できません", "err", MB_ICONINFORMATION);
        return false;
    }
 
    if(friendlyName.vt != VT_LPWSTR){
        swprintf(deviceName, size, L"%s", "Unknown");
    } else {
        swprintf(deviceName, size, L"%s", friendlyName.pwszVal);
    }
 
    PropVariantClear(&friendlyName);
 
    return true;
}
 
void ShowError(HRESULT hr, HWND hwnd)
{
    char err[256];
 
    switch(hr)
    {
    case AUDCLNT_E_ALREADY_INITIALIZED:wsprintf(err, "%s","AUDCLNT_E_ALREADY_INITIALIZED\n");break;
    case AUDCLNT_E_WRONG_ENDPOINT_TYPE:wsprintf(err, "%s","AUDCLNT_E_WRONG_ENDPOINT_TYPE\n");break;
    case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED:wsprintf(err, "%s","AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED\n");break;
    case AUDCLNT_E_BUFFER_SIZE_ERROR:wsprintf(err, "%s","AUDCLNT_E_BUFFER_SIZE_ERROR\n");break;
    case AUDCLNT_E_CPUUSAGE_EXCEEDED:wsprintf(err, "%s","AUDCLNT_E_CPUUSAGE_EXCEEDED\n");break;
    case AUDCLNT_E_DEVICE_INVALIDATED:wsprintf(err, "%s","AUDCLNT_E_DEVICE_INVALIDATED\n");break;
    case AUDCLNT_E_DEVICE_IN_USE:wsprintf(err, "%s","AUDCLNT_E_DEVICE_IN_USE\n");break;
    case AUDCLNT_E_ENDPOINT_CREATE_FAILED:wsprintf(err, "%s","AUDCLNT_E_ENDPOINT_CREATE_FAILED\n");break;
    case AUDCLNT_E_INVALID_DEVICE_PERIOD:wsprintf(err, "%s","AUDCLNT_E_INVALID_DEVICE_PERIOD\n");break;
    case AUDCLNT_E_UNSUPPORTED_FORMAT:wsprintf(err, "%s","AUDCLNT_E_UNSUPPORTED_FORMAT\n");break;
    case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED:wsprintf(err, "%s","AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED\n");break;
    case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL:wsprintf(err, "%s","AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL\n");break;
    case AUDCLNT_E_SERVICE_NOT_RUNNING:wsprintf(err, "%s","AUDCLNT_E_SERVICE_NOT_RUNNING\n");break;
    case E_POINTER:wsprintf(err, "%s","E_POINTER\n");break;
    case E_INVALIDARG:wsprintf(err, "%s","E_INVALIDARG\n");break;
    case E_OUTOFMEMORY:wsprintf(err, "%s","E_OUTOFMEMORY\n");break;
    default: wsprintf(err, "s", "unknown"); break;
    }
 
    MessageBox(hwnd, err, "err", MB_ICONINFORMATION);
}


Return to C言語何でも質問掲示板

オンラインデータ

このフォーラムを閲覧中のユーザー: ハワルド & ゲスト[34人]