none
鳴動させるアラーム音等の音の音量設定 RRS feed

  • 質問

  • いつも大変お世話になっております、鏑木です。
    現在、Windows CE 6.0 にてVisual Studio 2005のC++のダイアログベースのプログラムを作成しており、そのアプリケーション内で
    ある条件を満たしたときにアラーム音を再生したいと思っています。そこで、そのアラーム音の音量設定方法についてお聞きしたく、こちらに
    質問させていただきました。どうぞよろしくお願いいたします。

    現状で、私が使用しているアラーム音の音量設定としては、音量変更を8段階とし8つの音量に設定したアラーム音をwavファイルとして作成し、
    音量選択によって、その都度その音量に対応したwavファイルを設定し、そのwavファイルをAPIのPlaySound関数を使用してアラーム音を
    鳴動させています。
    この方法ですと、1つの音に対して音量分のwavファイルをハードディスク内に作成しなければならず、余計な容量を使ってしまいますし、また
    アラーム音以外にも操作音といったそれ以外の音楽ファイルも必要なため、結構な数となってしまいます。
    ネットを調べてみると、waveOutSetVolume関数でwavファイルの音量を設定できるようですが、この関数ですと再生されるすべてのwav
    ファイルの音量が設定されてしまうようで、私としては、アラーム音はアラーム音の音量設定、操作音は操作音の音量設定と、それぞれの音を
    個別に設定したいと思っています。
    そこで、wavファイルや単音でもよいので、音を個別に音量設定することができる方法をご存じの方がいらっしゃいましたら、教えていただけませんでしょうか?
    周波数の設定によって音程を変えることはできるようですが、音量の設定方法がネットを探しても見つけることができませんでした。
    ご回答をお待ちしております。

    なお、開発環境は
    Windows CE 6.0
    Visual Studio 2005
    です。
    よろしくお願いいたします。
    2009年10月30日 2:30

回答

  • あ、すいません。説明不足過ぎでした。

    Visual Studio 2008でC++によるWAVEファイルの再生プログラムについて

    での 佐祐理 さんのサンプル ( コンソールアプリケーションですが ) を見て頂くと判り易いと思います。
    このスレッドで行っていたのは、PlaySound 等を使わず、waveOut 系の API を使って、.wav データを鳴らすというものです。

    .wav データに著作権上の問題が無く、そのフォーマットと長さが判っており、かつ再コンパイルが必要になるまで変えないというのであれば、サンプリングデータのみをバイナリデータとしてアプリケーション側に取り込んでおきます。

    ここで取り込むのは最大音量のサンプリングデータのみです。

    後は waveOutPrepareHeader で登録する前に、取り込んでおいたサンプリングデータに 1 以下の値を掛けて、バッファに設定していきます。半分の音量で鳴らしたいのであれば、サンプリングデータ * 0.5 という感じで。

    その後、waveOutPrepareHeader、waveOutWrite と行えば、半分の音量に加工されたサンプリングデータが再生されるはずです。

    操作音などの他の音があるという事ですが、最終的な再生は Windows のバッファから行われるはず ( だったと思う ) なので、大丈夫だと思います。
    • 回答としてマーク 鏑木肆星 2009年12月3日 8:20
    2009年10月30日 7:26
  • ああ、一足遅かったですね・・・。

    こちらで試した所、やはり前述の通り、サンプリングデータ本体を取り出し、0.5 を掛ける事で半分と思われる音量になりました。

    ただし、サンプリングデータを取り出す時と、加工したサンプリングデータを格納する時に注意が必要です。

    実を言えば、半分の音量にする事自体はわりに早く出来ました。が、妙なノイズが入ってしまい、その原因に気付くまで随分と時間がかかってしまいました。

    私は Windows\\Media 内にある 22050 Hz、ステレオ、16 ビット量子化の .wav データを読み込み、半分の音量で鳴らそうとしていました。

    そこで、佐祐理 さんのサンプルの近くに転がっている、私が書いたコードを元に .wav ファイルを読み込み、サンプリングデータ本体を BYTE 配列に読み込みました。

    そして半分にする為、WORD のバッファを作成し、BYTE 配列から 2 つずつデータを読み込み、上位バイトを 8 ビット左シフトして、16 ビットデータにしていました。その値に 0.5 を掛け、WORD バッファに格納して鳴らしたのですが、サーッというノイズが入ってしまいました。

    その原因を調べるのに、一時間もかかってしまいました。マヌケですねぇ。

    原因は BYTE 配列から 2 つずつデータを読み込み、__int16 値にする部分にありました。

    static_cast<__int16>(BYTE[2 * i] | (BYTE[2 * i + 1] << 8))

    という感じのコードを書いていたのですが、正しくは

    static_cast<__int16>((0x00FF & BYTE[2 * i]) | (0xFF00 & (BYTE[2 * i + 1] << 8)))

    でした。キャスト時に __int16 を宣言した際のゴミデータとでも言うべきものが残っていました。


    そんな理由から返答が遅れてしまい、申し訳ありませんでした。

    wave データを直で作成するか、読み込むか、どちらの方法にされるか判りませんが、余計な手間を取らせる結果になり、すいませんでした。
    • 回答としてマーク 鏑木肆星 2009年12月3日 8:20
    2009年11月2日 8:46

すべての返信

  • 少し乱暴な方法かもしれませんが、音のデータ ( サンプリングデータ ) 自体を加工して、音量を下げるというのはどうでしょうか?

    44100 Hz、16 bit Stereo の場合、サンプリングデータ本体は -32768 ~ 32767 の値になっているはずなので、個々の値に 1 以下の値を掛けていき、最終的に waveOutWrite で鳴らしてはいかがでしょう?
    2009年10月30日 2:59
  • ミッヒー様ご回答ありがとうございます。

    waveファイルを扱った処理をしたことがないのでミッヒー様のおっしゃられた意味を半分以下くらいしか理解できなかったのですが、
    アラーム音用のwavの音楽ファイルを準備し、それをなんらかの方法で数値データ化させて、その値を編集し、waveOutWrite関数
    で鳴動させるということでしょうか?

    鏑木
    2009年10月30日 6:51
  • 単純なアラーム音ならBeepを使用してはどうでしょうか?
    http://msdn.microsoft.com/ja-jp/library/cc428923.aspx
    音の周波数,音の継続時間が変更できます。
    Sleepと組み合わせてループさせ別のスレッドで実行させてはどうでしょうか?
    2009年10月30日 6:53
  • あ、すいません。説明不足過ぎでした。

    Visual Studio 2008でC++によるWAVEファイルの再生プログラムについて

    での 佐祐理 さんのサンプル ( コンソールアプリケーションですが ) を見て頂くと判り易いと思います。
    このスレッドで行っていたのは、PlaySound 等を使わず、waveOut 系の API を使って、.wav データを鳴らすというものです。

    .wav データに著作権上の問題が無く、そのフォーマットと長さが判っており、かつ再コンパイルが必要になるまで変えないというのであれば、サンプリングデータのみをバイナリデータとしてアプリケーション側に取り込んでおきます。

    ここで取り込むのは最大音量のサンプリングデータのみです。

    後は waveOutPrepareHeader で登録する前に、取り込んでおいたサンプリングデータに 1 以下の値を掛けて、バッファに設定していきます。半分の音量で鳴らしたいのであれば、サンプリングデータ * 0.5 という感じで。

    その後、waveOutPrepareHeader、waveOutWrite と行えば、半分の音量に加工されたサンプリングデータが再生されるはずです。

    操作音などの他の音があるという事ですが、最終的な再生は Windows のバッファから行われるはず ( だったと思う ) なので、大丈夫だと思います。
    • 回答としてマーク 鏑木肆星 2009年12月3日 8:20
    2009年10月30日 7:26
  • myoko2様、ご回答ありがとうございます。

    はじめはBeepも考えたのですが、Beep音ではサウンドボードからの音なので音の高低を周波数の
    設定によって変更することができても、音量は一定となっており変更することができず断念しました。

    鏑木
    2009年10月30日 7:51
  • ミッヒー様ご回答ありがとうございます。

    ミッヒー様から教えていただきました過去のスレッドから鑑みて、waveファイルをWAVEHDR構造体とWAVEFORMATEX構造体にそれぞれ
    数値として代入し、そこから音楽ファイルの編集をしていくのだなということは分かりました。
    ただ、これを聞いてしまったら今回の私の質問の答えとなってしまうのですが、各構造体のどの値がその音楽ファイルの音量を司っているのかが
    判断できませんでした。波形データの振幅値を変更させればよいとは思うのですが。。
    構造体のどの値が波形データ、音量の調節をすることができる値なのでしょうか?

    ちなみにミッヒー様のご紹介いただいたスレッド以外に
    http://www13.plala.or.jp/kymats/study/multimedia.html
    というサイトも参考にしております。

    鏑木
    2009年10月30日 8:36
  • あ、いえ、WAVEFORMATEX は再生したい Hz、ビットレート、チャンネル数を設定するものです。音量というデータは .wav ファイルには存在しません。

    佐祐理 さんのサンプルでは、.wav ファイルのフォーマット部分を読み込み、それと同じ出力デバイスを開いているだけなので、予め再生するフォーマット、というか音の品質が決まっているなら、その値を WAVEFORMATEX に設定して waveOutOpen を行えばいいです ( ただ、Windows は WAVE_FORMAT_PCM フォーマット以外再生出来なかったはず ) 。

    ここで要となるのは WAVEHDR です。再生のみですから、lpData と dwBufferLength だけを設定し、後は 0 クリアなりしておけばいいです。

    で、再び 佐祐理 さんのサンプルに戻りますが、サンプリングデータ自体は samples の中に格納されています。この配列内の値がサンプリングされたデータ本体で、何ビットでサンプリングされたか、チャンネル数はいくつかによって異なる形で並んでいます。

    ここの形を決めているのが .wav ファイルの WAVEFORMATEX に読み込まれる部分で、nChannels がチャンネル数、wBitsPerSample がビットレートを示しています。後の値は録音時の周波数等の値です。

    私が 2 回目の投稿で サンプリングデータ と言っているのは sample 内に格納されたサンプリングデータ本体の事で、前述の通り、.wav ファイルの WAVEFORMATEX 部分によりどのような順番で格納されているのかが判ります。

    仮に 44100Hz、16 ビットレート、ステレオでサンプリングされたデータであれば、WORD 境界で右のデータ、左のデータという順序で、この組み合わせが 1 秒で 44100 組ある事になります ( 左右が逆だったかもしれませんが・・・ ) 。

    この値は音の波を 1 秒間を 44100 に区切り、その時点での波の高さを示したデータで、正負の符号付きです。

    波の揺れ幅が大きくなるほど音は大きくなるので、各データに 0.5 を掛けると、音の波全体としては半分の音量となります。

    ので、__int16 としてデータを読み込み、0.5 を掛ける、という作業をサンプリングデータ全体に行えば、音量は半分になります。

    後はそのデータを登録 ( waveOutPrepareHeader ) して、デバイスへ転送 ( waveOutWrite ) すれば再生されます。

    なお、登録したデータは登録解除が必要となりますので、waveOutOpen 時に再生終了メッセージをウィンドウで受け取るなり、佐祐理 さんが行われたようにコールバック関数で受け取るなりの設定をし、終了メッセージを受け取った時点で waveOutUnprepareHeader を忘れずに呼び出してください。
    • 編集済み ミッヒー 2009年10月30日 10:03 登録解除に関する記載漏れ追加
    2009年10月30日 9:58
  • ミッヒー様ご回答ありがとうございます。

    ミッヒー様のおっしゃられることと佐祐理様がご提示していたプログラムから考えまして、waveファイルの波形データ部分をとりだし、その波形データ=波形の
    振幅と考えられるので、その値に最大音量を1として、その1に対する比率を乗算した波形データを作成し、再生すればよい、という解釈でよろしいでしょうか?

    そのような解釈で、作成したところ音量が低くなることはなく、ノイズが増えただけのようでした。
    初めからwaveデータを作成する際に、波形データに格納する値の上下を変更させると音量変化に対応させることはできたのですが、出来上がった音楽ファイル
    の波形データの振幅にただ割合をかければ音量変化になる、というわけではないようです。


    鏑木
    2009年11月2日 6:06
  • ミッヒー様のおっしゃられることと佐祐理様がご提示していたプログラムから考えまして、waveファイルの波形データ部分をとりだし、その波形データ=波形の振幅と考えられるので、その値に最大音量を1として、その1に対する比率を乗算した波形データを作成し、再生すればよい、という解釈でよろしいでしょうか?
    あっていると思っていましたが、上手く鳴らないんですね・・・。

    実際の所、プログラムの組んで確認したわけではありませんでした。すいませんでした。

    とは言え、以前 .mod の再生プログラムを作った時には実際そうして音量を変えていたので・・・。

    サンプルプログラムを作成し、また返信させて頂きます。
    2009年11月2日 6:15
  • ミッヒー様、ご回答ありがとうございます。

    あくまで私の能力で、作成したプログラムではの話なのであまり自信はありませんが。。
    現状ではwaveデータを初めから作成し、周波数と波長から音楽データを作成して再生するようなプログラム
    を作成しています。この方法であれば、音量を変更することはできるようです。


    鏑木
    2009年11月2日 8:16
  • ああ、一足遅かったですね・・・。

    こちらで試した所、やはり前述の通り、サンプリングデータ本体を取り出し、0.5 を掛ける事で半分と思われる音量になりました。

    ただし、サンプリングデータを取り出す時と、加工したサンプリングデータを格納する時に注意が必要です。

    実を言えば、半分の音量にする事自体はわりに早く出来ました。が、妙なノイズが入ってしまい、その原因に気付くまで随分と時間がかかってしまいました。

    私は Windows\\Media 内にある 22050 Hz、ステレオ、16 ビット量子化の .wav データを読み込み、半分の音量で鳴らそうとしていました。

    そこで、佐祐理 さんのサンプルの近くに転がっている、私が書いたコードを元に .wav ファイルを読み込み、サンプリングデータ本体を BYTE 配列に読み込みました。

    そして半分にする為、WORD のバッファを作成し、BYTE 配列から 2 つずつデータを読み込み、上位バイトを 8 ビット左シフトして、16 ビットデータにしていました。その値に 0.5 を掛け、WORD バッファに格納して鳴らしたのですが、サーッというノイズが入ってしまいました。

    その原因を調べるのに、一時間もかかってしまいました。マヌケですねぇ。

    原因は BYTE 配列から 2 つずつデータを読み込み、__int16 値にする部分にありました。

    static_cast<__int16>(BYTE[2 * i] | (BYTE[2 * i + 1] << 8))

    という感じのコードを書いていたのですが、正しくは

    static_cast<__int16>((0x00FF & BYTE[2 * i]) | (0xFF00 & (BYTE[2 * i + 1] << 8)))

    でした。キャスト時に __int16 を宣言した際のゴミデータとでも言うべきものが残っていました。


    そんな理由から返答が遅れてしまい、申し訳ありませんでした。

    wave データを直で作成するか、読み込むか、どちらの方法にされるか判りませんが、余計な手間を取らせる結果になり、すいませんでした。
    • 回答としてマーク 鏑木肆星 2009年12月3日 8:20
    2009年11月2日 8:46
  • ミッヒー様、ご返答が遅くなり申し訳ございませんでした。
    周波数から単音を作成するということで自分の中で納得してしまい、ミッヒー様のご回答を見過ごしていました(==;)

    >そして半分にする為、WORD のバッファを作成し、BYTE 配列から 2 つずつデータを読み込み、上位バイトを 8 ビット左シフトして、16 ビットデータにしていました。
    音量を半分にするためにはこのような操作が必要だったのですね。。単に1/2をかけて算出していました。そのためにまったく音量の変化がみられなかったのだと思います。

    現在はとりあえず単音としていますが、今後時間の取れ次第ミッヒー様がお教えくださった音楽データの音量変更について試してみたいと思います。
    参考となるご意見ありがとうございました。

    鏑木
    2009年12月3日 8:20
  • 回答済みになっていますが、少しアドバイスを。

    私の遭遇したバグですが、サンプリングデータを BYTE 配列に読み込んでいた事が原因でもあります。

    予め、量子化ビット数が判っているのなら、16 ビットの場合は __int16 か WORD 配列に、8 ビットの場合は __int8 か BYTE 配列に読み込んでしまえば、データの加工の際にビットシフト等の余計な処理をしなくて済みます。

    それと http://www13.plala.or.jp/kymats/study/multimedia.html には 8 ビットの場合、0 ~ 255 と書いてありますが、-128 ~ 127 だった様な覚えがあります ( 未確認です。すいません )。

    追記

    なお、複数の Wave を鳴らす場合ですが、HWAVEOUT を複数開き、個々に waveOutWrite を行う事で Windows 側でミキシングが行われるようです。試していませんが、サンプリングデータを修正せずとも、個々の HWAVEOUT の waveOutSetVolume で音量を変えられるかもしれません。

    未確認ばかりですいません。

    2009年12月3日 9:42