리듬게임

Beatmania IIDX ASIO Int24LSB 변환

inseul인슬 2024. 6. 13. 03:38

Konami는 Beatmania IIDX Lighting 기체와 Sound Voltex Valkiry 기체에 ASUS Xonar AE를 공식 채택 하고 있다.

문제는 SDVX는 ASIO를 원활히 지원하는 반면 IIDX은 반드시 Int24LSB 포맷으로 출력을 지원하는 기기만 ASIO를 이용할 수 있다.

 

ASIO caps_009.1.7z
0.07MB

 

 

여기서 불만이 생긴다. ASIO를 지원하는 좋은 오디오 인터페이스를 가지고 있음에도 포맷이 달라 IIDX에서는 이용할 수 없어야 하는 건가?

그래서 여러모로 방법을 연구해보았다.

 

먼저 IIDX의 오디오 레이턴시와 샘플을 알면 좋을 것이다.

친절하게도 ASUS Xonar AE 사서 연구한 일본인의 연구자료가 있었다.

 

inf_launch_ext/asio.md at master · darekasan/inf_launch_ext · GitHub

 

inf_launch_ext/asio.md at master · darekasan/inf_launch_ext

INFINITASの起動オプションをいじる. Contribute to darekasan/inf_launch_ext development by creating an account on GitHub.

github.com

 

매우 귀중한 자료다. 여기서 asio.md를 보면

私の環境(Xonar AE)では44.1khzで24bit(ASIOSTInt24LSB)でレイテンシが8ms(352sample)でした。サンプリングレートとビット深度が合っていれば問題ないらしい(要検証)。

 

저의 환경(Xonar AE)에서는 44.1khz로 24bit(ASIOSTInt24LSB)에서 레이턴시가 8ms(352sample)였습니다. 샘플링 레이트와 비트 심도가 맞으면 문제 없는 것 같습니다(확인 필요).

 

IIDX의 오디오 환경을 알았으니 이제 변환이 필요하다. 이를 위해 FlexASIO를 이용한다.

Releases · dechamps/FlexASIO (github.com)

 

Releases · dechamps/FlexASIO

A flexible universal ASIO driver that uses the PortAudio sound I/O library. Supports WASAPI (shared and exclusive), KS, DirectSound and MME. - dechamps/FlexASIO

github.com

 

FlexASIO/CONFIGURATION.md at master · dechamps/FlexASIO · GitHub

 

FlexASIO/CONFIGURATION.md at master · dechamps/FlexASIO

A flexible universal ASIO driver that uses the PortAudio sound I/O library. Supports WASAPI (shared and exclusive), KS, DirectSound and MME. - dechamps/FlexASIO

github.com

 

공식에서 제공한 FlexASIO Configuration에 따라 FlexASIO.toml을 아래와 같이 작성한다.

FlexASIO GUI로 작성하면 한글에서 문제가 생긴다.

backend = "Windows WASAPI"
bufferSizeSamples = 352
[input]
device = ""
channels = 0
sampleType = "Int24"
wasapiExplicitSampleFormat = false
suggestedLatencySeconds = 0.0

[output]
device = "Speakers(Focusrite USB Audio)"
wasapiExclusiveMode = true
wasapiAutoConvert = false
channels = 2
sampleType = "Int24"
wasapiExplicitSampleFormat = false
suggestedLatencySeconds = 0.0

 

 

여기서 [output]의 device는 작업 표시줄의 소리 아이콘을 눌러보면 그 목록을 알 수 있다. 출력하고 싶은 device의 이름을 적는다.

 

 

또는 스피커 속성에서 이름 부분과 컨트롤러 정보를 조합하면 된다. 이 경우 스피커(Focusrite USB Audio)가 된다.

 

여기까지 마쳤으면 VBAsioTest_1012로 FlexASIO를 통해 원하는대로 출력이 되는지 테스트를 한다.

안에 32비트와 64비트 버전이 있는데 운영체제 버전에 따라 실행한다.

VBAsioTest_1012.7z
0.32MB

 

ASIO: FlexASIO를 선택한다.

 

 

삐~ 하는 소리와 함께 정상 출력 되고 있음을 확인한다.

TESTINGS: FlexASIO (44100 Hz) (buffer: 352 smp = 8.0ms), ASIOSTInt24LSB, 8.0ms 를 반드시 확인한다.

 

 

그리고 Asio Caps에서도 Int24LSB, 352 samples, 44100 Hz를 반드시 확인한다.

 

이를 INFINITAS에 적용해보도록 한다.

 

GitHub - darekasan/inf_launch_ext: INFINITASの起動オプションをいじる

 

GitHub - darekasan/inf_launch_ext: INFINITASの起動オプションをいじる

INFINITASの起動オプションをいじる. Contribute to darekasan/inf_launch_ext development by creating an account on GitHub.

github.com

 

# INFINITASのレジストリ インストール先の取得に使う
$InfRegistry = "HKLM:\SOFTWARE\KONAMI\beatmania IIDX INFINITAS"

# ゲーム本体のパス 通常はレジストリから取得
#$InfPath = "C:\Games\beatmania IIDX INFINITAS\"
$InfPath = Get-ItemPropertyValue -LiteralPath $InfRegistry -Name "InstallDir"
$InfExe = Join-Path $InfPath "\game\app\bm2dx.exe"
$InfLauncher = Join-Path $InfPath "\launcher\modules\bm2dx_launcher.exe"
cd $InfPath | Out-Null

# bm2dxinf:// のレジストリ
$InfOpen = "HKCR:bm2dxinf\shell\open\command\"

# このスクリプトのフルパス
$ScriptPath = $MyInvocation.MyCommand.Path

# 設定ファイル
$ConfigJson = Join-Path $PSScriptRoot "config.json"

$Config = @{
    "Option"="0"
    "WindowWidth"="1280"
    "WindowHeight"="720"
    "WindowPositionX"="0"
    "WindowPositionY"="0"
    "Borderless"=$false
}

# ウィンドウスタイル(調べてもよくわかんなかった)
$WSDefault = 348651520
$WSBorderless = 335544320

# Win32API関数の定義
Add-Type @"
    using System;
    using System.Runtime.InteropServices;

    public class Win32Api {
        [DllImport("user32.dll")]
        public static extern int MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

        [DllImport("user32.dll")]
        public static extern int SetWindowLong(IntPtr hWnd, int nIndex, long dwLong);

        [DllImport("user32.dll")]
        public static extern long GetWindowLong(IntPtr hWnd, int nIndex);

        [DllImport("user32.dll")]
        internal static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);

        [DllImport("user32.dll")]
        internal static extern bool GetClientRect(IntPtr hwnd, out RECT lpRect);

        [StructLayout(LayoutKind.Sequential)]
		internal struct RECT
		{
			public int left, top, right, bottom;
        }
        
        // 外枠の大きさを考慮したウィンドウサイズ変更
        public static void MoveWindow2(IntPtr hndl, int x, int y, int w, int h, bool isBl){
            if(isBl){
                MoveWindow(hndl, x, y, w, h, true);
            }else{
                RECT cRect = new RECT();
                RECT wRect = new RECT();

                GetClientRect(hndl, out cRect);
                GetWindowRect(hndl, out wRect);

                int cWidth = cRect.right - cRect.left;
                int cHeight = cRect.bottom - cRect.top;

                int wWidth = wRect.right - wRect.left;
                int wHeight = wRect.bottom - wRect.top;

                int newW = w + (wWidth - cWidth);
                int newH = h + (wHeight - cHeight);

                MoveWindow(hndl, x, y, newW, newH, true);
            }

        }
        
    }
"@

function Save-Config() {
    $Config | ConvertTo-Json | Out-File -FilePath $ConfigJson -Encoding utf8
}

function Start-Exe($exe, $workDir, $arg){
    $info = New-Object System.Diagnostics.ProcessStartInfo
    $info.FileName = $exe
    $info.WorkingDirectory = $workDir
    $info.Arguments = $arg
    $info.UseShellExecute = $false

    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $info
    
    $p.Start() | Out-Null

    return $p
}

function Switch-Borderless($isBl){
    if ($isBl) {
        [Win32Api]::SetWindowLong($handle, -16, $WSBorderless) | Out-Null
    }else{
        [Win32Api]::SetWindowLong($handle, -16, $WSDefault) | Out-Null
    }
}


# 引数を指定しなかったときにレジストリ変更
if ([string]::IsNullOrEmpty($Args[0])) {
    New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT | Out-Null
    $val = Get-ItemPropertyValue -LiteralPath $InfOpen -Name "(default)"

    echo("currently command: " + $val)
    echo ""
    echo("script path: " + $ScriptPath)
    echo("game path: " + $InfPath)
    echo ""
    
    echo "0 : revert to default"
    echo "1 : set to this script path"
    echo "3 : copy script file to game directory and set to new script path (recommended)"
    $num = Read-Host "number->"

    switch ($num) {
        0 {
            $val = """${InfLauncher}"" ""%1"""
        }
        1 {
            $val = """powershell"" ""-file"" ""${ScriptPath}"" ""%1"""
        }
        3 {
            $NewScriptPath = Join-Path $InfPath "inf_launch_ext.ps1"
            Copy-Item $ScriptPath $NewScriptPath
            $val = """powershell"" ""-file"" ""${NewScriptPath}"" ""%1"""
        }
        Default {
            exit
        }
    }
    Set-ItemProperty $InfOpen -name "(default)" -value $val
    echo "done. Press enter key to exit."
    Read-Host
    exit
}

# ゲームを起動するためのもの ここから
# 設定ファイルを読み込む
if(Test-Path $ConfigJson){
    $Config = @{}
(ConvertFrom-Json (Get-Content $ConfigJson -Encoding utf8 -Raw )).psobject.properties | Foreach { $Config[$_.Name] = $_.Value }
}


# ゲーム本体に渡す引数
$InfArgs = ""

# 引数からトークンを拾う
$Args[0] -match "tk=(.{64})" | Out-Null
$InfArgs += " -t "+$Matches[1]

# トライアルモードなら--trialをつける
if ($Args[0].Contains("trial")) {
    $InfArgs += " --trial"
}

echo "Please select option."
echo "0 : Launcher"
echo "1 : Normal"
echo "2 : Normal + window mode"
echo "3 : ASIO"
echo "4 : ASIO + window mode"

$num = Read-Host "number(last time: $($Config["Option"]))"
if([string]::IsNullOrEmpty($num)){
    $num=$Config["Option"]
}

switch ($num) {
    0 {
        Start-Process -FilePath $InfLauncher -ArgumentList $Args[0]
        exit
    }
    1 {

    }
    2 {
        $InfArgs += " -w"
    }
    3 {
        $InfArgs += " --asio"
    }
    4 {
        $InfArgs += " -w"
        $InfArgs += " --asio"
    }
    Default {
        exit
    }
}

# 設定を保存
$Config["Option"] = [string]$num
Save-Config

# INFINITASを起動
$p = Start-Exe($InfExe,"",""""+$InfArgs+"""")

# ウィンドウモードのとき
if($InfArgs.Contains("-w")){
    # ウィンドウ作成まで待つ
    $p.WaitForInputIdle() | Out-Null

    # ウィンドウハンドルの取得
    $handle = $p.MainWindowHandle

    # 前回の位置と大きさにする
    Switch-Borderless($Config["Borderless"])
    [Win32Api]::MoveWindow2($handle, $Config["WindowPositionX"], $Config["WindowPositionY"], $Config["WindowWidth"], $Config["WindowHeight"], $Config["Borderless"])

    echo ""
    echo "window mode setting"
    echo "example:"
    echo "window size -> 1280x720"
    echo "window position -> 100,100"
    echo "Press enter key to switch to Borderless window."

    while($true){
        $inputStr=Read-Host " "
        if([string]::IsNullOrEmpty($inputStr)){
            $Config["Borderless"] = (-Not $Config["Borderless"])
        }elseif($inputStr.Contains("x")){
            $val = $inputStr.Split('x')
            $Config["WindowWidth"]=$val[0]
            $Config["WindowHeight"]=$val[1]
        }elseif($inputStr.Contains(",")){
            $val = $inputStr.Split(',')
            $Config["WindowPositionX"]=$val[0]
            $Config["WindowPositionY"]=$val[1]
        }

        # ボーダーレス化
        Switch-Borderless($Config["Borderless"])

        # 位置とサイズを反映
        [Win32Api]::MoveWindow2($handle, $Config["WindowPositionX"], $Config["WindowPositionY"], $Config["WindowWidth"], $Config["WindowHeight"], $Config["Borderless"])

        # 設定ファイルに書き込む
        Save-Config
    }
}

inf_launch_ext.ps1
0.01MB

 

inf_launch_ext로 돌아가서 inf_launch_ext.ps1을 C:\Games\beatmania IIDX INFINITAS 폴더에 넣어준다.

사전 준비는 아래 README를 따라하기 바란다.

 

inf_launch_ext/README.md at master · darekasan/inf_launch_ext · GitHub

 

inf_launch_ext/README.md at master · darekasan/inf_launch_ext

INFINITASの起動オプションをいじる. Contribute to darekasan/inf_launch_ext development by creating an account on GitHub.

github.com

 

 

그리고 레지스트리 편집기에서 HKEY_LOCAL_MACHINE\SOFTWARE\ASIO 에서

ASIO 우클릭 - 새로 만들기(N) - 키(K)를 생성해주고 XONAR SOUND CARD(64)로 지어준다.

 

다음으로 XONAR SOUND CARD(64)키에서 새로 만들기 - 문자열 값으로 CLSID와 Description을 만들어준다.

CLSID에는 FlexASIO의 값을 넣어주고 Description에는 XONAR SOUND CARD(64)를 입력한다.

위 사진에서 (기본값)은 무시해도 된다.

 

 

그럼 VBAsioTest에서 XONAR SOUND CARD(64)가 추가된 것을 볼 수 있다.

테스트를 진행하면 삐~ 하는 소리와 함께 정상 출력 되고 있음을 확인한다.

TESTINGS: XONAR SOUND CARD(64) (44100 Hz) (buffer: 352 smp = 8.0ms), ASIOSTInt24LSB, 8.0ms 를 반드시 확인한다.

 

 

그리고 Asio Caps에서도 Int24LSB, 352 samples, 44100 Hz를 반드시 확인한다.

 

 

inf_launch_ext를 이용하기 위해서는 web에서 게임을 실행하도록 해야한다.

 

 

런처를 시작하면 PowerShell 화면이 나오고 3번을 입력한다. 만약 창모드로 하고 싶다면 4번을 입력한다.

잘 작동하면 INFINITAS 적용이 끝난다.

'리듬게임' 카테고리의 다른 글

DJ DAO/GAMO2 Tasoller Firmware Update  (0) 2024.07.02
SDVX Valkyrie 홈케이드  (0) 2024.05.11
Beatmania IIDX Lightning 홈케이드  (0) 2024.05.11
LR2 곡 검색 바 이중 입력 문제  (0) 2024.01.01
Reflec Beat Serial Touchscreen Drivers  (0) 2023.12.27