- PR -

C#でWin32APIを使ってPCM(WAVE)を再生するには?

投稿者投稿内容
tori31001
会議室デビュー日: 2006/03/11
投稿数: 6
投稿日時: 2006-03-11 06:46
はじめまして。
最近C#勉強し始めたばかりの初心者です。

C#でWin32APIを使い音を鳴らしたいと思って
Interop Declarations for Windows.h
を使い、色々調べて、自分なりにコードを書いてみたのですが、
コンパイルが通り実行できるものの、肝心の音が鳴りません。

粘ってみたのですが、どうしても分からないので投稿させていただきました。
どんな細かいことでもアドバイスいただけるとありがたいです。
以下に見苦しいですがコードを載せます。よろしくお願いします。

コード:
unsafe private void button1_Click(object sender, EventArgs e)
{
    //波形生成
    long t;
    short* wave_data = stackalloc short[44100];
    for (t = 0; t < 44100; t++)
    {
        //方形波生成
        if ((t % 200) < 100) *(wave_data + t) = 32767;
        else *(wave_data + t) = -32767;
    }

    //WAVEデバイス設定
    WAVEFORMATEX wf = new WAVEFORMATEX();
    wf.wFormatTag = 0x0001;     //PCM
    wf.nChannels = 1;           //モノラル
    wf.nSamplesPerSec = 44100;  //周波数
    wf.wBitsPerSample = 16;     //16bit
    wf.nBlockAlign = (ushort)(wf.nChannels * wf.wBitsPerSample / 8);
    wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
    wf.cbSize = 0;
    HWAVEOUT__ hwo = new HWAVEOUT__();
    HWAVEOUT__* hWOut = &hwo;
    windows.waveOutOpen(&hWOut, 0x0001, &wf, 0, 0, 0x0000);

    //WAVE情報設定
    wavehdr_tag wt = new wavehdr_tag();
    wt.lpData = (sbyte*)wave_data;
    wt.dwBufferLength = sizeof(short) * 44100;
    wt.dwFlags = 0;             //バッファの追加情報
    wt.dwLoops = 1;             //1回再生
    wt.dwBytesRecorded = 0;     //録音用
    wt.dwUser = 0;              //オプションのユーザ領域
    wt.lpNext = null;           //使用しない
    wt.reserved = 0;            //使用しない

    //再生
    windows.waveOutPrepareHeader(hWOut, &wt, (uint)sizeof(wavehdr_tag));
    windows.waveOutWrite(hWOut, &wt, (uint)sizeof(wavehdr_tag));
    MessageBox.Show("OK", "確認", MessageBoxButtons.OK);
    windows.waveOutClose(hWOut);
}

Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2006-03-11 08:52
C# 読んでる気が全くしないコードですね……

.NET では変な値を渡したりした場合、大抵は例外を投げますが、Win32API みたいなのでは、関数の失敗は返値で表します。
waveOut 系の関数の場合は返値から失敗の原因を文字列化する waveOutGetErrorText 関数がありますから、MMSYSERR_NOERROR = 0 以外が返ってきている関数の返値にこれを使って原因を探してください。
tori31001
会議室デビュー日: 2006/03/11
投稿数: 6
投稿日時: 2006-03-11 10:03
おはようございます。
Hongliangさんアドバイスありがとうございます。
早速waveOut系の返値を全て確認してみたのですが、0でした。

おっしゃる通り、全くC#っぽく無いです…。
結構検索してみたのですが、C#の記事はやはり少なく、その中でもwaveOut系のがほとんど無かったため、C++の記事を参考にして作りました。

あと、補足ですが、使用したInterop Declarations for Windows.h はDllImportの第一引数(リンク設定?)が初期状態で全て@"..¥dll¥user32.dll"のようになっていて使えなかったので、全て@"user32.dll"のように書き換えました。
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2006-03-11 10:24

あれ、成功してました?
お使いの PC にサウンドデバイスは 2 つ以上付けていますか? そうでなければ失敗するコードのはずですが。

> windows.waveOutOpen(&hWOut, 0x0001, &wf, 0, 0, 0x0000);
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2006-03-11 10:33
引用:

tori31001さんの書き込み (2006-03-11 06:46) より:

C#でWin32APIを使い音を鳴らしたいと思って


そもそも、Managed DirectX - DirectSound じゃダメなんですかね? (>_<)

_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
tori31001
会議室デビュー日: 2006/03/11
投稿数: 6
投稿日時: 2006-03-11 10:50
Hongliangさんありがとうございます!!
サウンドデバイス2つあったことをすっかり忘れていました。
デバイス替えたら無事音が鳴りました。
ついでに、指摘してくださったWAVE_MAPPERの値0x0001を0x0000に替えたら、デバイス替えなくても音が鳴りました。ライブラリのヘッダファイルはしっかり確認しないと駄目だと痛感しました。ありがとうございました。

----
じゃんぬさん、アドバイスありがとうございます。
DirectSoundも気になるのですが、wave関連以外でもWin32APIを使いこなせるようになりたいと思い、その取っ掛かりとして音を鳴らそうとしてみました。まだ文法すらままならないので、文法と並行してやっていこうと思ってます。
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2006-03-11 11:10
ていうか MSDN にクラス化してるのが載ってますな。

引用:

ついでに、指摘してくださったWAVE_MAPPERの値0x0001を0x0000に替えたら、デバイス替えなくても音が鳴りました。ライブラリのヘッダファイルはしっかり確認しないと駄目だと痛感しました。ありがとうございました。


0 だと定数 WAVE_MAPPER とは関係なく 1 番目のデバイスを見に行くだけのような。


Win32API を使いこなすためは良いですけど、もうちょっと C# に歩み寄ったコードの方が良いと思います。先の MSDN のを参考にしてみてください。
// あれもまあ微妙なところもありますけど。
tori31001
会議室デビュー日: 2006/03/11
投稿数: 6
投稿日時: 2006-03-11 11:50
Hongliangさん、たびたびすいません。
クラス便利そうですね。参考にします。ありがとうございます。

引用:

Win32API を使いこなすためは良いですけど、もうちょっと C# に歩み寄ったコードの方が良いと思います。



色々サンプルなどを見てがんばっていきたいです。
今まで文法書などを見ているだけだったので、これからはもっとコードを書いていこうと思います。やっぱ読むのと書くのじゃ全然違いますね…。

スキルアップ/キャリアアップ(JOB@IT)