|
 |
|
2025/8/29(Fri) 17:44:50|NO.103884
こんにちわ。
XAudio2でwavを再生するモジュールを作ってみました。
mmplayなどに比べ「DLL不要、多重再生、途中ループ、音量、パン、ピッチ、フィルター」が魅力かと思います。
Win10以降で動くと思います。("XAudio2_9.dll"標準で搭載されている環境)
なるべくシンプルにしたので、ちょっと無駄があるかもしれませんが、
もっとこうする方が良い、これは良くないなどご意見頂けると嬉しいです。
改変、組み込みもご自由にどうぞ。
※wavの途中ループはWavosaurなどのsmplチャンクを編集できるツールで保存。
;===============================================================================
; 簡易XAudioモジュール v1.0 author: usagi file: ezXAudio.hsp
;===============================================================================
#ifndef __EZXAUDIO__
#define global __EZXAUDIO__
#module "ezXAudio"
; DLL
#uselib "msvcrt.dll"
#cfunc malloc "malloc" int
#func free "free" int
#uselib "kernel32.dll"
#func VirtualProtect "VirtualProtect" var,int,int,var
#uselib "XAudio2_9.dll"
#func XAudio2Create "XAudio2Create" var,int,int
; COM
#define IID_IXAudio2 "{2B02E3CF-2E0B-4ec3-BE45-1B2A3FE7210D}"
#usecom IXAudio2 IID_IXAudio2
#comfunc CreateSourceVoice 5 var,var,int,float,int,int,int
#comfunc CreateSubmixVoice 6 var,int,int,int,int,int,int
#comfunc CreateMasteringVoice 7 var,int,int,int,int,int,int
#comfunc StartEngine 8
#comfunc StopEngine 9
; Dummy Function
#uselib ""
#cfunc float "" float
#func __GetVoiceDetails "" int, var
#func __SetFilterParameters "" int, var, int
#func __SetVolume "" int, float, int
#func __SetOutputMatrix "" int,int,int,int,var,int
#func __DestroyVoice "" int
#func __Start "" int, int, int
#func __Stop "" int, int, int
#func __SubmitSourceBuffer "" int, int, int
#func __FlushSourceBuffers "" int
#func __ExitLoop "" int, int
#func __GetState "" int, var, int
#func __SetFrequencyRatio "" int, float, int
; Overwrite Function
#deffunc local overwriteFunction int _libptr, int _pp, int _index
dupptr p, _pp, 4 : dupptr pfuncs, p, (_index+1)*4 : dupptr _sdat, _libptr, 28 : _sdat.6 = pfuncs._index : return
#define ctype def(%1,%2,%3=_p) overwriteFunction libptr(%2),%3,%1:%2 %3
; ------------------
#deffunc local _GetVoiceDetails int _p, var _details
def( 0, __GetVoiceDetails), _details : return stat
#deffunc local _SetFilterParameters int _p, var _filterParams
def( 8, __SetFilterParameters ), _filterParams, 0 : return stat
#deffunc local _SetVolume int _p, double _vol
def(12, __SetVolume), _vol,0 : return stat
#deffunc local _SetOutputMatrix int _p, int _srcCh, int _dstCh, var _lvMtx
def(16, __SetOutputMatrix), 0,_srcCh,_dstCh,_lvMtx,0 : return stat
#deffunc local _DestroyVoice int _p
def(18, __DestroyVoice) : return stat
#deffunc local _Start int _p
def(19, __Start), 0, 0 : return stat
#deffunc local _Stop int _p
def(20, __Stop), 0, 0 : return stat
#deffunc local _SubmitSourceBuffer int _p, int _pBuffer
def(21, __SubmitSourceBuffer), _pBuffer, 0 : return stat
#deffunc local _FlushSourceBuffers int _p
def(22, __FlushSourceBuffers) : return stat
#deffunc local _ExitLoop int _p
def(24, __ExitLoop), 0 : return stat
#deffunc local _GetState int _p, var _state
def(25, __GetState), _state, 0 : return stat
#deffunc local _SetFrequencyRatio int _p, double _ratio
def(26, __SetFrequencyRatio), _ratio, 0 : return stat
; ------------------
#deffunc local machinecode int _p, array _code
VirtualProtect _code, varsize(_code),$40, _ : dupptr _sdat, _p, 28 : _sdat.6 = varptr(_code) : return
#defcfunc local getInputChannels int _p
res = 0,0,0,0 : _GetVoiceDetails _p, res : return res.2
#defcfunc local getInputSampleRate int _p
res = 0,0,0,0 : _GetVoiceDetails _p, res : return res.3
#defcfunc local getStateBuffersQueued int _p
res = 0,0,0,0 : _GetState _p, res : return res.1
#defcfunc local getStateSamplesPlayed int _p
res = 0,0,0,0 : _GetState _p, res : d = 0.0 : lpoke d,0,res.2 : lpoke d,4,res.3 : return double(strf("%I64u",d))
#defcfunc local checkID var _data, str _identifier, int _offset
mref identifier, 65 : identifier = " " : lpoke identifier, 0, lpeek(_data, _offset) : return identifier == _identifier
#defcfunc local dupChunkData var _dup, var _data, int _offset
size = lpeek(_data, _offset + 4) : dupptr _dup, varptr(data) + offset + 8, size : return offset + 8 + size
; Once
#deffunc local call_once
__float = $0424448b, $c3909090 : machinecode libptr(float), __float ; mov 0x4(%esp),%eax ; ret
xaInit 1024, 2, 0, 0
return
;===============================================================================
; コマンド
;===============================================================================
; 初期化
;-----------------------------
#deffunc xaInit int _max, double _freqRatio, int _channels, int _sampleRate
if m_pXAudio != 0 { xaDeinit }
; Reset
m_voiceMax = _max
m_freqRatio = limitf(_freqRatio, 1.0/1024.0, 1024.0)
dim m_pSourceVoice, m_voiceMax
dim m_pAudioData, m_voiceMax
dim m_pAudioDataSize, m_voiceMax
dim m_loopBegin, m_voiceMax
dim m_loopLength, m_voiceMax
dim m_loopCount, m_voiceMax
; XAudio2 Start
XAudio2Create m_pXAudio, 0, 1
newcom m_XAudio, IID_IXAudio2, -1, m_pXAudio
if stat != 0 : return -1
CreateMasteringVoice m_XAudio, m_pMasteringVoice, _channels, _sampleRate, 0, 0,0,0
if stat != 0 : return -1
return 0
; 読み込み
;-----------------------------
#deffunc xaLoad str _file, int _id, int _count, int _flags
flags = 0
if _flags & 1 : flags |= 0x0002 ; *XAUDIO2_VOICE_NOPITCH
if _flags & 2 : flags |= 0x0008 ; *XAUDIO2_VOICE_USEFILTER
if _id >= m_voiceMax : return -1
xaDel _id
xaLoop _id, 0, 0, _count
exist _file : sdim data, strsize : bload _file, data
if (checkID(data,"RIFF", 0) && checkID(data,"WAVE", 8)) = 0 : return -1
offset = 12
repeat -1
if offset >= varsize(data) : break
if checkID(data,"fmt ", offset) {
offset = dupChunkData(fmtChunk, data, offset)
continue
}
if checkID(data,"data", offset) {
offset = dupChunkData(dataChunk, data, offset)
continue
}
if checkID(data,"smpl", offset) {
offset = dupChunkData(smplChunk, data, offset)
m_loopBegin._id = smplChunk.11
m_loopLength._id = smplChunk.12 - smplChunk.11
continue
}
offset = dupChunkData(_, data, offset)
loop
m_pAudioDataSize._id = varsize(dataChunk)
m_pAudioData._id = malloc(m_pAudioDataSize._id)
dupptr audioData, m_pAudioData._id, m_pAudioDataSize._id
memcpy audioData, dataChunk, m_pAudioDataSize._id
CreateSourceVoice m_XAudio, m_pSourceVoice._id, fmtChunk, flags, m_freqRatio,0,0,0
if stat != 0 : return -1
xaPan _id, 0
return 0
; 削除
;-----------------------------
#deffunc xaDel int _id
if m_pAudioData._id != 0 {
free m_pAudioData._id
m_pAudioDataSize._id = 0
m_pAudioData._id = 0
}
if m_pSourceVoice._id != 0 {
_DestroyVoice m_pSourceVoice._id
m_pSourceVoice._id = 0
}
return
#deffunc xaDelAll
repeat m_voiceMax : xaDel cnt : loop
return
; 再生
;-----------------------------
#deffunc xaPlay int _id, int _mode
xaStop _id
if m_pAudioDataSize._id == 0 : return
xaudio2_buffer.0 = 0x40, m_pAudioDataSize._id, m_pAudioData._id, 0, 0
xaudio2_buffer.5 = m_loopBegin._id, m_loopLength._id, m_loopCount._id, 0
_SubmitSourceBuffer m_pSourceVoice._id, varptr(xaudio2_buffer)
_Start m_pSourceVoice._id
return stat
; 停止
;-----------------------------
#deffunc xaStop int _id
if m_pSourceVoice._id == 0 : return
_Stop m_pSourceVoice._id : _FlushSourceBuffers m_pSourceVoice._id
return stat
#deffunc xaStopAll
repeat m_voiceMax : xaStop cnt : loop
return
; ポーズ
;-----------------------------
#deffunc xaPause int _id
if m_pSourceVoice._id == 0 : return
_Stop m_pSourceVoice._id
return stat
; リジューム
;-----------------------------
#deffunc xaResume int _id
if m_pSourceVoice._id == 0 : return
_Start m_pSourceVoice._id
return stat
; ループ
;-----------------------------
#deffunc xaLoop int _id, int _st, int _ed, int _count
m_loopBegin._id = _st : m_loopLength._id = _ed - _st
if _count < 0 { m_loopCount._id = 255 } else { m_loopCount._id = limit(_count, 0, 255) }
return stat
#deffunc xaExitLoop int _id
if m_pSourceVoice._id == 0 : return
_ExitLoop m_pSourceVoice._id
return stat
; 音量
;-----------------------------
#deffunc xaVol int _id, double _vol
if m_pSourceVoice._id == 0 : return
_SetVolume m_pSourceVoice._id, _vol
return stat
; 周波数
;-----------------------------
#deffunc xaFreq int _id, double _ratio
if m_pSourceVoice._id == 0 : return
_SetFrequencyRatio m_pSourceVoice._id, _ratio
return stat
; 定位
;-----------------------------
#deffunc xaPan int _id, double _pan
if m_pSourceVoice._id == 0 : return
srcCh = getInputChannels(m_pSourceVoice._id)
dstCh = getInputChannels(m_pMasteringVoice)
; TODO: 現状ステレオのリニアパンニングにだけ対応
if dstCh = 2 {
l = limitf(0.5 - _pan * 0.5, 0, 1)
r = 1.0 - l
if srcCh = 1 {
; L_IN
;-------------------
lvMtx.0 = float(l) ; L_OUT
lvMtx.1 = float(r) ; R_OUT
_SetOutputMatrix m_pSourceVoice._id, srcCh, dstCh, lvMtx
}
if srcCh = 2 {
; L_IN : R_IN
;-----------------------------
lvMtx.0 = float(l), float(0) ; L_OUT
lvMtx.2 = float(0), float(r) ; R_OUT
_SetOutputMatrix m_pSourceVoice._id, srcCh, dstCh, lvMtx
}
}
return
; フィルター tyep: enum { LowPass, BandPass, HighPass, Notch }
;-----------------------------
#deffunc xaFilter int _id, int _type, double _freq, double _oneOverQ
if m_pSourceVoice._id == 0 : return
filterParams = _type, float(_freq), float(limitf(_oneOverQ, 0, 1.5))
_SetFilterParameters m_pSourceVoice._id, filterParams
return
; 情報
;-----------------------------
#defcfunc xaStat int _id, int _mode
if m_pSourceVoice._id == 0 : return
; TODO: 種類を増やす、番号整理(もしくは小分けにする)
if _mode = 4 : return getInputChannels(m_pSourceVoice._id)
if _mode = 5 : return getInputSampleRate(m_pSourceVoice._id)
if _mode = 16 : return getStateBuffersQueued(m_pSourceVoice._id)
if _mode = 17 : return getStateSamplesPlayed(m_pSourceVoice._id)
return
; 破棄
;-----------------------------
#deffunc xaDeinit onexit
if m_pXAudio = 0 : return
StopEngine m_XAudio
repeat m_voiceMax : xaDel cnt : loop
_DestroyVoice m_pMasteringVoice : m_pMasteringVoice = 0
delcom m_XAudio : m_pXAudio = 0
return
#global
call_once@ezXAudio
#endif
;===============================================================================
; テスト
xaLoad dir_tv+"se_block2.wav", 0, -1, 2 : xaPlay 0
xaLoad dir_tv+"se_shot.wav" , 1
xaLoad dir_tv+"se_bom.wav" , 2
*MAIN
vol = 1.0 - (double(mousey)/ginfo_sy)
freq= vol
pan = (double(mousex)/ginfo_sx)*2.0 - 1.0
title strf("pan=%f, freq=%f, stat=%f", pan, freq, xaStat(0, 16))
xaFilter 0, 0, freq, 1
xaPan 0, pan
getkey key, 51 : if key { xaVol 0, freq }
getkey key, 52 : if key { xaFreq 0, freq }
stick pad
if pad & 256 : xaPlay 0
if pad & 512 : xaStop 0
getkey key, 49 : if key { xaPlay 1 }
getkey key, 50 : if key { xaPlay 2 }
cmp = ginfo_wx1<ginfo_mx & ginfo_mx<ginfo_wx2 & ginfo_wy1<ginfo_my & ginfo_my<ginfo_wy2
if cmp : xaResume 0 : else :xaPause
await 16
goto *MAIN

| |
|
2025/8/29(Fri) 19:31:24|NO.103885
HSPしか分からない自分にはXAudio2とか理解できないので扱えちゃうのが凄いです。
usagiさんの技術力とフリーで公開しちゃう心の広さに脱帽です。
|
|
2025/8/31(Sun) 23:47:53|NO.103901
すごいのだ!! 研究素材として使用させて頂きます。
この手あまり反応ない気がしますが、HSPはこの部分(サウンド周り)あまり強くないんで
もう少し拡充してほしいと思ってたりします。
そう思ってうちも独自開発して追加しようと思ってるんですが、なかなかリリースできないのだ。
HSPOGGの周波数変更については要望だしてありますが、これが追加されるだけで
できることがかなり広がると思われます。
|
|
2025/9/3(Wed) 16:25:07|NO.103927
DLL不要で、途中のループ再生や音量、パンなどを扱えるのは大きな利点です。通常、こういった機能は実装が最も難しい部分ですからね。シンプルさを保つという設計思想も素晴らしいですし、他の人も採用して発展させやすいでしょう。素晴らしい仕事です!
https://ragdollhitstickman.io
|
|
2025/9/5(Fri) 11:55:25|NO.103939
みなさんコメントありがとうございます。
>名無しさん
とんでもないです。私も研究目的なので至らない所があり、指摘いただけると嬉しいです。
>窓月ららさん
OGGのデコードも面白そうですね。自作音源開発されてましたね楽しみです。
>Nick Bender
難しい処理は全部XAudioがしてくれるので楽でしたが、インターフェースから関数引っ張ってくるのが厄介でした。。。
|
|
2025/9/8(Mon) 08:42:10|NO.103958
ちょうどXAudio2について調べてるところだったので、すごく有難いです。
理解は全く追いついておりませんが、色々と実験したいので使わせていただきます。
ありがとうございます!
|
|
2025/9/20(Sat) 22:40:26|NO.104022
こちらコンテスト作品で使用させていただきました(NO.103884のスクリプト)
ogg再生ではできなかったことができるようになって助かりました
これからも使わせていただきます
ありがとうございました
|
|
2025/9/25(Thu) 08:26:51|NO.104026
>へびぃさん
ちょっとこれ私の魔改造が過ぎてしまったせいか分かりにくかったですよね。。。
個人的に反省してまして、手入れの面からももっとシンプルで制限してたものに書き直しを考えてます。(基本は標準命令のみで実現)
>レピッシュさん
作品プレイさせていただきました。
ゲーム自体も素晴らしいですが、UIやチュートリアルの作りこみがスゴイですね!
こんなしっかりとしたゲームで使っていただき感激です。わたし自身も使用に耐えうるか実験中だったので参考になりました。
|
|
2025/9/25(Thu) 08:52:16|NO.104027
こんにちわ。今までの反省を生かして書き直してみました。(ちょっと誤字も修正)
関数ポインタ書き換えたり、機械語使ったり、ぱっと見分からない所が多かったので、
Xaudio2のdllとcom以外はシンプルに標準命令で出来るだけ記載しました。
ついでに、同じwavの同時再生数を増やす目的として参照再生(メモリ増えない)と、音ゲー用途などでシーク再生できるようにしました。
あと、よくゲームのメニューでBGMやSEの音量調整あると思うので、グループ音量機能付けました。
これくらいがミニマムな機能として丁度いいかなぁ。。。と思いつつ。
良くない所や改善点あったらご指摘いただければと思います。
;===============================================================================
; XAudio2再生モジュール author: usagi file: ezXAudio.hsp
; シンプル命令,WAV複製再生可能,機械語&特殊マクロを使わない バージョン
;===============================================================================
#ifndef __EZXAUDIO__
#define global __EZXAUDIO__
; WAVEモジュール変数
#module mod_wave m_wav, m_buffer
#defcfunc local getChunkID var _data, int _offset
id = " " : lpoke id, 0, lpeek(_data, _offset) : return id
#defcfunc local dupChunkData var _dup, var _data, int _offset
size = lpeek(_data, _offset + 4) : dupptr _dup, varptr(_data) + _offset + 8, size : return _offset + 8 + size + (size\2)
#modinit str _file
mref i, 2
m_buffer = /*xaudio2_buffer*/0x0040, 0,0, 0,0, 0,0,0/*8:fmt_size, 9:fmt_ptr*/,0,0
exist _file : sdim m_wav, strsize : bload _file, m_wav
if getChunkID(m_wav, 0)!="RIFF" || getChunkID(m_wav, 8)!="WAVE" : return -1
offset = 12
do
chunkID = getChunkID(m_wav, offset)
offset = dupChunkData(chunk, m_wav, offset)
switch chunkID
case "fmt " : m_buffer.8 = varsize(chunk), varptr(chunk) : swbreak
case "data" : m_buffer.1 = varsize(chunk), varptr(chunk) : swbreak
case "smpl" : if length(chunk)>12 { m_buffer.5 = chunk.11, chunk.12 - chunk.11, 255 } : swbreak
swend
until offset >= varsize(m_wav)
return i
#modcfunc local pBuff
return varptr(m_buffer)
#modcfunc local pFmt
return m_buffer.9
#global
; 本体
;===============================================================================
#module "ezXAudio"
; DLL と COM
#uselib "XAudio2_9.dll"
#func XAudio2Create "XAudio2Create" var,int,int
#define IID_IXAudio2 "{2B02E3CF-2E0B-4ec3-BE45-1B2A3FE7210D}"
#usecom IXAudio2 IID_IXAudio2
#comfunc CreateSourceVoice 5 var,int,int,float,int,int,int
#comfunc CreateSubmixVoice 6 var,int,int,int,int,int,int
#comfunc CreateMasteringVoice 7 var,int,int,int,int,int,int
#comfunc StopEngine 9
; Floatのキャスト
#defcfunc d2f double _d
f = _d * BIAS : return (lpeek(f, 4)&0x80000000) | (lpeek(f, 4)<<3&$7fffffff) | (lpeek(f)>>29&7)
#defcfunc f2d int _f
t = 0.0 : lpoke t, 4, (_f&$80000000) | ((_f&$7fffffff)>>3) : lpoke t, 0, _f<<29 : return t*BIAS
; インターフェースメソッド呼び出し
#defcfunc local callInterfaceFunc array args, int _funcidx, int _argnum
dupptr p, args.0, 4 : dupptr pfuncs, p, _funcidx*4+4
return callfunc(args, pfuncs._funcidx, _argnum)
#deffunc local SetOutputVoices var _p, int _pSendList
args = _p, _pSendList
return callInterfaceFunc(args, 1, 2)
#deffunc local SetVolume var _p, double _vol
args = _p, d2f(_vol), 0
return callInterfaceFunc(args, 12, 3)
#deffunc local SetOutputMatrix var _p, int _srcCh, int _dstCh, var _lvMtx
args = _p, 0, _srcCh, _dstCh, varptr(_lvMtx), 0
return callInterfaceFunc(args, 16, 6)
#deffunc local DestroyVoice var _p
args = _p
res = callInterfaceFunc(args, 18, 1)
if res == 0 { _p = 0 }
return res
#deffunc local StartVoice var _p
args = _p, 0, 0
return callInterfaceFunc(args, 19, 3)
#deffunc local StopVoice var _p
args = _p, 0, 0
return callInterfaceFunc(args, 20, 3)
#deffunc local SubmitSourceBuffer var _p, int _pBuf
args = _p, _pBuf, 0
return callInterfaceFunc(args, 21, 3)
#deffunc local FlushSourceBuffers var _p
args = _p
return callInterfaceFunc(args, 22, 1)
#deffunc local SetFrequencyRatio var _p, double _ratio
args = _p, d2f(_ratio), 0
return callInterfaceFunc(args, 26, 3)
#defcfunc local GetInputChannels int _p
args = _p, varptr(res) : res = 0,0,0,0
res = callInterfaceFunc(args, 0, 2)
return res.2
#defcfunc local GetBuffersQueued int _p
args = _p, varptr(res), 0x100 : res = 0,0,0,0
res = callInterfaceFunc(args, 25, 3)
return res.1
#defcfunc local GetSamplesPlayed int _p
args = _p, varptr(res), 0 : res = 0,0,0,0
res = callInterfaceFunc(args, 25, 3)
return res.2
; 定数
#define DEF_SMPLRATE 48000 ; マスター再生周波数
#define DEF_CHANNEL 2 ; マスターチャンネル数
#define DEF_MAXFREQ 8 ; 最大再生速度
;===============================================================================
; コマンド
;===============================================================================
; 初期化
;-----------------------------
#deffunc xaInit int _wavMax, int _groupMax
if m_pXAudio != 0 { xaDeinit }
dim m_pVoice, _wavMax
dim m_i , _wavMax : foreach m_i : m_i.cnt = -1 : loop
dimtype m_wave, 5
XAudio2Create m_pXAudio, 0, 1 : newcom m_XAudio, IID_IXAudio2, -1, m_pXAudio
CreateMasteringVoice m_XAudio, m_pMasterVoice, DEF_CHANNEL, DEF_SMPLRATE, 0,0,0,0
if stat != 0 : return -1
repeat _groupMax
CreateSubmixVoice m_XAudio, m_pGroupVoice.cnt, DEF_CHANNEL, DEF_SMPLRATE, 0,0,0,0
if stat != 0 : return -1
loop
return 0
; 破棄
;-----------------------------
#deffunc xaDeinit onexit
if m_pXAudio = 0 : return
StopEngine m_XAudio
xaDelAll
DestroyVoice m_pMasterVoice
foreach m_pGroupVoice : DestroyVoice m_pGroupVoice.cnt : loop
delcom m_XAudio : m_pXAudio = 0
return
; ★読込: ファイル名, ID
;-----------------------------
#deffunc xaLoad str _file, int _id
xaDel _id
newmod m_wave, mod_wave, _file : m_i._id = stat
CreateSourceVoice m_XAudio, m_pVoice._id, pfmt@mod_wave(m_wave(m_i._id)), 0, DEF_MAXFREQ, 0,0,0
xaPan _id, 0.0
return stat
; ★複製: ID, 複製元のID
;-----------------------------
#deffunc xaPoly int _id, int _dupId
m_i._id = m_i._dupId
CreateSourceVoice m_XAudio, m_pVoice._id, pfmt@mod_wave(m_wave(m_i._dupId)), 0, DEF_MAXFREQ, 0,0,0
xaPan _id, 0.0
return stat
; ★破棄: ID ※複製の場合も削除されます
;-----------------------------
#deffunc xaDel int _id
if m_pVoice._id != 0 { DestroyVoice m_pVoice._id }
if m_i._id != -1 { delmod m_wave(m_i._id) : m_i._id = -1 }
return
#deffunc xaDelAll
foreach m_pVoice : xaDel cnt : loop
return
; ★再生: ID
;-----------------------------
#deffunc xaPlay int _id
if varuse(m_wave(m_i._id)) == 0 : return -1
xaStop _id
SubmitSourceBuffer m_pVoice._id, pBuff@mod_wave(m_wave(m_i._id))
StartVoice m_pVoice._id
return stat
; ★シーク再生: ID, 開始位置(サンプル数)
;-----------------------------
#deffunc xaSeek int _id, int _begin
if varuse(m_wave(m_i._id)) == 0 : return -1
xaStop _id
dupptr buff, pBuff@mod_wave(m_wave(m_i._id)), 32
buff.3 = _begin
SubmitSourceBuffer m_pVoice._id, varptr(buff)
buff.3 = 0
StartVoice m_pVoice._id
return stat
; ★停止: ID
;-----------------------------
#deffunc xaStop int _id
if m_pVoice._id == 0 : return -1
StopVoice m_pVoice._id
FlushSourceBuffers m_pVoice._id
return stat
; ★一時停止: ID
;-----------------------------
#deffunc xaPause int _id
if m_pVoice._id == 0 : return -1
StopVoice@ezXAudio m_pVoice._id
return stat
; ★再開: ID
;-----------------------------
#deffunc xaResume int _id
if m_pVoice._id == 0 : return -1
StartVoice m_pVoice._id
return stat
; ★音量: ID, 音量
;-----------------------------
#deffunc xaVol int _id, double _vol
if m_pVoice._id == 0 : return -1
SetVolume m_pVoice._id, _vol
return stat
; ★音程: ID, 比率
;-----------------------------
#deffunc xaFreq int _id, double _ratio
if m_pVoice._id == 0 : return -1
SetFrequencyRatio m_pVoice._id, _ratio
return stat
; ★定位: ID, パン
;-----------------------------
#deffunc xaPan int _id, double _pan
if m_pVoice._id == 0 : return -1
srcCh = GetInputChannels(m_pVoice._id)
dstCh = DEF_CHANNEL
if dstCh = 2 { ; ステレオのみ対応
l = limitf(0.5 - _pan * 0.5, 0, 1) : r = 1.0 - l ; リニア
l = sin(0.5*M_PI*l) : r = sin(0.5*M_PI*r) ; 等パワー
if srcCh = 1 {
lvMtx.0 = d2f(l)
lvMtx.1 = d2f(r)
}
if srcCh = 2 {
lvMtx.0 = d2f(l), d2f(0)
lvMtx.2 = d2f(0), d2f(r)
}
SetOutputMatrix m_pVoice._id, srcCh, dstCh, lvMtx
}
return
; ★再生中: ID
;-----------------------------
#defcfunc xaIsPlay int _id
if m_pVoice._id == 0 : return -1
return GetBuffersQueued(m_pVoice._id)
; ★再生時間: ID
;-----------------------------
#defcfunc xaTime int _id
if m_pVoice._id == 0 : return -1
return GetSamplesPlayed(m_pVoice._id)
; ★グループ: ID, グループID (-1マスター)
;-----------------------------
#deffunc xaGroup int _id, int _gId
if m_pVoice._id = 0 : return
if _gId < 0 {
SetOutputVoices m_pVoice._id, 0
} else {
sends = 0, m_pGroupVoice._gId
sendList = 1, varptr(sends)
SetOutputVoices m_pVoice._id, varptr(sendList)
}
return
; ★グループ音量: グループID, 音量
;-----------------------------
#deffunc xaGroupVol int _id, double _vol
if m_pGroupVoice._id == 0 : return -1
SetVolume m_pGroupVoice._id, _vol
return stat
; 起動時に一度だけ実行
#deffunc local call_once
BIAS = powf(2,$380) ; float, double 変換用のバイアス
xaInit 256, 16 ; 初期化
return
#global
call_once@ezXAudio
#endif
;===============================================================================
; ■□■ ここからテストプログラム ■□■
xaLoad dir_tv+"se_shot.wav", 0
xaGroup 0, 1
repeat 7, 1
xaPoly cnt, 0 ; 複製
xaGroup cnt, 1
loop
xaGroupVol 1, 0.4 ; グループ音量設定
xaLoad dir_tv+"se_bom.wav", 8
*MAIN
stick pad, 256
if pad=256 && interval<=0 {
spx.index = mousex : spy.index = mousey
pan = double(mousex)/ginfo_sx*2-1
vol = 0.9 + 0.1*(double(rnd(32768))/32767.0)
frq = 0.8 + 0.2*(double(rnd(32768))/32767.0)
xaPlay index
xaVol index, vol
xaPan index, pan
xaFreq index, frq
index++ : index \= 8
interval = 6
}
interval--
if pad=512 {
xaPlay 8
}
redraw 0 : color : boxf
color 255
repeat 8
if xaIsPlay(cnt) == 0 : continue
hsvcolor 20*cnt, 255, 255
r = xaTime(cnt)/200\20
circle spx.cnt-r, spy.cnt-r, spx.cnt+r, spy.cnt+r, 0
loop
redraw 1 : await 16
goto *MAIN

| |
|
2025/9/27(Sat) 17:56:25|NO.104039
>作品プレイさせていただきました。
>ゲーム自体も素晴らしいですが、UIやチュートリアルの作りこみがスゴイですね!
>こんなしっかりとしたゲームで使っていただき感激です
プレイありがとうございます
oggだと一時停止/再開がどうやってもずれてしまったり
xaFreqを使用してテンポを変える演出が可能になったりと
かなり助かりました
>わたし自身も使用に耐えうるか実験中だったので参考になりました。
有用なモジュールだと感じておりますので
検証などのできる協力はしていきたいと思っております
ということでNO.104027の最新のモジュールを組み込んで
検証してみました
BGM、SEの再生、xaFreqを使用した演出など
前回のモジュールでできていたことは一通り動作していました
グループ音量機能を使ってオプションでのBGM、SE音量調整を反映させたところ
こちらも問題なく動作いたしました
また、ogg→wavでexeファイルのサイズが5倍になったことから
XAudio2.9はxWMA再生に対応しているとのことだったので
xWMA再生できるように改造してexeファイルのサイズダウンもしました
xWMAに関してですがusagiさんの方で
wav以外の形式の実装の予定はありますでしょうか?
xWMAはXAudio2.8で非対応、XAudio2.9で復活という流れなので
またいつ非対応になるか読めないことと
C++でMediaFoundationを使用してmp3の再生をしているソースを参考に
こちらに組み込もうと頑張ってみましたが
C++の開発にしても、HSP側のDLL使用にしても
圧倒的に経験値が足りていないため、遅々として進まずといったところで
少し気になっております
|
|
2025/9/27(Sat) 22:56:24|NO.104042
とても発想の勉強になります!!
ランダム再生くらいの用途に考えてたのですが、マリオ的なテンポアップの表現に使えるとわ。
しかもxWMAまで対応なんて、すっすごい。。
実際に作品を作ると容量の問題が出てくるのですね。参考になります。
とく更新の予定は無いですが、ローカルではdllを使ってogg vorbisの再生と、wavのストリーム(コールバックは機械語使うことになるのでtimerなどで。。)はお遊びレベルでしか無いですが実験してみてます。
もし今後試してみるとしたらopusでしょうか。音質も圧縮も良いと感じますしライセンスも使いやすく、採用例から長く使えそうな気がしてます。
ただHSPだけで行うとすると時間はかかるかもですね。。。
Oggのコンテナは簡単に剥がせそうですが、デコード部分の難易度が高そうでした。
(私もC++はよく分かりません、むつかしい。。。)
|
|
2025/9/28(Sun) 15:26:20|NO.104045
容量については
サイズが5倍になったことに驚いて
勝手に焦ってしまったことなので
問題があるというつもりはございません
すいませんでした
別形式への対応の件も了解いたしました
今回、新しい知識に触れるいい機会を得ましたので
経験を積む意味でも
自分なりに頑張ってみたいと思います
ありがとうございました
|
|
2025/9/28(Sun) 17:16:10|NO.104047
1/4程度の圧縮でよければADPCM使うのもありかもしれませんね。
MediaFoundationちょっと面白そうだったので、まにゅある見ながらHSPに書き下ろしてみました。
メモリからの読み込みは資料が探せなかったのでまだ分かってないのですがファイルからなら簡単にできそうです。
wav,adpcm,mp3,wmaの読み込みは確認できましたが、ogg,opusはダメでした(メディアプレイヤーで再生できるので行けそうな気がするのでけど。。)
よろしかったらどうぞ。
#module
#uselib "Ole32.dll"
#func CoTaskMemFree "CoTaskMemFree" int
#uselib "Mfplat.dll"
#func MFStartup "MFStartup" int, int
#func MFShutdown "MFShutdown"
#func MFCreateMediaType "MFCreateMediaType" int
#func MFCreateWaveFormatExFromMFMediaType "MFCreateWaveFormatExFromMFMediaType" int, int, int, int
#uselib "Mfreadwrite.dll"
#func MFCreateSourceReaderFromURL "MFCreateSourceReaderFromURL" wptr, int, int
#func MFCreateSourceReaderFromByteStream "MFCreateSourceReaderFromByteStream" var, int, int
#define IID_IMFSourceReader "{70ae66f2-c809-4e4f-8915-bdcb406b7993}"
#usecom IMFSourceReader IID_IMFSourceReader
#comfunc GetCurrentMediaType 6 int, int
#comfunc SetCurrentMediaType 7 int, int, int
#comfunc ReadSample 9 int, int, int, int, int, int
#define IID_IMFMediaType "{44ae0fa8-ea31-4109-8d2e-4cae4997c555}"
#usecom IMFMediaType IID_IMFMediaType
#comfunc SetGUID 24 int, int
#define IID_IMFSample "{c40a00f2-b93a-4d80-ae8c-5a1c634f58e4}"
#usecom IMFSample IID_IMFSample
#comfunc ConvertToContiguousBuffer 41 int
#define IID_IMFMediaBuffer "{045fa593-8799-42b8-bc8d-8968c6453507}"
#usecom IMFMediaBuffer IID_IMFMediaBuffer
#comfunc Lock 3 int, int, int
#comfunc Unlock 4
#define IID_IMFByteStream "{ad4c1b00-4bf7-422f-9175-756693d9130d}"
#usecom IMFByteStream IID_IMFByteStream
#comfunc Write 12 var, int, var
; ★オーディオを読み込む: URL, formatコピー先, dataコピー先
;----------------------------------------------------------
#deffunc mfLoadAudio str _url, var _fmt, var _data
MFStartup /*MF_VERSION*/(0x2 << 16 | 0x0070), /*MFSTARTUP_NOSOCKET*/0x1
MFCreateSourceReaderFromURL _url, 0, varptr(pMFSourceReader)
if pMFSourceReader == 0 : MFShutdown : return -1
newcom MFSourceReader, IID_IMFSourceReader, -1, pMFSourceReader
; メディアタイプを設定
MF_MT_MAJOR_TYPE = $48eba18e,$4687f8c9,$740a11bf,$8f6af9c9 ; "{48eba18e-f8c9-4687-bf11-0a74c9f96a8f}"
MF_MT_SUBTYPE = $f7e34c9a,$471442e8,$29cb4bb7,$e5352cd7 ; "{f7e34c9a-42e8-4714-b74b-cb29d72c35e5}"
MFMediaType_Audio = $73647561,$00100000,$aa000080,$719b3800 ; "{73647561-0000-0010-8000-00aa00389b71}"
MFAudioFormat_PCM = $00000001,$00100000,$aa000080,$719b3800 ; "{00000001-0000-0010-8000-00aa00389b71}"
MFCreateMediaType varptr(pMFMediaType)
newcom MFMediaType, IID_IMFMediaType, -1, pMFMediaType
SetGUID MFMediaType, varptr(MF_MT_MAJOR_TYPE), varptr(MFMediaType_Audio)
SetGUID MFMediaType, varptr(MF_MT_SUBTYPE), varptr(MFAudioFormat_PCM)
SetCurrentMediaType MFSourceReader, /*MF_SOURCE_READER_FIRST_AUDIO_STREAM*/0xfffffffd, 0, pMFMediaType
delcom MFMediaType : pMFMediaType = 0
GetCurrentMediaType MFSourceReader, /*MF_SOURCE_READER_FIRST_AUDIO_STREAM*/0xfffffffd, varptr(pMFMediaType)
newcom MFMediaType, IID_IMFMediaType, -1, pMFMediaType
; フォーマット取得
MFCreateWaveFormatExFromMFMediaType pMFMediaType, varptr(pWF), varptr(cbSize), /*MFWaveFormatExConvertFlag_Normal*/0
dupptr _wf, pWF, cbSize , 2
dim _fmt, cbSize/4+1 : memcpy _fmt, _wf, cbSize
CoTaskMemFree pWF
; データ取得
sdim _data : data_size = 0
repeat -1
ReadSample MFSourceReader, /*MF_SOURCE_READER_FIRST_AUDIO_STREAM*/0xfffffffd, 0, 0, varptr(dwStreamFlags), 0, varptr(pMFSample)
if (dwStreamFlags) || (pMFSample == 0) { break }
newcom MFSample, IID_IMFSample, -1, pMFSample
ConvertToContiguousBuffer MFSample, varptr(pMFMediaBuffer)
newcom MFMediaBuffer, IID_IMFMediaBuffer, -1, pMFMediaBuffer
Lock MFMediaBuffer, varptr(pBuffer), 0, varptr(cbCurrentLength)
dupptr buf, pBuffer, cbCurrentLength
memexpand _data, data_size+cbCurrentLength
memcpy _data, buf, cbCurrentLength, data_size, 0
data_size += cbCurrentLength
Unlock MFMediaBuffer : dup buf, _dummy_
delcom MFMediaBuffer : pMFMediaBuffer = 0
delcom MFSample : pMFSample = 0
loop
delcom MFMediaType : pMFMediaType = 0
delcom MFSourceReader : pMFSourceReader = 0
MFShutdown
return 0
#global
; テスト
button "開く", *L_OPEN_AUDIO
stop
*L_OPEN_AUDIO
dialog ,16
if stat != 1: mes "キャンセル" : stop
rgbcolor $FFFFFF : boxf : color : pos 0, 32
mfLoadAudio refstr, fmt, data
if stat : mes "読み込み失敗" : stop
mes "----- format -----"
mes strf("wFormatTag : %d", fmt.0&$FF)
mes strf("nChannels : %d", fmt.0>>16)
mes strf("nSamplesPerSec : %d", fmt.1)
mes strf("nAvgBytesPerSec: %d", fmt.2)
mes strf("nBlockAlign : %d", fmt.3&$FF)
mes strf("wBitsPerSample : %d", fmt.3>>16)
ch = fmt.0>>16
rate = fmt.1
mes "----- data -----"
if ch = 1 {
scale = double(ginfo_sx) / (varsize(data)/2)
redraw 0 : pos 0, 256 : color 0,128,255
i = 0
repeat varsize(data)/2
l = wpeek(data, cnt*2)<<16>>16
line scale*i, 256.0 - 128.0*(double(l)/65535.0)
i++
if cnt\rate=0 : redraw 1 : wait 0 : redraw 0
loop
redraw 1
}
if ch = 2 {
scale = double(ginfo_sx) / (varsize(data)/4)
redraw 0 : pos 0, 256 : color 0,128,255
i = 0
repeat varsize(data)/4
l = wpeek(data, cnt*4)<<16>>16
line scale*i, 256.0 - 128.0*(double(l)/65535.0)
i++
if cnt\rate=0 : redraw 1 : wait 0 : redraw 0
loop
i = 0
pos 0, 384 : color 0,128,255
repeat varsize(data)/4
r = wpeek(data, cnt*4+2)<<16>>16
line scale*i, 384.0 - 128.0*(double(r)/65535.0)
i++
if cnt\rate=0 : redraw 1 : wait 0 : redraw 0
loop
redraw 1
}

| |
|
2025/9/30(Tue) 23:17:30|NO.104052
MediaFoundationのソースありがとうございます
私の方でも恐らく同じC++のソースをHSPに落とし込もうと
自分なりに頑張っていたのですが
「アクセス違反 C0000005」で落ちまくって困っていたところ
非常に参考になるソースで助かりました
本当にありがとうございました
|
|
2025/10/1(Wed) 08:17:16|NO.104053
とんでもないです。私も知らなかった事が知れて実験が楽しかったです。コメントありがとうございました。
今回もろもろはMSDNのマニュアルと、comなどはmingwのヘッダ参考にさせていただきました。
HSPは気軽に使えるのに、dllやcomまで使えてしまうので、結構色々できますよね。
私はゲームが好きなのですが、"気軽に少しだけ"サウンドにこだわった作品も増えるといいなぁという気持ちで投稿しました。
もっとこだわりたい場合はステキなライブラリが色々あると思いますので、それを使うのが良いと思ってます。
このスレッドは終了したいと思います。
最後におきまりですが、モジュールの使用は自己責任でお願いします。
エラーチェックなど最低限に省いているので、使い方によっては改造は必要かと考えてます。
※FXに関しては、使いにくいのとパラメタの参考が未確認なところがあったので削除しました。
以上ありがとうございました。
|
|
2025/10/2(Thu) 18:53:46|NO.104057
まだちょっとしか見てないのですが、
こういう情報は今後 XAudio2 を利用したいと考えた方々が参考になると思うので
大変貴重だと思われます。そこからまた新たなソフトが誕生すると思われます。
ありがとうございます。
|
|
2025/10/16(Thu) 16:20:54|NO.104101
MediaFoundationのメモリから読み込みですが時間ができたのでやってみました。
長くなってしまうので、一部抜粋です。前回のスクリプトに追加してみてくださいまし。
これでパックファイルとかから読み込めますね。
□必要なコマンド
#uselib "Shlwapi.dll"
#cfunc SHCreateMemStream "SHCreateMemStream" int, int
#define IID_IStream "{0000000c-0000-0000-c000-000000000046}"
#uselib "Mfplat.dll"
#func MFCreateMFByteStreamOnStream "MFCreateMFByteStreamOnStream" int, int
#uselib "Mfreadwrite.dll"
#func MFCreateSourceReaderFromByteStream "MFCreateSourceReaderFromByteStream" int, int, int
□スタートアップ部分の差し替えイメージ
MFStartup /*MF_VERSION*/(0x2 << 16 | 0x0070), /*MFSTARTUP_NOSOCKET*/0x1
; ファイル読み込み
exist _file : if strsize = -1 : return -1
sdim buf, strsize : bload _file, buf
; ストリームの作成とソースリーダーの作成
pIStream = SHCreateMemStream(varptr(buf), varsize(buf))
if pIStream = 0 : /*ここで開放もしてリターン*/ : return -2
newcom IStream, IID_IStream, -1, pIStream
MFCreateMFByteStreamOnStream pIStream, varptr(pMFByteStream)
newcom MFByteStream, IID_IMFByteStream, -1, pMFByteStream
MFCreateSourceReaderFromByteStream pMFByteStream, 0, varptr(pMFSourceReader)
※余談※
いまさらながらMSDNって言わないんですね。。。MSLearn?

| |
|