Spaces:
Paused
Paused
File size: 6,567 Bytes
c685172 a062bed c685172 a062bed c685172 a062bed c685172 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | import io
import os
import uuid
import librosa
import numpy as np
import soundfile as sf
import streamlit as st
from huggingface_hub import HfApi, CommitOperationAdd
DATASET_REPO_ID = "uchi8977/kiss-vs-tsk"
TARGET_SECONDS = 0.5
TARGET_SR = 16000
SILENCE_THRESHOLD = 0.01
st.set_page_config(page_title="投げキッスと舌打ち", page_icon="😘", layout="centered")
def process_audio(audio_file) -> bytes | None:
try:
y, sr = librosa.load(io.BytesIO(audio_file.getvalue()), sr=TARGET_SR, mono=True)
peak = float(np.max(np.abs(y)))
if peak < SILENCE_THRESHOLD:
st.warning("⚠️ 音が小さすぎるか、無音のようです。もう一度録音してください。")
return None
y = y / peak
target_samples = int(sr * TARGET_SECONDS)
if len(y) < target_samples:
y = np.pad(y, (0, target_samples - len(y)))
else:
peak_idx = int(np.argmax(np.abs(y)))
start = max(0, min(peak_idx - target_samples // 2, len(y) - target_samples))
y = y[start:start + target_samples]
buf = io.BytesIO()
sf.write(buf, y, sr, format='WAV', subtype='PCM_16')
return buf.getvalue()
except Exception as e:
st.error(f"音声処理エラー: {e}")
return None
def submit_dataset(kiss_bytes: bytes, tsk_bytes: bytes, hf_token: str) -> str | None:
try:
submission_id = uuid.uuid4().hex
operations = [
CommitOperationAdd(
path_in_repo=f"data/{label}/{label}_{submission_id}.wav",
path_or_fileobj=wav_bytes,
)
for label, wav_bytes in [("kiss", kiss_bytes), ("tsk", tsk_bytes)]
]
HfApi(token=hf_token).create_commit(
repo_id=DATASET_REPO_ID,
repo_type="dataset",
operations=operations,
commit_message=f"Add submission {submission_id}",
)
return submission_id
except Exception as e:
st.error(f"❌ 送信エラー: {str(e)}")
return None
def go_to(step: int):
st.session_state.step = step
st.rerun()
def back_button():
if st.button("← 前に戻る", use_container_width=True, key="back"):
go_to(st.session_state.step - 1)
for key, val in {"step": 0, "kiss_bytes": None, "tsk_bytes": None, "submission_id": None}.items():
st.session_state.setdefault(key, val)
st.title("投げキッスと舌打ち")
st.write("機械学習のデータセット作成にご協力ください。")
st.divider()
step = st.session_state.step
if step == 0:
st.warning("⚠️ ご提供いただいた音声データは、機械学習のオープンデータセットとしてHugging Face上で一般公開されます。")
agree = st.checkbox("音声データが公開されることに同意します")
st.write("準備ができたら下のボタンを押して録音を開始してください。")
if st.button("はじめる", type="primary", use_container_width=True, disabled=not agree):
go_to(1)
elif step in (1, 2):
label, display_name, next_label = (
("kiss", "投げキッス", "次へ(舌打ちの録音)➔")
if step == 1 else
("tsk", "舌打ち", "送信画面へ ➔")
)
st.subheader(f"Step {step}/2: {display_name}")
st.write(f"下のマイクボタンを押して{display_name}を録音してください。")
audio_file = st.audio_input(
f"{display_name}を録音",
label_visibility="collapsed",
key=f"{label}_input"
)
st.write(f"※最大音量の瞬間を中心に【{TARGET_SECONDS}秒】だけを自動で切り抜きます({TARGET_SECONDS}秒未満は無音で補完)。")
if audio_file:
wav_bytes = process_audio(audio_file)
if wav_bytes:
st.session_state[f"{label}_bytes"] = wav_bytes
st.success("✅ 切り抜き完了!再生して確認してください。")
st.audio(wav_bytes)
if st.button(next_label, type="primary", use_container_width=True):
go_to(step + 1)
back_button()
elif step == 3:
hf_token = os.getenv("HF_TOKEN")
if not hf_token:
st.error("❌ サーバー側の設定エラー:HF_TOKEN が設定されていません。管理者にお問い合わせください。")
st.stop()
kiss_bytes = st.session_state.kiss_bytes
tsk_bytes = st.session_state.tsk_bytes
if not (kiss_bytes and tsk_bytes):
st.error("❌ 録音データが見つかりません。最初からやり直してください。")
if st.button("最初に戻る", use_container_width=True):
st.session_state.kiss_bytes = None
st.session_state.tsk_bytes = None
go_to(0)
st.stop()
st.subheader("🚀 最終確認")
st.write("以下の2つのデータを送信します。よろしいですか?")
col1, col2 = st.columns(2)
with col1:
st.write("投げキッス")
st.audio(kiss_bytes)
with col2:
st.write("舌打ち")
st.audio(tsk_bytes)
st.write("※送信ボタンを押すと、Hugging Faceの公開リポジトリにアップロードされます。")
if st.button("送信する!", type="primary", use_container_width=True):
with st.spinner("Hugging Faceに送信中..."):
submission_id = submit_dataset(kiss_bytes, tsk_bytes, hf_token)
if submission_id:
st.session_state.submission_id = submission_id
st.session_state.kiss_bytes = None
st.session_state.tsk_bytes = None
go_to(4)
back_button()
elif step == 4:
st.balloons()
st.header("🎉 送信完了!")
st.write("ご協力ありがとうございました。タブを閉じて終了してください。")
submission_id = st.session_state.submission_id
if submission_id:
st.divider()
st.write(f"**あなたのデータのID: `{submission_id}`**")
st.write(f"リポジトリで `kiss_{submission_id}.wav` と `tsk_{submission_id}.wav` として公開されています。")
st.divider()
st.write("寄せられた録音データは、Hugging Faceのリポジトリでリアルタイムに公開されています👇")
st.link_button(
"他の人の投げキッスと舌打ちを聴きに行く",
f"https://huggingface.co/datasets/{DATASET_REPO_ID}/tree/main/data",
use_container_width=True
) |