Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,9 @@ IMPLEMENTATION_STATUS.md
.cache
en.subject.pdf
saves/
ft_minecraft
docs/

# Audio assets (>42MB total) — use scripts/download_assets.sh
assets/music/
assets/sounds/
34 changes: 34 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,31 @@ if(FT_MINECRAFT_FETCH_DEPS)
INTERFACE
${fastnoiselite_SOURCE_DIR}/Cpp
)

# ── miniaudio (single-header audio engine) ─────────────────────────────────
FetchContent_Declare(
miniaudio
GIT_REPOSITORY https://github.com/mackron/miniaudio.git
GIT_TAG 0.11.22
GIT_SHALLOW TRUE
)
FetchContent_GetProperties(miniaudio)
if(NOT miniaudio_POPULATED)
FetchContent_Populate(miniaudio)
endif()

# ── stb (provides stb_vorbis.c for OGG decoding) ───────────────────────────
FetchContent_Declare(
stb
GIT_REPOSITORY https://github.com/nothings/stb.git
GIT_TAG master
GIT_SHALLOW TRUE
)
FetchContent_GetProperties(stb)
if(NOT stb_POPULATED)
FetchContent_Populate(stb)
endif()

else()
find_package(glfw3 REQUIRED)
find_package(glm CONFIG REQUIRED)
Expand Down Expand Up @@ -88,6 +113,11 @@ add_executable(
srcs/network/client.cpp
srcs/mob/mob_manager.cpp
srcs/mob/arrow_manager.cpp
# ── Audio system ──────────────────────────────────────────────────────────
srcs/audio/audio_impl.cpp
srcs/audio/audio_manager.cpp
srcs/audio/biome_audio_system.cpp
srcs/audio/sound_emitter.cpp
)

target_include_directories(
Expand All @@ -96,6 +126,8 @@ target_include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/srcs
${glfw_SOURCE_DIR}/deps
${miniaudio_SOURCE_DIR}
${stb_SOURCE_DIR}
)

target_compile_definitions(ft_minecraft PRIVATE GLFW_INCLUDE_NONE)
Expand All @@ -108,6 +140,8 @@ target_link_libraries(
glm::glm
ft_minecraft_fastnoise
Threads::Threads
${CMAKE_DL_LIBS} # miniaudio uses dlopen on Linux for audio backends
m # math library (miniaudio, stb_vorbis)
)

set_target_properties(
Expand Down
165 changes: 165 additions & 0 deletions scripts/download_assets.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#!/usr/bin/env bash
# download_assets.sh
#
# 使い方:
# bash scripts/download_assets.sh
#
# URL の拡張子は自動判定(ogg / mp3 / wav / flac すべてOK)。
# "" のままにすれば無音 WAV で自動補完。

set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"

# =============================================================================
# ここに URL を書く(不要なら "" のままでOK)
# =============================================================================

# ── BGM ──────────────────────────────────────────────────────────────────────
# CC-BY 3.0: RPG Exploration Level 1 (HitCtrl)
URL_MUSIC_PLAINS="https://opengameart.org/sites/default/files/RPG_Exploration_Level_1.ogg"
# CC0: Desert theme / caravan (Loyalty Freak Music)
URL_MUSIC_DESERT="https://opengameart.org/sites/default/files/caravan.ogg.ogg"
# CC0: Ice Shine Bells (hc)
URL_MUSIC_TUNDRA="https://opengameart.org/sites/default/files/Ice-Shine-Bells_0.ogg"
# CC0: Cave Theme (Brandon Morris)
URL_MUSIC_ROCKY="https://opengameart.org/sites/default/files/cave%20themeb4.ogg"
# CC-BY 3.0: Hazy Darkness Looping (Eric Matyas)
URL_MUSIC_SWAMP="https://opengameart.org/sites/default/files/hazy_darkness_looping_0.ogg"
# CC-BY-SA 3.0: DarkWinds orchestral (Zefz)
URL_MUSIC_MOUNTAIN="https://opengameart.org/sites/default/files/DarkWinds_0.OGG"
# CC0: Birds and Wind - Ambient (Spring Spring)
URL_MUSIC_CANYON="https://opengameart.org/sites/default/files/Birds%20and%20Wind%20-%20Ambient_1.ogg"
# CC0: Flowerbed Fields (Zane Little Music)
URL_MUSIC_SPRING="https://opengameart.org/sites/default/files/flowerbed_fields.ogg"
# CC-BY 3.0: The Last Leaf of the Fall (HitCtrl)
URL_MUSIC_AUTUMN="https://opengameart.org/sites/default/files/the_last_leaf_of_the_fall.ogg"

# ── アンビエント ──────────────────────────────────────────────────────────────
URL_AMBIENT_PLAINS=""
URL_AMBIENT_DESERT=""
URL_AMBIENT_TUNDRA=""
URL_AMBIENT_ROCKY=""
URL_AMBIENT_SWAMP=""
URL_AMBIENT_MOUNTAIN=""
URL_AMBIENT_CANYON=""
URL_AMBIENT_SPRING=""
URL_AMBIENT_AUTUMN=""

# ── SE(効果音) ──────────────────────────────────────────────────────────────
# CC0: grass_1 (Delta12 Studio)
URL_SE_FOOTSTEP_GRASS="https://opengameart.org/sites/default/files/grass_1.ogg"
# CC0: snd_footsteps1 (Spring Spring)
URL_SE_FOOTSTEP_STONE="https://opengameart.org/sites/default/files/snd_footsteps1.wav"
# CC0: sand_footsteps (various)
URL_SE_FOOTSTEP_SAND="https://opengameart.org/sites/default/files/sand_footsteps_0.mp3"
# CC0: tap_stone で代用 (雪は直リンクなし)
URL_SE_FOOTSTEP_SNOW="https://opengameart.org/sites/default/files/tap_stone.wav"
# CC0: grass_1 で代用 (木材は直リンクなし)
URL_SE_FOOTSTEP_WOOD="https://opengameart.org/sites/default/files/grass_1.ogg"
# CC0: punch_1 (Delta12 Studio)
URL_SE_ATTACK="https://opengameart.org/sites/default/files/punch_1.ogg"
# CC0: hurt_1 (Delta12 Studio)
URL_SE_HURT="https://opengameart.org/sites/default/files/hurt_1.ogg"
# CC0: swim (Skippy Fish)
URL_SE_SWIM="https://opengameart.org/sites/default/files/swim.ogg"
# CC0: small_rock_impact (Spring Spring)
URL_SE_BLOCK_BREAK="https://opengameart.org/sites/default/files/small_rock_impact.wav"
# CC0: tap_stone (Spring Spring)
URL_SE_BLOCK_PLACE="https://opengameart.org/sites/default/files/tap_stone.wav"
# CC0: zombie groan (saturn91.dev)
URL_SE_MOB_GROAN="https://opengameart.org/sites/default/files/01_zombey.mp3"
# CC0: zombie_pain (various)
URL_SE_MOB_HURT="https://opengameart.org/sites/default/files/zombie_pain.ogg"
# CC0: explosion1 (various)
URL_SE_MOB_EXPLODE="https://opengameart.org/sites/default/files/explosion1_0.ogg"

# =============================================================================
# 以下は変更不要
# =============================================================================

mkdir -p \
"$REPO_ROOT/assets/music" \
"$REPO_ROOT/assets/sounds/ambient" \
"$REPO_ROOT/assets/sounds"

# URL の末尾から拡張子を取り出す(クエリ文字列を除去してから)
url_ext() {
local url="$1"
# クエリ文字列・フラグメントを除去してから拡張子を取得
local path="${url%%\?*}"
path="${path%%\#*}"
echo "${path##*.}"
}

# stem: 拡張子なしのファイルパス(例: assets/music/plains_bgm)
# URL の拡張子でファイルを保存。stem.* がすでに存在すればスキップ。
fetch() {
local url="$1" stem="$2"
[[ -z "$url" ]] && return 0

# 本物のファイルがすでにある(マーカーなし)ならスキップ
if [[ ! -f "${stem}.placeholder" ]]; then
for f in "${stem}".ogg "${stem}".mp3 "${stem}".wav "${stem}".flac; do
[[ -f "$f" ]] && return 0
done
fi

local ext
ext="$(url_ext "$url" | tr '[:upper:]' '[:lower:]')"
local dst="${stem}.${ext}"
echo " downloading $(basename "$dst") ..."
if command -v curl &>/dev/null; then
curl -fsSL -o "$dst" "$url"
else
wget -q -O "$dst" "$url"
fi

# ダウンロード成功 → プレースホルダーを削除(実ダウンロードが .wav の場合は消さない)
rm -f "${stem}.placeholder"
if [[ "$ext" != "wav" ]]; then rm -f "${stem}.wav"; fi
}

# BGM
fetch "$URL_MUSIC_PLAINS" "$REPO_ROOT/assets/music/plains_bgm"
fetch "$URL_MUSIC_DESERT" "$REPO_ROOT/assets/music/desert_bgm"
fetch "$URL_MUSIC_TUNDRA" "$REPO_ROOT/assets/music/tundra_bgm"
fetch "$URL_MUSIC_ROCKY" "$REPO_ROOT/assets/music/rocky_bgm"
fetch "$URL_MUSIC_SWAMP" "$REPO_ROOT/assets/music/swamp_bgm"
fetch "$URL_MUSIC_MOUNTAIN" "$REPO_ROOT/assets/music/mountain_bgm"
fetch "$URL_MUSIC_CANYON" "$REPO_ROOT/assets/music/canyon_bgm"
fetch "$URL_MUSIC_SPRING" "$REPO_ROOT/assets/music/spring_bgm"
fetch "$URL_MUSIC_AUTUMN" "$REPO_ROOT/assets/music/autumn_bgm"

# Ambient
fetch "$URL_AMBIENT_PLAINS" "$REPO_ROOT/assets/sounds/ambient/plains"
fetch "$URL_AMBIENT_DESERT" "$REPO_ROOT/assets/sounds/ambient/desert"
fetch "$URL_AMBIENT_TUNDRA" "$REPO_ROOT/assets/sounds/ambient/tundra"
fetch "$URL_AMBIENT_ROCKY" "$REPO_ROOT/assets/sounds/ambient/rocky"
fetch "$URL_AMBIENT_SWAMP" "$REPO_ROOT/assets/sounds/ambient/swamp"
fetch "$URL_AMBIENT_MOUNTAIN" "$REPO_ROOT/assets/sounds/ambient/mountain"
fetch "$URL_AMBIENT_CANYON" "$REPO_ROOT/assets/sounds/ambient/canyon"
fetch "$URL_AMBIENT_SPRING" "$REPO_ROOT/assets/sounds/ambient/spring"
fetch "$URL_AMBIENT_AUTUMN" "$REPO_ROOT/assets/sounds/ambient/autumn"

# SE
fetch "$URL_SE_FOOTSTEP_GRASS" "$REPO_ROOT/assets/sounds/footstep_grass"
fetch "$URL_SE_FOOTSTEP_STONE" "$REPO_ROOT/assets/sounds/footstep_stone"
fetch "$URL_SE_FOOTSTEP_SAND" "$REPO_ROOT/assets/sounds/footstep_sand"
fetch "$URL_SE_FOOTSTEP_SNOW" "$REPO_ROOT/assets/sounds/footstep_snow"
fetch "$URL_SE_FOOTSTEP_WOOD" "$REPO_ROOT/assets/sounds/footstep_wood"
fetch "$URL_SE_ATTACK" "$REPO_ROOT/assets/sounds/attack"
fetch "$URL_SE_HURT" "$REPO_ROOT/assets/sounds/hurt"
fetch "$URL_SE_SWIM" "$REPO_ROOT/assets/sounds/swim"
fetch "$URL_SE_BLOCK_BREAK" "$REPO_ROOT/assets/sounds/block_break"
fetch "$URL_SE_BLOCK_PLACE" "$REPO_ROOT/assets/sounds/block_place"
fetch "$URL_SE_MOB_GROAN" "$REPO_ROOT/assets/sounds/mob_groan"
fetch "$URL_SE_MOB_HURT" "$REPO_ROOT/assets/sounds/mob_hurt"
fetch "$URL_SE_MOB_EXPLODE" "$REPO_ROOT/assets/sounds/mob_explode"

# 未ダウンロードのファイルは無音 WAV で補完
echo ""
echo "Generating placeholders for missing files..."
ASSETS="$REPO_ROOT/assets" bash "$REPO_ROOT/scripts/gen_placeholder_assets.sh"

echo ""
echo "Done."
66 changes: 66 additions & 0 deletions scripts/gen_placeholder_assets.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/usr/bin/env bash
# gen_placeholder_assets.sh
#
# 開発用: 無音の WAV プレースホルダーを生成する。
# 生成したファイルには stem.placeholder マーカーを置く。
# マーカーがあるファイルは download_assets.sh が URL 設定時に上書きする。

set -euo pipefail

REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
ASSETS="${ASSETS:-$REPO_ROOT/assets}"

python3 - "$ASSETS" <<'PYEOF'
import struct, os, sys

assets = sys.argv[1]

def make_wav(stem, duration_s, sample_rate=44100, channels=1):
os.makedirs(os.path.dirname(stem), exist_ok=True)

# 本物のファイルがあればスキップ(マーカーなし=本物)
for ext in ('.ogg', '.mp3', '.wav', '.flac'):
candidate = stem + ext
if os.path.exists(candidate) and not os.path.exists(stem + '.placeholder'):
return

wav_path = stem + '.wav'
num_samples = int(sample_rate * duration_s * channels)
pcm = bytes(num_samples * 2)
with open(wav_path, 'wb') as f:
data_size = len(pcm)
f.write(b'RIFF')
f.write(struct.pack('<I', 36 + data_size))
f.write(b'WAVE')
f.write(b'fmt ')
f.write(struct.pack('<IHHIIHH', 16, 1, channels, sample_rate,
sample_rate * channels * 2, channels * 2, 16))
f.write(b'data')
f.write(struct.pack('<I', data_size))
f.write(pcm)

# マーカーを置く(download_assets.sh が上書き対象と判断するため)
open(stem + '.placeholder', 'w').close()
print(f" placeholder {os.path.relpath(wav_path)}")

biomes = ["plains","desert","tundra","rocky","swamp","mountain","canyon","spring","autumn"]
se_files = [
"footstep_grass","footstep_stone","footstep_sand","footstep_snow","footstep_wood",
"attack","hurt","swim","block_break","block_place",
"mob_groan","mob_hurt","mob_explode",
]

print("\n[BGM]")
for b in biomes:
make_wav(f"{assets}/music/{b}_bgm", 1.0)

print("\n[Ambient]")
for b in biomes:
make_wav(f"{assets}/sounds/ambient/{b}", 1.0)

print("\n[SE]")
for s in se_files:
make_wav(f"{assets}/sounds/{s}", 0.3)

print("\nDone.")
PYEOF
32 changes: 32 additions & 0 deletions srcs/audio/audio_impl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Single compilation unit that expands the miniaudio and stb_vorbis
// implementation bodies. No other .cpp must define MINIAUDIO_IMPLEMENTATION.

#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wunused-function"
# pragma clang diagnostic ignored "-Wsign-compare"
# pragma clang diagnostic ignored "-Wimplicit-fallthrough"
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
# pragma clang diagnostic ignored "-Wmissing-field-initializers"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-function"
# pragma GCC diagnostic ignored "-Wsign-compare"
# pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
# pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#endif

// stb_vorbis must be included before MINIAUDIO_IMPLEMENTATION
// so that miniaudio can detect and use it for OGG decoding.
#include "stb_vorbis.c"

#define MINIAUDIO_IMPLEMENTATION
#include <miniaudio.h>

#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
Loading