|
 |
|
|
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?

| |
|
|
2025/11/25(Tue) 20:22:43|NO.104337
NO.104163-NO.104332の投稿は宣伝目的のスパム投稿でしょうか。
AI連続投稿攻撃、なんか怖いですね…。
|
|
|
2026/2/27(Fri) 22:13:01|NO.104767
こんにちわ。はい、おっしゃるとおりその関数を使えば実装できます。
Xaudio2fxもxaudioの実装とさほど変わらないので、COMや構造体が理解できていれば
youdaiさんはたしかMinGWを使われていたかと思うので、そのヘッダから必要な定数等を拾ってくれば実装できるかと思います。
流れとしてはVSTもご存知かと思いますので一般的なDAWのセンドリターンにインサートエフェクトをかけるイメージで、
プログラムでルーティングを組む感じです。
以前はこのスレにFX(メーターやリバーブ)のサンプルも載せていいたのですが、
先に書いてある通り削除させていただきました。
(シンプルにしたかった趣旨もありますし、エフェクト自体の概念が複雑なので。。。)
とは言え「さっぱり見当がつかない」との事なので、
拡張のお力になる程度ですがスクリプトを参考にどうぞ。
注意点として、リバーブは沢山種類があるわけではなく、
いわゆるシュレーダー方式のリバーブのようなものが1つあり
パラメーターの調整で音色を作ります。
そのそのプリセットがいろいろヘッダ等に定義されているという感じです。
(このサンプルではジェネリックな設定になってます)
軽いのでゲーム等に向いているエフェクトですね。
;===============================================================================
; 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
#func XAudio2CreateReverb "CreateAudioReverb" var,int
#func XAudio2CreateVolumeMeter "CreateAudioVolumeMeter" var,int
#define IID_IUnknown "{00000000-0000-0000-C000-000000000046}"
#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 SetEffectChain var _p, int _pEffectChain
args = _p, _pEffectChain, 0
return callInterfaceFunc(args, 2, 3)
#deffunc local EnableEffect var _p, int _index
args = _p, _index, 0
return callInterfaceFunc(args, 3, 3)
#deffunc local DisableEffect var _p, int _index
args = _p, _index, 0
return callInterfaceFunc(args, 4, 3)
#deffunc local SetEffectParameters var _p, int _index, var _param, int _size
args = _p, _index, varptr(_param), _size, 0
return callInterfaceFunc(args, 6, 5)
#deffunc local GetEffectParameters var _p, int _index, var _param, var _size
args = _p, _index, varptr(_param), varptr(_size)
return callInterfaceFunc(args, 7, 4)
#deffunc local SetFilterParameters var _p, array _filterParams
args = _p, varptr(_filterParams), 0
return callInterfaceFunc(args, 8, 3)
#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
; メーター取得用
dim m_peakLevels, DEF_CHANNEL
dim m_rmsLevels, DEF_CHANNEL
m_volumeMeterLevels = varptr(m_peakLevels), varptr(m_rmsLevels), DEF_CHANNEL
dimtype m_reverb, 6, _groupMax
dimtype m_meter, 6, _groupMax
;
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
flags = 0x0008 ; *XAUDIO2_VOICE_USEFILTER
repeat _groupMax
stage = 2147483647 - cnt
CreateSubmixVoice m_XAudio, m_pGroupVoice.cnt, DEF_CHANNEL, DEF_SMPLRATE, flags, stage, 0,0
if stat != 0 : return -1
m_pReverb.cnt = 0 : m_pMeter.cnt = 0
CreateGroupFx cnt
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 _gId, double _vol
if m_pGroupVoice._gId == 0 : return -1
SetVolume m_pGroupVoice._gId, _vol
return stat
; ★グループフィルター: グループID, タイプ{ LowPass, BandPass, HighPass, Notch }, 周波数, Q
;-----------------------------
#deffunc xaGroupFilter int _gId, int _type, double _freq, double _q/*1/q*/
if m_pGroupVoice._gId == 0 : return -1
filterParams = _type, d2f(_freq), d2f(limitf(_q, 0, 1.5))
SetFilterParameters m_pGroupVoice._gId, filterParams
return stat
; ★グループ出力先設定: グループID, 送り先グループID... ※自分より若い番号にしか送れません。
;-----------------------------
#define global xaGroupSend(%1,%2=-1,%3=-1,%4=-1,%5=-1) _xaGroupSend %1,%2,%3,%4,%5
#deffunc _xaGroupSend int _gId, int _s1, int _s2, int _s3, int _s4
if m_pGroupVoice._gId = 0 : return
sendList = 0, 0 : count = 0 : i = 0
if _s1>-1 { sends.i = 0 : i++ : sends.i = m_pGroupVoice._s1 : i++ : count++ }
if _s2>-1 { sends.i = 0 : i++ : sends.i = m_pGroupVoice._s2 : i++ : count++ }
if _s3>-1 { sends.i = 0 : i++ : sends.i = m_pGroupVoice._s3 : i++ : count++ }
if _s4>-1 { sends.i = 0 : i++ : sends.i = m_pGroupVoice._s4 : i++ : count++ }
if count > 0 { sendList = count, varptr(sends)}
SetOutputVoices m_pGroupVoice._gId, varptr(sendList)
if stat != 0 : return -1
return
; ★ピークメーター: グループID, 音量返す変数
;-----------------------------
#deffunc xaPeakMeter int _gId, array _out
EnableEffect m_pGroupVoice._gId, 1
GetEffectParameters m_pGroupVoice._gId, 1, m_volumeMeterLevels, size
repeat DEF_CHANNEL : _out.cnt = f2d(m_peakLevels.cnt) : loop
return
; ★リバーブ: グループID, リバーブパラメータの構造体
;-----------------------------
#deffunc xaFxReverb int _gId, var paramNative
if m_pGroupVoice._gId = 0 : return
EnableEffect m_pGroupVoice._gId, 0
SetEffectParameters m_pGroupVoice._gId, 0, paramNative, 54
return
; エフェクト作成
;-----------------------------
#deffunc local CreateGroupFx int _id
if m_pReverb._id != 0 {
DisableEffect m_pSubmixVoice._id, 0 : delcom m_reverb._id : m_pReverb._id = 0
}
if m_pMeter._id != 0 {
DisableEffect m_pSubmixVoice._id, 1 : delcom m_meter._id : m_pMeter._id = 0
}
XAudio2CreateReverb m_pReverb._id, 0
newcom m_reverb._id, IID_IUnknown, -1, m_pReverb._id
XAudio2CreateVolumeMeter m_pVolumeMeter._id, 0
newcom m_meter._id, IID_IUnknown, -1, m_pMeter._id
effects = /*0*/ m_pReverb._id, 0, DEF_CHANNEL, /*1*/ m_pVolumeMeter._id, 0, DEF_CHANNEL
effectChain = 2, varptr(effects)
SetEffectChain m_pGroupVoice._id, varptr(effectChain)
return stat
; 起動時に一度だけ実行
#deffunc local call_once
BIAS = powf(2,$380) ; float, double 変換用のバイアス
xaInit 256, 16 ; 初期化
return
#global
call_once@ezXAudio
#endif
;===============================================================================
; ■□■ ここからテストプログラム ■□■
#enum SND_GROUP_MIX = 0 ; 出力ミックス
#enum SND_GROUP_REV ; 残響エフェクト
#enum SND_GROUP_SFX ; 効果音
#enum SND_CUE_SHOT = 0 ; 銃声
TBL_GROUP_ID = SND_GROUP_MIX, SND_GROUP_REV, SND_GROUP_SFX
TBL_GROUP_TEXT = "MIX", "REVERB", "SFX"
xaLoad dir_tv+"se_shot.wav", 0 : xaGroup SND_CUE_SHOT, SND_GROUP_SFX
; ★出力先の設定
xaGroupSend SND_GROUP_SFX, SND_GROUP_MIX, SND_GROUP_REV
xaGroupSend SND_GROUP_REV, SND_GROUP_MIX
; ★リバーブの設定
; ★パラメータの種類(プリセット)はご自分でお調べください。
param = $42c80000, $00000007, $0605050b, $0f1b1b06, $0704080f, $9c400006, $20000045, $800000c1, $d028f6bf, $000000c1, $beb85240, $c800003f, $c8000042, $00000042
xaFxReverb SND_GROUP_REV, param
*MAIN
redraw 0 : gmode 0 : color : boxf
stick pad, 256
if pad=256 && interval<=0 {
xaPlay SND_CUE_SHOT
p = double(mousex)/ginfo_winx*2-1.0
xaPan SND_CUE_SHOT, p
interval = 10
} interval--
f = double(mousey)/ginfo_winy
xaGroupFilter SND_GROUP_SFX, 0, f, 1
pos 0,64
foreach TBL_GROUP_ID
; ★ボリュームメーターの取得
xaPeakMeter TBL_GROUP_ID.cnt, peak
rgbcolor $ffffff: mes "◆"+TBL_GROUP_TEXT.cnt, 4
foreach peak
dB = limitf(20.0*(logf(peak.cnt)/logf(10)), -96) : amp = limitf(peak.cnt*96, 0, 120)
rgbcolor $333333: boxf ginfo_cx, ginfo_cy, ginfo_cx+ 96, ginfo_cy+15
rgbcolor $663333: boxf ginfo_cx+96, ginfo_cy, ginfo_cx+120, ginfo_cy+15
rgbcolor $00ee00: boxf ginfo_cx, ginfo_cy, ginfo_cx+amp, ginfo_cy+14
rgbcolor $ffffff: if dB<=-96 : mes "-∞",4 : else : mes strf("%+4.2f",dB), 4
loop
loop
redraw 1 : await 16
goto *MAIN

| |
|
|
2026/2/28(Sat) 17:38:03|NO.104771
>usagi さん
おお! ありがとうございます!
さっそくスクリプトを参考に勉強してみようと思います!
|
|
|
2026/2/28(Sat) 18:13:43|NO.104772
リバーブは奥が深いので。。。こんなの追加して
#module
#define global xaReverbDef(%1) %1 = $42c80000,$00000000,$06050500,$0f1b1b06,$0804080f,$9c400006,$00000045,$00000000,$00000000,$00000000,$00000000,$c8000040,$c8000042,$00000042
#define global xaReverbWetDryMix(%1,%2=100) lpoke %1, 0, d2f(%2) ; 出力割合 0~100%
#define global xaReverbReflectionsDelay(%1,%2=0) lpoke %1, 4, %2 ; 初期反射遅延 0~300ms
#define global xaReverbReverbDelay(%1,%2=0) poke %1, 8, %2 ; 後期反射遅延 0~85ms
#define global xaReverbRearDelay(%1,%2=5) poke %1, 9, %2 ; 背面出力遅延 0~5ms
#define global xaReverbSideDelay(%1,%2=5) poke %1, 10, %2 ; 側面出力遅延 0~5ms
#define global xaReverbPositionLeft(%1,%2=6) poke %1, 11, %2 ; リスナ位置左 0^30
#define global xaReverbPositionRight(%1,%2=6) poke %1, 12, %2 ; リスナ位置右 0^30
#define global xaReverbPositionMatrixLeft(%1,%2=27) poke %1, 13, %2 ; ソース-リスナ距離左 0^30
#define global xaReverbPositionMatrixRight(%1,%2=27) poke %1, 14, %2 ; ソース-リスナ距離右 0^30
#define global xaReverbEarlyDiffusion(%1,%2=15) poke %1, 15, %2 ; 初期反射拡散 0^15
#define global xaReverbLateDiffusion(%1,%2=15) poke %1, 16, %2 ; 後期反射拡散 0^15
#define global xaReverbLowEQGain(%1,%2=8) poke %1, 17, %2 ; 低周波減衰 0^12 (-8~+4dB)
#define global xaReverbLowEQCutoff(%1,%2=4) poke %1, 18, %2 ; 低周カットオフ 0^9 (50,100,150,200,250,300,350,400,450,500)
#define global xaReverbLowHighEQGain(%1,%2=8) poke %1, 19, %2 ; 高周波減衰 0^8 (-8~+0dB)
#define global xaReverbLowHighEQCutoff(%1,%2=6) poke %1, 20, %2 ; 高周カットオフ 0^14 (1,1.5,2,2.5,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8)
#define global xaReverbRoomFilterFreq(%1,%2=5000) lpoke %1, 21, d2f(%2) ; フィルタ周波数 20~20000Hz
#define global xaReverbRoomFilterMain(%1,%2=0) lpoke %1, 25, d2f(%2) ; フィルタ減衰 -100~0dB
#define global xaReverbRoomFilterHF(%1,%2=0) lpoke %1, 29, d2f(%2) ; フィルタ高周波減衰 -100~0dB
#define global xaReverbReflectionsGain(%1,%2=0) lpoke %1, 33, d2f(%2) ; 初期反射ゲイン-100~0dB
#define global xaReverbReverbGain(%1,%2=0) lpoke %1, 37, d2f(%2) ; 後期反射ゲイン -100~0dB
#define global xaReverbDecayTime(%1,%2=2) lpoke %1, 41, d2f(%2) ; 残響減衰時間 0.1~inf s
#define global xaReverbDensity(%1,%2=100) lpoke %1, 45, d2f(%2) ; モーダル密度 0~100%
#define global xaReverbRoomSize(%1,%2=100) lpoke %1, 49, d2f(%2) ; ルームサイズ 1~1ft
#define global xaReverbDisableLateField(%1,%2=0) poke %1, 53, d2f(%2) ; 遅延フィールド無効 0~1
#global
この二つのパラメーターだけでも結構遊べると思います。
★部屋とか
xaReverbDef param
xaReverbDecayTime param, 1
xaReverbRoomFilterMain param, -10
xaReverbRoomFilterHF param, -10
xaFxReverb SND_GROUP_REV, param
★洞窟とか
xaReverbDef param
xaReverbDecayTime param, 3
xaReverbRoomFilterMain param, -5
xaReverbRoomFilterHF param, -1
xaFxReverb SND_GROUP_REV, param

| |
|
|
2026/3/2(Mon) 01:06:36|NO.104778
じつはusagiさんのサンプルを参考に解析して自分なりに再構築しようとしています。
大変ありがたいです。
|
|
|
2026/3/2(Mon) 12:21:22|NO.104782
> usagi さん
これ、すごいですね!
xAudio2はここまでできるとなると、DTMのツールとしてそのまんま使えると思います。
しかもスクリプト制御もできるわけですから、すごい可能性を感じました。
|
|
|
2026/3/2(Mon) 15:40:27|NO.104783
DAWやってるけど、
HSPで,VSTが使える本格DAWがつくれるのかな?
レトロゲームミュージックが打ち込めるね。
使ってみたいね。
|
|
|
2026/3/2(Mon) 17:48:05|NO.104784
コメントありがとうございます。
>窓月ららさん
どんどん改造してくださいませ。
親しみやすいようmm系の置き換えの思想で書いたのですが、
もっとXAudio2を生かそうと思うと設計は変えた方がよさそうです。
何か面白い事ができたら教えていただけると嬉しいです。
>youdaiさん
いろいろできそうですよね。
私はHGIMG4向けのゲームに生かせればと思ってます。(標準サウンドだけだと3D系で少し機能が足りなくなるので)
コールバックが使えなので出力バッファ書き換えとか、
自作エフェクトとかは難しいですが、オーサリングしたサウンドデータを
好きな空間で、好きなタイミング再生したりなどは自由にできるかと考えてます。
>うしさん
VSTはC++向けみたいですからASIO出力のホストアプリとなるものをdllで作らないと難しいかもですね。
(あとこれライセンスいるのでは?)
レトロミュージックでしたら、下記スレが面白かったですよ。
直接サウンド出力バッファに書き込むので、FM音源もエフェクタもなんでも好きに作れます。
・ByteBeat
https://hsp.tv/play/pforum.php?mode=pastwch&num=99175
あわせてDAWでしたらMidiも使えるようにすると良いかもですね。
・Midi
https://hsp.tv/play/pforum.php?mode=pastwch&num=99555
|
|