音声再生

目次

はじめに

 HGIMG4を使って作ったゲームにも音をつけたいですよね。 少し困ったことがあったので、HSP3での音まわりも含めて情報を整理してみます。

HSP3での音の再生

 HGIMG4を使って作ったゲームにも音をつけたいですよね。 少し困ったことがあったので、HSP3での音まわりも含めて情報を整理してみます。

 まずHSP3で音声を再生する場合、標準では3種類の方法が用意されています。

  • mci命令
  • mmplay命令などmm系命令
  • dmmplay命令などdmm系命令

特徴を整理してみました。

mcimm系dmm系
プラグイン※不要不要
hsp3dish.as
hgimg4.as
hspogg.as
hgimg3.as
プラットフォーム
win
dish ××
フィル形式
WAV
MIDI ×
CD-DA ×
AVI ×
MP3 ×
ASF(ASF,WMV,WMA)×
MPEG ×
OGG ××
その他
dpm ? ?

※ プラグインは、書いてあるどれか1つを導入すれば使用できます。

 さらに、mm系とdmm系命令は同じような機能の命令が多いので比較してみました。 機能や使い方はどちらもよく似ており、命令名も頭にdがあるかないかの違いしかありません。 相互に乗り換えがしやすい作りになっています。hgimg3からhgimg4へ、またその逆も移植作業の負担が小さくて済みますね。

mm系dmm系
読み込み mmloaddmmload
再生 mmplaydmmplay
再生停止 mmstopdmmstop
パン設定 mmpan dmmpan
状態取得 mmstatdmmstat
音量設定 mmvol dmmvol
初期化 - dmmini
終了処理 - dmmbye
設定をリセット- dmmreset
削除 - dmmdel
ループ設定 - dmmloop

MCI

 MCIは、HSP3で昔から使われている機能です。 コマンド文字列を与えて操作するのですが、資料が少なくてついこなせなかった記憶しか…。 詳しくは他のサイトにお任せします。

HSP3命令入門講座 <mci命令> - Let's HSP!
http://lhsp.s206.xrea.com/command/mci.html

以降はmm系/dmm系命令について記述していきます。

mm系命令

 hgimg4を使用する場合は、mm系命令を使います。 hgimg4.asとhspogg.asは同時にincludeすることができないため、dmm系命令は使用することができません。 「#Error 38 外部DLLの呼び出しに失敗しました」が出てしまいます。

 プラグインなしでも利用できるので、hgimg3を使用する場合もmm系命令を使用することができます。

 使い方は簡単で、次のような手順です。

  1. mmload命令で音声ファイルを読み込んで メディアバッファID に割り当てる。
  2. mmplay命令で メディアバッファID を指定して再生する。

 基本的なサンプルを書いてみました。


;
;	hsptvのSE素材を再生
;	mm系命令
;
;#include "hsp3dish.as"
;#include "hgimg4.as"

; ファイル
fname = dir_tv + "se_bom.wav"

;	サウンドを登録
id = 0
mmload fname, id

;	再生ボタン
button gosub "PLAY", *l_play
redraw 1
stop

;	再生ボタン
*l_play
	;mmstop -1
	mmplay id
	return

 stopの前の redraw 1 は、hsp3dish/hgimg4を使用した際に必要になります。 redraw 1 をしないとボタンが描画されません。

dmm系命令

 音声機能だけが必要な場合は、hspogg.asをincludeして使用します。 hgimg3の音以外の機能も使用する場合は、hgimg3.asをincludeして使用します。 hgimg3.asをincludeすれば、hspogg.asをincludeする必要はありません。

 前述したとおり、hgimg4を使用する場合は使用できません。

  1. dmmini命令でサウンド機能を初期化(1回だけやればいい)
  2. dmmload命令で音声ファイルを読み込んで サウンドID に割り当てる。
  3. dmmplay命令で サウンドID を指定して再生する。

 基本的なサンプルを書いてみました。


;
;	hsptvのSE素材を再生
;	dmm系命令
;

#include "hspogg.as"

; ファイル
fname = dir_tv + "se_bom.wav"

;	サウンドを登録
dmmini
id = 0
dmmload fname, id

;	再生ボタン
button gosub "PLAY", *l_play
stop

;	再生ボタン
*l_play
	;dmmstop -1
	dmmplay id
	return

 mm系命令とほぼ同じでしたね。

HSPTVフォルダ素材

 前述したサンプルでも使用していますが、HSP3にはゲームに使用できる素材ファイルが付属しています。 結構豊富なので、この素材だけでゲームを作って公開することもできます。開発用の仮の素材としても便利です。

HSPTVフォルダ素材一覧
https://www.onionsoft.net/hsp/v36/doclib/hsptv_res.htm

 せっかくなので全部聞いてみようと思います。 どうやら hsptv_res.as をincludeすると素材ファイルを一括で読み込めるみたいです。 これを使うと簡単そうですね。ちょっと作ってみました。


;
;	「HSPTVフォルダ素材」の使用例
;
; 「HSPTVフォルダ素材」
; https://www.onionsoft.net/hsp/v36/doclib/hsptv_res.htm
; の音楽素材を再生するサンプルです。

; hsp3dish.as / hgimg4.as を使用しなくても音を再生することはできます。
; どちらかをINCLUDEすると、再生開始のラグを低減させることができます。
; また、複数の音の同時再生ができるようになります。
;#include "hsp3dish.as"
;#include "hgimg4.as"

#include "hsptv_res.as"

;--------------------
;	SE
;--------------------
hsptv_se 1	;効果音をまとめて読み込む
repeat 15
	button gosub "SE" + strf("%02d", cnt+1), *l_play_SE
	objid_SE(cnt) = stat
	idSE(cnt) = SE01 + cnt
loop

;--------------------
;	SND
;--------------------
pos 100, 0
hsptv_se 2	;効果音をまとめて読み込む
repeat 19
	button gosub "SND" + strf("%02d", cnt+1), *l_play_SND
	objid_SND(cnt) = stat
	idSND(cnt) = SND01 + cnt
loop

redraw 1
stop


;--------------------
;	SE 再生ボタン
;--------------------
; どのボタンが押されたかを判定して、再生する音を選択しています。
; ボタン1個にサブルーチン1個を準備するのが面倒だったので、
; このような実装にしています。
*l_play_SE
	idBtn = stat	; 押されたボタンのオブジェクトID
	repeat 15
		if objid_SE(cnt) = idBtn {
			mmplay idSE(cnt)
			break
		}
	loop
	return

;--------------------
;	SND 再生ボタン
;--------------------
*l_play_SND
	idBtn = stat
	repeat 19
		if objid_SND(cnt) = idBtn {
			mmplay idSND(cnt)
			break
		}
	loop
	return

 効果音だけでも結構たくさんありますね。 素材にこだわらないのであれば、ここにあるものだけでも十分かもしれません。

HSPTVフォルダ素材から音を選ぶ

 hsptv_res.asを読み込んでしまうと、使わない素材も読み込んでしまいます。 配布するアプリ開発の場合は、必要なファイルだけを添付して配布したいです。

 ということなので、HSPTVフォルダにある音声ファイルをファイル名とともに再生できるようにしてみました。 これで再生して音を聞きながら必要なファイル名を知ることができます。


;
;	hsptvのSE素材を再生
;

;#include "hsp3dish.as"
;#include "hgimg4.as"

; ファイル一覧を取得
; HSPTVフォルダにあるWAVファイルの名前をすべて取得します。
mes "フォルダ:" + dir_tv
dirlist s, dir_tv + "*.wav", 3
numFList = stat
dim fnameList, 64, numFList
notesel s
repeat numFList
	noteget b, cnt
	fnameList(cnt) = b
loop

;	サウンドを登録
; 見つかったWAVファイルすべてに対して、再生ボタンを作ります。
dim objid, numFList
dim idSE,  numFList

px = 10
py = 30
repeat numFList
	; 再生ボタン
	pos px, py
	button gosub "PLAY", *l_play
	objid(cnt) = stat
	pos px + 70, py
	mes fnameList(cnt)
	py += 30
	if py > ginfo_winy - 40 {
		px+= 200
		py = 30
	}

	; 音声登録
	fnameSE   = dir_tv + fnameList(cnt)
	idSE(cnt) = cnt
	mmload fnameSE, idSE(cnt)
loop

redraw 1
stop

;	再生ボタン
; どのボタンが押されたかを判定して、再生する音を選択しています。
*l_play
	idBtn = stat	; 押されたボタンのオブジェクトID
	;mmstop -1
	repeat numFList
		if objid(cnt) = idBtn {
			mmplay idSE(cnt)
			break
		}
	loop
	
	return

MIDIもあるのでこれも同じようにしてみます。


;
;	hsptvのMIDI素材を再生
;
;#include "hgimg4.as"


; ファイル一覧を取得
mes "フォルダ:" + dir_tv
dirlist s, dir_tv + "*.mid", 3
numFList = stat
dim fnameList, 64, numFList
notesel s
repeat numFList
	noteget b, cnt
	fnameList(cnt) = b
loop

;	サウンドを登録
dim objid, numFList
dim idMB,  numFList


px = 10
py = 30
repeat numFList
	; 再生ボタン
	pos px, py
	button gosub "PLAY", *l_play
	objid(cnt) = stat
	pos px + 70, py
	mes fnameList(cnt)
	py += 30
	if py > ginfo_winy - 40 {
		px+= 200
		py = 30
	}

	; 音声登録
	fnameMIDI = dir_tv + fnameList(cnt)
	idMB(cnt) = cnt
	mmload fnameMIDI, idMB(cnt)
	;mmvol  idMB(cnt), -1000
loop

redraw 1
stop

;	再生ボタン
*l_play
	idBtn = stat
	;mmstop
	repeat numFList
		if objid(cnt) = idBtn {
			mmplay idMB(cnt)
			break
		}
	loop
	
	return

MIDIファイルもたくさんありますね。BGMはここから選んでもよさそうです。

音量設定

 mm系とdmm系にはどちらにも音量(ボリューム)設定命令があります。 基本的には同じような機能なのですが、引数の数値に違いがあります。

mmvol dmmvol
音量(引数) -1000~0 -10000~0
音量(設定値)-100.0~0-100.00~0
単位 リニア デシベル

 mmvolはリニア値を指定します。 0で音量最大、-1000で無音です。使用する際は、次のように考えると理解しやすいと思います。

  • 音量変更 0.0 %にするときは、 0を指定する。
  • 音量変更 -50.0 %にするときは、 -500を指定する。(音量半分)
  • 音量変更 -100.0 %にするときは、-1000を指定する。

 dmmvolはデシベル値を指定します。 0で音量最大、-10000で無音です。使用する際は、次のように考えると理解しやすいと思います。

  • 音量変更 0.00 dBにするときは、 0を指定する。
  • 音量変更 -6.00 dBにするときは、 -600を指定する。(音量半分)
  • 音量変更 -100.00 dBにするときは、-1000を指定する。

実際に使う際はわかりにくいので、マニュアルにある通りにサンプルvolsamp.hsp内のvol2db命令を使うと簡単です。

 また、mmvolは注意点があります。

  • hsp3dish.as、hgimg4.asのいずれかをincludeしないと使えません。
  • MIDIファイルは音量調整できません。

BGMの音量調整をしたい場合は、MIDIを避ける必要があり注意が必要ですね。

音を重ねる

 前述のサンプルで音声をいくつも再生していて気がついたと思うのですが、音が一つずつしか再生されません。 前の音を再生中に次の音を再生しようとすると、前の音が停止してしまいます。特に同じ音は必ず同時再生できません。

 dmm系の場合は、複数の音を重ねて再生することができます。 実はmm系も hsp3dish.as、hgimg4.asのいずれかをincludeすれば、複数の音を重ねて再生することができるようになります。 また、includeすることで再生までにかかる時間も少し短くなるようです。

 しかしどちらも同じ音は重複再生することはできません。 同じ音を複数再生したい場合は、同じ音のファイルを複数回読み込んで運用する方法を使います。 複数回読み込めば、同じ音でも別のIDなので重複再生することができます。

多重再生

 違う音は重ねて再生できますが、同じ音を重ねて再生することはできません。 同じ音を複数回読み込んでしまえば回避できますが、管理がちょっと面倒です。

 しかしゲームでの効果音は、同じ音を重ねて再生したい場面は必ずあります。 面倒でも必要になってくる処理です。 そういう時はモジュールにしておくと便利ですね。


#module

;-----------------------------------------------------------
;	サウンドデータの複数読み込み
;
;[ Infomation ]
; p_fname : ファイル名
; p_sid   : サウンドID初期値
; p_num   : 登録個数
;
;[ comment ]
; "ファイル名"で指定されたファイルをサウンドデータとして登録します。
; p_sid~(p_sid + p_num - 1)の範囲でサウンドIDを登録します。
; mmload 命令と同じ動作内容です。
;
; mmMPlay命令を使って再生してください。
;
; リピート再生は行いません。
; 失敗すると stat に-1を返します。
; 成功すると、 (p_sid + p_num) を返します。
;
#deffunc mmMLoad str p_fname, int p_sid, int p_num
	; 引数チェック
	if p_fname = "" : return 1
	if p_sid <0     : return 1
	if p_num <= 0   : return 1

	; 読み込み
	repeat p_num
		mmload p_fname, p_sid + cnt
	loop

	return (p_sid + p_num)


;-----------------------------------------------------------
;	サウンドデータの複数再生
;
;[ Infomation ]
; p_sid   : サウンドID初期値
; p_num   : 登録個数
;
;[ comment ]
; p_sidで指定した複数のサウンドIDを再生します。
; 同じサウンドを止めずに多重再生することができます。
; p_num で指定した登録個数まで多重再生できます。
; 再生数が p_num 個を超えた場合は、一番古いサウンドが停止されて新たに再生されます。
;
; 必ず mmMLoad 命令で読み込んだサウンドIDを使用してください。
; mmplay 命令と同じ動作内容です。
;
; 失敗すると stat に1を返します。成功時は0を返します。
;
#deffunc mmMPlay int p_sid, int p_num
	; 引数チェック
	if p_sid <0     : return 1
	if p_num <= 0   : return 1

	; 再生していないサウンドIDを探して再生
	f = 0
	repeat p_num
		id = p_sid + cnt
		mmstat s, id, 16
		if s = 0 {
			mmplay id
			f = 1
			break
		}
	loop
	; すべて再生中だった場合
	; 再生時間が一番経過しているサウンドIDを使用して再生します。
	; 使用されたサウンドIDは再生が中断されて最初から再生されます。
	if f = 0 {
		pmax  = 0.0
		idold = p_sid
		repeat p_num
			id = p_sid + cnt
			mmstat p, id, $100
			if p > pmax {
				pmax  = p
				idold = id
			}
		loop
		mmplay idold
	}
	return 0

;-----------------------------------------------------------
;	複数のサウンドデータの音量設定
;
;[ Infomation ]
; p_sid   : サウンドID初期値
; p_num   : 登録個数
; p_vol   : 音量(-10000~0)
;
;[ comment ]
; p_sidで指定した複数のサウンドIDの音量(ボリューム)を設定します。
; 必ず mmMLoad 命令で読み込んだサウンドIDを使用してください。
; mmvol 命令と同じ動作内容です。
;
; 失敗すると stat に1を返します。
;
#deffunc mmMVol int p_sid, int p_num, int p_vol
	; 引数チェック
	if p_sid <0     : return 1
	if p_num <= 0   : return 1
	vol = limit( p_vol, -10000, 0 )
	
	repeat p_num
		mmvol p_sid + cnt, vol
	loop

	return 0

#global

;=================================================
; ボタンを押すと音声が再生されます。
; 連打しても音が重なってい再生されます。

#include "hgimg4.as"

; ファイル一覧を取得
mes "フォルダ:" + dir_tv
fname  = dir_tv + "se_bom.wav"
fname2 = dir_tv + "se_block2.wav"


; 音声登録
numSE  = 10	; 重複再生できる音の数
numSE2 = 10
idSE  = 0    : mmMLoad fname , idSE , numSE
idSE2 = stat : mmMLoad fname2, idSE2, numSE2

; 音量調整
mmMVol idSE , numSE,  -500
mmMVol idSE2, numSE2, -500

button gosub "se_bom", *l_play1
button gosub "se_block2", *l_play2
redraw 1
stop

*l_play1
	mmMPlay idSE , numSE
	return

*l_play2
	mmMPlay idSE2, numSE2
	return

 ボタンを連打しても、再生が止まらずにちゃんと音が重なって再生出来ています。 「se_block2」が特にわかりやすくて、最後のチャリチャリという音までちゃんと聞こえています。

まとめ

 HSP3で音声再生を行う場合は、 hgimg4ならmm系命令、 hgimg3ならdmm系またはmm系命令のどちらかを使うことになります。 ただしOGGを使うならdmm系です。

 プラグイン、ファイル形式、重複再生、ボリューム調整、特に凝ったことをしないのであれば、このあたりに注意しておけば良さそうです。