OSDN Git Service

[Feature] BGMと効果音の音量設定機能
authorHabu <habu1010+github@gmail.com>
Sat, 5 Nov 2022 15:17:46 +0000 (00:17 +0900)
committerHabu <habu1010+github@gmail.com>
Sat, 12 Nov 2022 23:55:34 +0000 (08:55 +0900)
BGMと効果音の音量をメニューから設定できる機能を追加する。
それぞれ10%~100%の10%刻みで設定でき、設定内容はINIファイルに保存される。

src/ang_eng.rc
src/ang_jp.rc
src/game-option/runtime-arguments.cpp
src/game-option/runtime-arguments.h
src/main-win.cpp
src/main-win/main-win-menuitem.h
src/main-win/main-win-music.cpp
src/main-win/main-win-music.h
src/main-win/main-win-sound.cpp
src/main-win/main-win-sound.h

index 1aba632..ed2d351 100644 (file)
@@ -127,10 +127,36 @@ ANGBAND MENU
 
                MENUITEM SEPARATOR
                MENUITEM "&Music", 420
+               POPUP "Volume"
+               {
+                       MENUITEM "100%", 460
+                       MENUITEM "90%", 461
+                       MENUITEM "80%", 462
+                       MENUITEM "70%", 463
+                       MENUITEM "60%", 464
+                       MENUITEM "50%", 465
+                       MENUITEM "40%", 466
+                       MENUITEM "30%", 467
+                       MENUITEM "20%", 468
+                       MENUITEM "10%", 469
+               }
                MENUITEM "&Pause music when inactive", 421
                MENUITEM "&Open music folder", 422
                MENUITEM SEPARATOR
                MENUITEM "&Sound", 410
+               POPUP "Volume"
+               {
+                       MENUITEM "100%", 470
+                       MENUITEM "90%", 471
+                       MENUITEM "80%", 472
+                       MENUITEM "70%", 473
+                       MENUITEM "60%", 474
+                       MENUITEM "50%", 475
+                       MENUITEM "40%", 476
+                       MENUITEM "30%", 477
+                       MENUITEM "20%", 478
+                       MENUITEM "10%", 479
+               }
                MENUITEM "&Open sound folder", 411
                MENUITEM SEPARATOR
                MENUITEM "&HTML dump", 450
index 3cc8431..86e6b9d 100644 (file)
@@ -11,7 +11,7 @@ ANGBAND MENU
                MENUITEM SEPARATOR
                MENUITEM "\83X\83R\83A(&C)", 120
                MENUITEM "\83\80\81[\83r\81[(&M)", 121
-               MENUITEM SEPARATOR  
+               MENUITEM SEPARATOR
                MENUITEM "\8fI\97¹(&X)", 130
        }
 
@@ -126,10 +126,36 @@ ANGBAND MENU
                }
                MENUITEM SEPARATOR
                MENUITEM "BGM(&M)", 420
+               POPUP "\89¹\97Ê"
+               {
+                       MENUITEM "100%", 460
+                       MENUITEM "90%", 461
+                       MENUITEM "80%", 462
+                       MENUITEM "70%", 463
+                       MENUITEM "60%", 464
+                       MENUITEM "50%", 465
+                       MENUITEM "40%", 466
+                       MENUITEM "30%", 467
+                       MENUITEM "20%", 468
+                       MENUITEM "10%", 469
+               }
                MENUITEM "\83A\83N\83e\83B\83u\8e\9e\82Ì\82ÝBGM\8dÄ\90¶(&M)", 421
                MENUITEM "BGM\90Ý\92è\83t\83H\83\8b\83_\82ð\8aJ\82­(&O)", 422
                MENUITEM SEPARATOR
                MENUITEM "\8cø\89Ê\89¹(&S)", 410
+               POPUP "\89¹\97Ê"
+               {
+                       MENUITEM "100%", 470
+                       MENUITEM "90%", 471
+                       MENUITEM "80%", 472
+                       MENUITEM "70%", 473
+                       MENUITEM "60%", 474
+                       MENUITEM "50%", 475
+                       MENUITEM "40%", 476
+                       MENUITEM "30%", 477
+                       MENUITEM "20%", 478
+                       MENUITEM "10%", 479
+               }
                MENUITEM "\8cø\89Ê\89¹\90Ý\92è\83t\83H\83\8b\83_\82ð\8aJ\82­(&P)", 411
                MENUITEM SEPARATOR
                MENUITEM "\89æ\96Ê\82ðHTML\82Å\95Û\91¶(&H)...", 450
index 7d0461b..ec53275 100644 (file)
@@ -1,7 +1,9 @@
 #include "game-option/runtime-arguments.h"
 
 bool arg_sound; /* Command arg -- Request special sounds */
+int arg_sound_volume_table_index;
 bool arg_music; /* Command arg -- Request special musics */
+int arg_music_volume_table_index;
 byte arg_graphics; /* Command arg -- Request graphics mode */
 bool arg_monochrome; /* Command arg -- Request monochrome mode */
 bool arg_force_original; /* Command arg -- Request original keyset */
index 4937a32..ddc2627 100644 (file)
@@ -3,7 +3,9 @@
 #include "system/angband.h"
 
 extern bool arg_music;
+extern int arg_music_volume_table_index;
 extern bool arg_sound;
+extern int arg_sound_volume_table_index;
 extern byte arg_graphics;
 extern bool arg_monochrome;
 extern bool arg_force_original;
index aca0724..f188531 100644 (file)
 #include "wizard/spoiler-util.h"
 #include "wizard/wizard-spoiler.h"
 #include "world/world.h"
+#include <algorithm>
 #include <commdlg.h>
 #include <cstdlib>
 #include <direct.h>
@@ -405,10 +406,12 @@ static void save_prefs(void)
 
     strcpy(buf, arg_sound ? "1" : "0");
     WritePrivateProfileStringA("Angband", "Sound", buf, ini_file);
+    WritePrivateProfileStringA("Angband", "SoundVolumeTableIndex", std::to_string(arg_sound_volume_table_index).data(), ini_file);
 
     strcpy(buf, arg_music ? "1" : "0");
     WritePrivateProfileStringA("Angband", "Music", buf, ini_file);
     strcpy(buf, use_pause_music_inactive ? "1" : "0");
+    WritePrivateProfileStringA("Angband", "MusicVolumeTableIndex", std::to_string(arg_music_volume_table_index).data(), ini_file);
     WritePrivateProfileStringA("Angband", "MusicPauseInactive", buf, ini_file);
 
     wsprintfA(buf, "%d", current_bg_mode);
@@ -507,7 +510,9 @@ static void load_prefs(void)
     arg_bigtile = (GetPrivateProfileIntA("Angband", "Bigtile", false, ini_file) != 0);
     use_bigtile = arg_bigtile;
     arg_sound = (GetPrivateProfileIntA("Angband", "Sound", 0, ini_file) != 0);
+    arg_sound_volume_table_index = std::clamp<int>(GetPrivateProfileIntA("Angband", "SoundVolumeTableIndex", 0, ini_file), 0, SOUND_VOLUME_TABLE.size() - 1);
     arg_music = (GetPrivateProfileIntA("Angband", "Music", 0, ini_file) != 0);
+    arg_music_volume_table_index = std::clamp<int>(GetPrivateProfileIntA("Angband", "MusicVolumeTableIndex", 0, ini_file), 0, main_win_music::VOLUME_TABLE.size() - 1);
     use_pause_music_inactive = (GetPrivateProfileIntA("Angband", "MusicPauseInactive", 0, ini_file) != 0);
     current_bg_mode = static_cast<bg_mode>(GetPrivateProfileIntA("Angband", "BackGround", 0, ini_file));
     GetPrivateProfileStringA("Angband", "BackGroundBitmap", DEFAULT_BG_FILENAME, wallpaper_file, 1023, ini_file);
@@ -903,7 +908,7 @@ static errr term_xtra_win_sound(int v)
     if (!use_sound) {
         return 1;
     }
-    return play_sound(v);
+    return play_sound(v, SOUND_VOLUME_TABLE[arg_sound_volume_table_index]);
 }
 
 /*!
@@ -1522,8 +1527,12 @@ static void setup_menus(void)
     CheckMenuItem(hm, IDM_OPTIONS_NEW2_GRAPHICS, (arg_graphics == enum2i(graphics_mode::GRAPHICS_HENGBAND) ? MF_CHECKED : MF_UNCHECKED));
     CheckMenuItem(hm, IDM_OPTIONS_BIGTILE, (arg_bigtile ? MF_CHECKED : MF_UNCHECKED));
     CheckMenuItem(hm, IDM_OPTIONS_MUSIC, (arg_music ? MF_CHECKED : MF_UNCHECKED));
+    CheckMenuRadioItem(hm, IDM_OPTIONS_MUSIC_VOLUME_100, IDM_OPTIONS_MUSIC_VOLUME_010,
+        IDM_OPTIONS_MUSIC_VOLUME_100 + arg_music_volume_table_index, MF_BYCOMMAND);
     CheckMenuItem(hm, IDM_OPTIONS_MUSIC_PAUSE_INACTIVE, (use_pause_music_inactive ? MF_CHECKED : MF_UNCHECKED));
     CheckMenuItem(hm, IDM_OPTIONS_SOUND, (arg_sound ? MF_CHECKED : MF_UNCHECKED));
+    CheckMenuRadioItem(hm, IDM_OPTIONS_SOUND_VOLUME_100, IDM_OPTIONS_SOUND_VOLUME_010,
+        IDM_OPTIONS_SOUND_VOLUME_100 + arg_sound_volume_table_index, MF_BYCOMMAND);
     CheckMenuItem(hm, IDM_OPTIONS_NO_BG, ((current_bg_mode == bg_mode::BG_NONE) ? MF_CHECKED : MF_UNCHECKED));
     CheckMenuItem(hm, IDM_OPTIONS_BG, ((current_bg_mode == bg_mode::BG_ONE) ? MF_CHECKED : MF_UNCHECKED));
     CheckMenuItem(hm, IDM_OPTIONS_PRESET_BG, ((current_bg_mode == bg_mode::BG_PRESET) ? MF_CHECKED : MF_UNCHECKED));
@@ -1870,6 +1879,22 @@ static void process_menus(PlayerType *player_ptr, WORD wCmd)
         }
         break;
     }
+    case IDM_OPTIONS_MUSIC_VOLUME_100:
+    case IDM_OPTIONS_MUSIC_VOLUME_090:
+    case IDM_OPTIONS_MUSIC_VOLUME_080:
+    case IDM_OPTIONS_MUSIC_VOLUME_070:
+    case IDM_OPTIONS_MUSIC_VOLUME_060:
+    case IDM_OPTIONS_MUSIC_VOLUME_050:
+    case IDM_OPTIONS_MUSIC_VOLUME_040:
+    case IDM_OPTIONS_MUSIC_VOLUME_030:
+    case IDM_OPTIONS_MUSIC_VOLUME_020:
+    case IDM_OPTIONS_MUSIC_VOLUME_010: {
+        arg_music_volume_table_index = wCmd - IDM_OPTIONS_MUSIC_VOLUME_100;
+        if (use_music) {
+            main_win_music::set_music_volume(main_win_music::VOLUME_TABLE[arg_music_volume_table_index]);
+        }
+        break;
+    }
     case IDM_OPTIONS_MUSIC_PAUSE_INACTIVE: {
         use_pause_music_inactive = !use_pause_music_inactive;
         break;
@@ -1885,6 +1910,19 @@ static void process_menus(PlayerType *player_ptr, WORD wCmd)
         change_sound_mode(arg_sound);
         break;
     }
+    case IDM_OPTIONS_SOUND_VOLUME_100:
+    case IDM_OPTIONS_SOUND_VOLUME_090:
+    case IDM_OPTIONS_SOUND_VOLUME_080:
+    case IDM_OPTIONS_SOUND_VOLUME_070:
+    case IDM_OPTIONS_SOUND_VOLUME_060:
+    case IDM_OPTIONS_SOUND_VOLUME_050:
+    case IDM_OPTIONS_SOUND_VOLUME_040:
+    case IDM_OPTIONS_SOUND_VOLUME_030:
+    case IDM_OPTIONS_SOUND_VOLUME_020:
+    case IDM_OPTIONS_SOUND_VOLUME_010: {
+        arg_sound_volume_table_index = wCmd - IDM_OPTIONS_SOUND_VOLUME_100;
+        break;
+    }
     case IDM_OPTIONS_OPEN_SOUND_DIR: {
         std::vector<char> buf(MAIN_WIN_MAX_PATH);
         path_build(&buf[0], MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_SOUND, "sound.cfg");
@@ -2228,7 +2266,7 @@ LRESULT PASCAL angband_window_procedure(HWND hWnd, UINT uMsg, WPARAM wParam, LPA
         return 0;
     }
     case MM_MCINOTIFY: {
-        main_win_music::on_mci_notify(wParam, lParam);
+        main_win_music::on_mci_notify(wParam, lParam, main_win_music::VOLUME_TABLE[arg_music_volume_table_index]);
 
         return 0;
     }
index a360dbd..79777a7 100644 (file)
 #define IDM_OPTIONS_PRESET_BG 442
 #define IDM_OPTIONS_OPEN_BG 449
 
+#define IDM_OPTIONS_MUSIC_VOLUME_100 460
+#define IDM_OPTIONS_MUSIC_VOLUME_090 461
+#define IDM_OPTIONS_MUSIC_VOLUME_080 462
+#define IDM_OPTIONS_MUSIC_VOLUME_070 463
+#define IDM_OPTIONS_MUSIC_VOLUME_060 464
+#define IDM_OPTIONS_MUSIC_VOLUME_050 465
+#define IDM_OPTIONS_MUSIC_VOLUME_040 466
+#define IDM_OPTIONS_MUSIC_VOLUME_030 467
+#define IDM_OPTIONS_MUSIC_VOLUME_020 468
+#define IDM_OPTIONS_MUSIC_VOLUME_010 469
+
+#define IDM_OPTIONS_SOUND_VOLUME_100 470
+#define IDM_OPTIONS_SOUND_VOLUME_090 471
+#define IDM_OPTIONS_SOUND_VOLUME_080 472
+#define IDM_OPTIONS_SOUND_VOLUME_070 473
+#define IDM_OPTIONS_SOUND_VOLUME_060 474
+#define IDM_OPTIONS_SOUND_VOLUME_050 475
+#define IDM_OPTIONS_SOUND_VOLUME_040 476
+#define IDM_OPTIONS_SOUND_VOLUME_030 477
+#define IDM_OPTIONS_SOUND_VOLUME_020 478
+#define IDM_OPTIONS_SOUND_VOLUME_010 479
+
 #define IDM_DUMP_SCREEN_HTML 450
index e77fc88..b68a22b 100644 (file)
@@ -20,6 +20,9 @@
 #include "term/z-term.h"
 #include "util/angband-files.h"
 #include "world/world.h"
+#include <algorithm>
+#include <digitalv.h>
+#include <limits>
 
 bool use_pause_music_inactive = false;
 static int current_music_type = TERM_XTRA_MUSIC_MUTE;
@@ -243,13 +246,30 @@ errr play_music_scene(int val)
     return 0;
 }
 
+void set_music_volume(int volume)
+{
+    if (current_music_type == TERM_XTRA_MUSIC_MUTE) {
+        return;
+    }
+
+    MCI_DGV_SETAUDIO_PARMSW mci_vol{};
+    mci_vol.dwItem = MCI_DGV_SETAUDIO_VOLUME;
+    mci_vol.dwValue = volume;
+    mciSendCommandW(
+        mci_open_parms.wDeviceID,
+        MCI_SETAUDIO,
+        MCI_DGV_SETAUDIO_ITEM | MCI_DGV_SETAUDIO_VALUE,
+        (DWORD)&mci_vol);
+}
+
 /*
  * Notify event
  */
-void on_mci_notify(WPARAM wFlags, LONG lDevID)
+void on_mci_notify(WPARAM wFlags, LONG lDevID, int volume)
 {
     if (wFlags == MCI_NOTIFY_SUCCESSFUL) {
         // play a music (repeat)
+        set_music_volume(volume);
         mciSendCommandW(lDevID, MCI_SEEK, MCI_SEEK_TO_START | MCI_WAIT, 0);
         mciSendCommandW(lDevID, MCI_PLAY, MCI_NOTIFY, (DWORD)&mci_play_parms);
     }
index 6f05bf8..3e09249 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "main/music-definitions-table.h"
 
+#include <array>
 #include <windows.h>
 
 extern bool use_pause_music_inactive;
@@ -18,5 +19,9 @@ errr play_music(int type, int val);
 errr play_music_scene(int val);
 void pause_music(void);
 void resume_music(void);
-void on_mci_notify(WPARAM wFlags, LONG lDevID);
+void set_music_volume(int volume);
+void on_mci_notify(WPARAM wFlags, LONG lDevID, int volume);
+
+/*! 音量 100%,90%,…,10% それぞれに割り当てる実際の値(音量の指定可能範囲:0~1000) */
+constexpr std::array<int, 10> VOLUME_TABLE = { { 1000, 800, 600, 450, 350, 250, 170, 100, 50, 20 } };
 }
index 449b3fa..8daae3c 100644 (file)
@@ -3,6 +3,8 @@
  * @brief Windows版固有実装(効果音)
  */
 
+#define NOMINMAX
+
 #include "main-win/main-win-sound.h"
 #include "main-win/main-win-cfg-reader.h"
 #include "main-win/main-win-define.h"
@@ -78,6 +80,51 @@ struct sound_res {
 std::queue<sound_res *> sound_queue;
 
 /*!
+ * @brief PCMデータの振幅を変調する
+ *
+ * 引数で与えられたPCMデータの振幅を mult/div 倍に変調する。
+ * PCMデータのサンプリングビット数は bits_per_sample 引数で指定する。
+ * 変調した値がサンプリングビット数で表現可能な範囲に収まらない場合は表現可能な範囲の最大値/最小値に制限する。
+ * サンプリングビット数で有効なのは 8 か 16 のみであり、それ以外の値が指定された場合はなにも行わない。
+ *
+ * @param bits_per_sample PCMデータのサンプリングビット数 (8 or 16)
+ * @param pcm_buf PCMデータバッファ領域へのポインタ
+ * @param bufsize PCMデータバッファ領域のサイズ
+ * @param mult 振幅変調倍率 mult/div の mult
+ * @param div 振幅変調倍率 mult/div の div
+ */
+static void modulate_amplitude(int bits_per_sample, BYTE *pcm_buf, size_t bufsize, int mult, int div)
+{
+    auto modulate = [mult, div](auto sample) {
+        using sample_t = decltype(sample);
+        constexpr auto min = std::numeric_limits<sample_t>::min();
+        constexpr auto max = std::numeric_limits<sample_t>::max();
+        const auto modulated_sample = std::clamp<int>(sample * mult / div, min, max);
+        return static_cast<sample_t>(modulated_sample);
+    };
+
+    switch (bits_per_sample) {
+    case 8:
+        for (auto i = 0U; i < bufsize; ++i) {
+            pcm_buf[i] = modulate(pcm_buf[i]);
+        }
+        break;
+
+    case 16:
+        for (auto i = 0U; i < bufsize; i += 2) {
+            const auto sample = static_cast<int16_t>((static_cast<uint16_t>(pcm_buf[i + 1]) << 8) | static_cast<uint16_t>(pcm_buf[i]));
+            const auto modulated_sample = modulate(sample);
+            pcm_buf[i + 1] = static_cast<uint16_t>(modulated_sample) >> 8;
+            pcm_buf[i] = static_cast<uint16_t>(modulated_sample) & 0xFF;
+        }
+        break;
+
+    default:
+        break;
+    }
+}
+
+/*!
  * 効果音の再生と管理キューへの追加.
  *
  * @param wf WAVEFORMATEXへのポインタ
@@ -86,7 +133,7 @@ std::queue<sound_res *> sound_queue;
  * @retval true 正常に処理された
  * @retval false 処理エラー
  */
-static bool add_sound_queue(const WAVEFORMATEX *wf, BYTE *buf, DWORD bufsize)
+static bool add_sound_queue(const WAVEFORMATEX *wf, BYTE *buf, DWORD bufsize, int volume)
 {
     // 再生完了データをキューから削除する
     while (!sound_queue.empty()) {
@@ -107,6 +154,8 @@ static bool add_sound_queue(const WAVEFORMATEX *wf, BYTE *buf, DWORD bufsize)
         return false;
     }
 
+    modulate_amplitude(wf->wBitsPerSample, buf, bufsize, volume, SOUND_VOLUME_MAX);
+
     WAVEHDR *wh = &res->wh;
     wh->lpData = (LPSTR)buf;
     wh->dwBufferLength = bufsize;
@@ -140,7 +189,7 @@ static bool add_sound_queue(const WAVEFORMATEX *wf, BYTE *buf, DWORD bufsize)
  * @retval true 正常に処理された
  * @retval false 処理エラー
  */
-static bool play_sound_impl(char *filename)
+static bool play_sound_impl(char *filename, int volume)
 {
     wav_reader reader;
     if (!reader.open(filename)) {
@@ -153,7 +202,7 @@ static bool play_sound_impl(char *filename)
         return false;
     }
 
-    return add_sound_queue(wf, data_buffer, reader.get_data_chunk()->cksize);
+    return add_sound_queue(wf, data_buffer, reader.get_data_chunk()->cksize, volume);
 }
 
 /*!
@@ -204,7 +253,7 @@ void finalize_sound(void)
  * @retval 1 設定なし
  * @retval -1 PlaySoundの戻り値が正常終了以外
  */
-errr play_sound(int val)
+errr play_sound(int val, int volume)
 {
     concptr filename = sound_cfg_data->get_rand(TERM_XTRA_SOUND, val);
     if (!filename) {
@@ -214,7 +263,7 @@ errr play_sound(int val)
     char buf[MAIN_WIN_MAX_PATH];
     path_build(buf, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_SOUND, filename);
 
-    if (play_sound_impl(buf)) {
+    if (play_sound_impl(buf, volume)) {
         return 0;
     }
 
index d85701d..41e4fa4 100644 (file)
@@ -2,10 +2,15 @@
 
 #include "main-win/main-win-cfg-reader.h"
 #include "system/h-type.h"
+#include <array>
 
 extern concptr ANGBAND_DIR_XTRA_SOUND;
 extern CfgData *sound_cfg_data;
 
 void load_sound_prefs(void);
 void finalize_sound(void);
-errr play_sound(int val);
+errr play_sound(int val, int volume);
+
+/*! 音量 100%,90%,…,10% それぞれに割り当てる実際の値(効果音の音声データの振幅を volume/SOUND_VOLUME_MAX 倍する) */
+constexpr std::array<int, 10> SOUND_VOLUME_TABLE = { { 1000, 800, 600, 450, 350, 250, 170, 100, 50, 20 } };
+constexpr auto SOUND_VOLUME_MAX = SOUND_VOLUME_TABLE.front();