Nodachisoft Nodachi Sword Icon
  
@あまじ✎ 2021年3月20日に更新

第3章8 PlaySound関数を使って音を鳴らす

イチからゲーム作りで覚えるC言語
第3章7 ネット接続してWebからデータを取得 : PREV
NEXT : 第3章9 MCIを使って音を鳴らす :

概要

Windows API を使って音声を鳴らす方法について確認します。 目的によって、使う Windows API が違ってきます。

音声の波形データを直接編集して再生するようなエフェクトをかけたり 小さいファイルを単純に再生したい場合に有用な PlaySound 関数について確認していきます。

なお、大きなサイズの音声ファイルのを再生したり、 動画を再生したり、MIDIファイルを演奏したりしたい場合には メディアコントロールインターフェイス(Media Control Interface)を使用すると楽です。

PlaySound 関数

Windows API の PlaySound 関数を使用することで、Wav ファイルと呼ばれる音声ファイルを Windows API を使って再生します。 Wav ファイル(waveform-audio)には、音声の波形をデジタルデータに変換した内容が入っています。

PlaySound 関数の定義は下の用になています。Windows.h ヘッダーをインクルードすることで使えるようになります。

PlaySound使い方
BOOL PlaySound(
   PCTSTR pszSound, // 音声ファイル名へのパス
   HMODULE hmod,    // リソースファイルへのパス
   DWORD  fdwSound  // 音声ファイルのプレイ方法を指定
);

使い方の例はこんな感じです。

PlaySound例
BOOL PlaySound(TEXT("kick.wav"), NULL , SND_FILENAME);

PlaySound 関数で音声の再生に成功すると TRUE を返します。

では音楽を再生して、再生が終わったらプログラムを終了する例を確認します。

音声再生の例

同じフォルダに gb1.wav を格納してから、プログラムを実行すると、 短い音楽が流れますのでご注意くださいね。 gb1.wav はこちらからダウンロードください。

 
playsound_ex.1
#include <stdio.h>
#include <Windows.h>
#pragma comment(lib, "winmm.lib")   // MSVC 用

int main() {
    printf("はーぴー:リラックスできる音楽を流しますね。\n");
    printf("はーぴー:~~~♪\n");
    PlaySound( TEXT("gb1.wav") , NULL, SND_FILENAME);
    printf("はーぴー:精一杯演奏してみました。\n");
}

実行すると、音声が流れます。 サンプルの音は、収録時にだいぶ小さめな音量に設定しています。(いきなり大音量でながれてびっくりしないように)

音の再生が完了したら、「はーぴー:精一杯演奏してみました。」と表示されてプログラムが終了します。

実行結果
はーぴー:リラックスできる音楽を流しますね。
はーぴー:~~~♪
はーぴー:精一杯演奏してみました。

ループ再生や非同期再生

先ほどの例だと音楽の再生が終わるまでの間、プログラムは PlaySound から先に 進まない状態でした。

これは同期処理として実行していたためです。

第3引数の fdwSound には沢山のプレイ方法の指定する種類がありますが、 今回使用する主要なものをピックアップします。

フラグ 機能
SND_ASYNC 非同期で音声を再生する。プログラムの裏で音声が流れる。
SND_FILENAME 第1引数に指定した音声ファイルを再生する
SND_LOOP 音声ファイルをループ再生
SND_NOSTOP 既に再生されている音声を停止しない
SND_SYSTEM 音量調節バーを WIndows の通知アイコンから操作できるようにする
SND_SYNC 同期モードで再生する。音声ファイルの再生が終わるまで、PlaySound の実行中となる
SND_NODEFAULT 再生したい音声ファイルが見つからなかった場合、デフォルト音声は再生しない
SND_MEMORY メモリ上の音声データを再生する

たとえば、音声ファイルを取得するためのフラグ「SND_FILENAME」と 音声をループ再生するためのフラグ「SND_LOOP」を有効にして PlaySound 関数を実行する には、下のように記載します。

 
Flag指定例
PlaySound(TEXT("gb1.wav"), NULL, SND_FILENAME | SND_LOOP);

フラグ1 | フラグ2 | フラグ3 ・・・ のように OR 演算子の「|」記号で複数のフラグをつなげて書くことで、複数のフラグを指定することができます。

非同期の音声再生と停止

以下のように非同期のフラグ(SND_ASYNC)で PlaySound 関数を実行した場合、 バックグラウンドで音楽が流れ続けます。

PlaySound
// 音声を非同期でループ再生する
PlaySound(TEXT("gb1.wav"), NULL, SND_ASYNC | SND_FILENAME );

非同期で関数を実行した場合は、PlaySound で音声ファイルの再生スタートが 始まったら即座に「成功」と関数の結果が返ってきて、 プログラムは次の処理に移ります。

つづいて、音声の再生を停止する方法です。

停止したいタイミングで、再度 PlaySound 関数を呼び出すことで止めることができます。

音声の再生を停止させる
PlaySound(NULL, 0, 0);

ループ再生させている音楽を停止する時などには、 自分で時間を計測して、一定時間になったら停止させるなどの処理を書くことができます。

例として、5秒間ループで音を再生してから停止するプログラムです。

 
playsound_ex.2
#include <stdio.h>
#include <Windows.h>
#pragma comment(lib, "winmm.lib")   // MSVC 用

int main() {
    printf("はーぴー:リラックスできる音楽を流しますね。\n");
    printf("はーぴー:~~~♪\n");
    PlaySound(TEXT("gb1.wav"), NULL, SND_FILENAME | SND_LOOP | SND_ASYNC);
    printf("はーぴー:5 秒間、音をループで流しますね!\n");
    for (int i = 0; i < 5; i++) {
        Sleep(1 * 1000);
        printf("はーぴー:あと %d 秒です♪\n", (4 - i));
    }
    PlaySound(NULL, 0, 0);
    printf("はーぴー:精一杯演奏してみました。\n");
    printf("System: エンターキーでプログラムを終了します。\n");
    getc(stdin);
}

プログラムが終了するとき、自動的に PlaySound 関数のループ再生は止まります。 今回は、ちゃんと PlaySoun(NULL,0,0) のタイミングで音楽の再生が停止したことがわかるように、 最後にエンターキーの入力で終了するように getc 関数で入力を受け付けるようにしています。

再生したいファイルを読み込める場所

PlaySound で、「SND_FILENAME」フラグを付けて音声ファイルを指定して再生するとき 以下のフォルダの場所から音声ファイルを探し出して再生しようとします。

  1. カレントディレクトリ
  2. Windows ディレクトリ(標準では「c:\Windows」などの Windows インストール先フォルダ )
  3. Windows System ディレクトリ(通常では「c:\windows\system32\」のWindows のシステムフォルダ)
  4. 環境変数「PATH」に含まれるディレクトリ
想定外の音が鳴る

再生したい音声ファイルが見つからなかったとき、 システムのデフォルト音声が再生されます。 ファイルが見つからなかったときに、何も音を再生したくない場合は、 PlaySound の第3引数に SND_NODEFAULT を付けて呼び出ししましょう。

Windows 10 であれば、デフォルト音で「ボ↓ロ↑~ォン♪」というエラー時の音が聞こえます。

再生できるファイルの種類

PlaySound 関数で再生出来るのは Wave 形式ファイルのみです。

Wave ファイル形式について簡単に触れておくと、 Wave ファイルは通常、拡張子「.wav」で名前がついているファイルで、 ファイルは、音楽の作者やタイトル、コメント、どのような音声データ形式のデータが入っているかの指定などメタデータと、音声データ(スピーカーから出力する音声の波形データの元となるデータ)から構成されています。

このように、、波形データについての作者の情報や、データの圧縮形式などのメタデータと、音声データ本体がセットになっているファイルを 一般的にコンテナファイルと呼びます。

よく勘違いされますが、wav ファイルは生の波形サンプリングデータだけではなく、 例えば mp3 形式などの圧縮されたデータも wav ファイルの中のデータとして扱うことができます。

PlaySound 関数は mp3 ファイルそのものは再生できませんが、Wave ファイルに mp3 圧縮データを格納することで、 mp3 ファイルも扱うことができます。

なお、mp3 ファイルや ogg ファイルなどを直接扱いたい場合は、次のページの MCI コマンドを使用すると簡単です。

扱うファイルサイズ

PlaySound 関数で音楽ファイルを扱う時、wave ファイルをすべてメモリ上に読み込んでから再生をします。

試に、300 MB ほどのサイズのファイルを読み込んだところ、下のようにメモリの使用サイズが膨らんでいることが分かります。

PlaySound関数で大きなファイルを読込

大きな wave ファイルを扱う時はメモリを喰いますのでご注意ください。

ゲームなどで使用するときの注意

ゲームで音を再生するとき、タイムラグなしにその場で高速に再生したい場合があります。

ファイルから読み込んでから再生するときは、まずディスクにアクセスして読み込むという作業をしているので、 実際に音が再生されるまでタイムラグが出てしまう可能性があります。

頻繁につかったり、なるべくタイムラグなしに音を再生したい場合は まず最初にメモリ上に音声ファイルのデータを読み込んでおき、 鳴らしたいときに、予めロードしておいたメモリを再生する方が高速です。

メモリに読み込んでから再生する例は下のようになります。

 
playsound_ex4.c
#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
#pragma comment(lib, "winmm.lib")   // MSVC 用

// 指定したファイルのファイルサイズを確認して return で返す
int getFileSize(const char *fileName) {
    FILE* fp = fopen(fileName , "rb");
    long fileSize = 0;
    fseek(fp, 0, SEEK_END); // seek to the end of the file
    fileSize = ftell(fp);  // get the current file pointer 
    rewind(fp);  // rewind to the beginning of the file
    fclose(fp);
    return  fileSize;
}

// バイナリデータをヒープ領域のメモリに読み込む
char* readBinaryFile(const char* fileName) {
    int fileSize = getFileSize(fileName);
    char* fileData = (char*)calloc(sizeof(char), fileSize + 1);
    FILE* fp = fopen(fileName, "rb");
    int resultSize = fread(fileData, sizeof(char), fileSize, fp);
    fclose(fileName);
    if (resultSize != fileSize) {
        printf("システム:正しくファイルが読み込めませんでした。サイズが一致しません。\n");
        exit(-1);
    }
    return fileData;
}


int main() {
    printf("はーぴー:リラックスできる音楽を一度頭の中で思い浮かべてから流しますね。\n");
    printf("はーぴー:頭に読み込むぞー!\n");
    char *soundFileData = readBinaryFile("gb1.wav");
    printf("はーぴー:読み込んだ音楽を流しますよー♪");
    PlaySound( soundFileData , NULL, SND_MEMORY );
    printf("はーぴー:精一杯演奏してみました。\n");
    free(soundFileData);
}

実行すると、ビルドするフォルダか、exeファイルを実行するフォルダと同じフォルダにある「gb1.wav」を 読み込んで、音声を再生できます。

メモリ上のデータを再生する時の PlaySound 関数は下のように呼び出します。

PlaySound関数でメモリから再生
PlaySound( 音声バイナリデータ, NULL, SND_MEMORY );

補足となりますが、getFileSize関数は引数で指定したファイルのサイズを返します。 また、readBinaryFile関数は引数で指定したファイルをchar 型のバイナリデータとして全てメモリのヒープ領域に読み込んで、 読み込んだデータの先頭へのポインタを返しています。

2章25

補足:リソースファイルからの音声再生

特定の実行可能ファイル(exeファイルなど)に 音声や画像などの素材(リソースと呼ばれます)をまとめて埋め込み、 個別のファイルではなく、リソースから読み込む方法もあります。

音声や画像をまとめたリソースファイルの作り方やデータの読み込み方は今回は割愛します。

参考文献

イチからゲーム作りで覚えるC言語
第3章7 ネット接続してWebからデータを取得 : PREV
NEXT : 第3章9 MCIを使って音を鳴らす :
 
 
送信しました!

コメント、ありがとうございます。

なんかエラーでした

ごめんなさい。エラーでうまく送信できませんでした。ご迷惑をおかけします。しばらくおいてから再度送信を試していただくか、以下から DM などでご連絡頂ければと思います。

Twitter:@NodachiSoft_jp
お名前:
 
連絡先:
 
メッセージ:
 
戻る
内容の確認!

以下の内容でコメントを送信します。よろしければ、「送信」を押してください。修正する場合は「戻る」を押してください

お名前:
 
連絡先:
 
メッセージ:
 
Roboto からの操作ではないという確認のため確認キーを入れてください。
確認キー=95
戻る
 / 
送信確認へ
コメント欄
コメント送信確認へ

関連ありそうな記事(5件)です!

第1章01 Visual Studio Community 2019 のインストール手順

#C11仕様#C言語#ゲームプログラミング✎ 2021-08-08
C言語でプログラミングをするために、無料で使える Visual Studio Community を使った開発環境を揃えていく手順や注意点をお話しています。
目次
第3章8 PlaySound関数を使って音を鳴らす
第3章8 PlaySound関数を使って音を鳴らす
概要
概要
PlaySound 関数
PlaySound 関数
音声再生の例
音声再生の例
ループ再生や非同期再生
ループ再生や非同期再生
非同期の音声再生と停止
非同期の音声再生と停止
再生したいファイルを読み込める場所
再生したいファイルを読み込める場所
再生できるファイルの種類
再生できるファイルの種類
扱うファイルサイズ
扱うファイルサイズ
ゲームなどで使用するときの注意
ゲームなどで使用するときの注意
補足:リソースファイルからの音声再生
補足:リソースファイルからの音声再生
参考文献
参考文献
Nodachisoft © 2021