Инструменты пользователя

Инструменты сайта


software:development:demo:cms:ucms:appendix:appendix_js_speech_audio_search_v2_comment

Код примера Speech Audio Seach v2

speech_audio_search_v2.php
<?php
// PHP: Ищем все файлы с расширением .mp3 в папке music
$files = glob("music/*.mp3");
// Создаем пустой массив для хранения данных плейлиста
$playlist = [];
// Перебираем каждый найденный файл
foreach ($files as $file) {
    // Добавляем в массив: название трека (без расширения) и путь к файлу
    $playlist[] = ["title" => basename($file, ".mp3"), "src" => $file];
}
// Если папка пуста, создаем один пустой элемент, чтобы избежать ошибок
if (empty($playlist)) $playlist[] = ["title" => "Нет файлов", "src" => ""];
// Переводим PHP-массив в формат JSON, чтобы его смог прочитать JavaScript
$jsonPlaylist = json_encode($playlist);
?>
 
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>UCMS Voice Search Player</title>
    <style>
        /* Общие стили страницы: центрирование и светлый фон */
        body { background: #F0F2F5; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; font-family: sans-serif; }
        /* Карточка плеера: фиксированный размер 600x300 в стиле Facebook */
        .fb-card { width: 600px; height: 300px; background: #fff; border-radius: 8px; box-shadow: 0 12px 28px rgba(0,0,0,0.1); display: flex; flex-direction: column; position: relative; border: 1px solid #dddfe2; }
        /* Шапка плеера */
        .fb-header { padding: 10px 16px; border-bottom: 1px solid #ebedf0; display: flex; justify-content: space-between; }
        /* Индикатор работы микрофона */
        .status-indicator { display: flex; align-items: center; gap: 5px; font-size: 12px; }
        .dot { width: 10px; height: 10px; border-radius: 50%; background: #ccc; }
        /* Анимация пульсации для активного микрофона */
        .active-mic .dot { background: #31a24c; animation: pulse 1.5s infinite; }
        @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
        /* Главная область с названием трека */
        .fb-main { flex: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 20px; text-align: center; }
        #currentTitle { font-size: 20px; font-weight: bold; color: #1c1e21; margin-bottom: 5px; cursor: pointer; }
        /* Панель кнопок управления */
        .fb-controls { display: flex; border-top: 1px solid #ebedf0; }
        .fb-btn { flex: 1; background: none; border: none; padding: 12px; color: #65676B; font-weight: 600; cursor: pointer; }
        /* Выдвижной плейлист (изначально высота 0) */
        #playlistOverlay { position: absolute; bottom: 85px; left: 0; width: 100%; height: 0; background: #fff; overflow-y: auto; transition: 0.3s; z-index: 10; border-top: 1px solid #ddd; }
        #playlistOverlay.open { height: 160px; } /* Раскрытое состояние плейлиста */
        #playlistView { list-style: none; padding: 0; margin: 0; }
        #playlistView li { padding: 10px 20px; border-bottom: 1px solid #eee; cursor: pointer; font-size: 13px; }
        #playlistView li.active { color: #1877F2; background: #e7f3ff; font-weight: 600; }
        /* Подсказки внизу плеера */
        .hints { background: #f7f8fa; padding: 8px; font-size: 10px; color: #65676B; text-align: center; }
        audio { display: none; } /* Плеер скрыт, так как мы используем свои кнопки */
    </style>
</head>
<body>
 
<div class="fb-card" id="playerNode">
    <div class="fb-header">
        <b style="color:#1877F2">UCMS Audio Search</b>
        <!-- Статус микрофона -->
        <div class="status-indicator" id="statusBox"><div class="dot"></div> <span id="statusLabel">Кликните для активации</span></div>
    </div>
    <div class="fb-main">
        <!-- Текст текущего трека -->
        <div id="currentTitle" onclick="togglePlaylist()">Ожидание команды...</div>
        <!-- Слой для вывода распознанного текста -->
        <div id="debug" style="font-size: 11px; color: #1877F2; margin-top: 5px;"></div>
    </div>
    <!-- Контейнер плейлиста -->
    <div id="playlistOverlay"><ul id="playlistView"></ul></div>
    <!-- Кнопки управления -->
    <div class="fb-controls">
        <button class="fb-btn" onclick="runCmd('назад')">Назад</button>
        <button class="fb-btn" onclick="runCmd('играть')">Играть</button>
        <button class="fb-btn" onclick="runCmd('пауза')">Стоп</button>
        <button class="fb-btn" onclick="runCmd('дальше')">Дальше</button>
    </div>
    <div class="hints">Скажите: <b>"Включи [название]"</b> или "играть", "стоп", "дальше"</div>
    <!-- Аудио-элемент HTML5 -->
    <audio id="audio" crossorigin="anonymous"></audio>
</div>
 
<script>
// Переносим данные плейлиста из PHP в JS
const tracks = <?php echo $jsonPlaylist; ?>;
let currentIdx = 0; // Индекс текущего трека
const audio = document.getElementById('audio'); // Элемент аудио
const title = document.getElementById('currentTitle'); // Элемент заголовка
const statusLabel = document.getElementById('statusLabel'); // Текст статуса
const statusBox = document.getElementById('statusBox'); // Блок статуса
const debug = document.getElementById('debug'); // Блок отладки
 
// Функция синтеза речи (чтобы плеер отвечал голосом)
// Инициализируем объект синтеза речи
const synth = window.speechSynthesis;
 
function speak(txt) {
    if (!synth) return;
    synth.cancel(); // Прерываем старую речь
 
    // 1. Улучшаем текст для интонации:
    // Добавляем запятые и точки там, где нужны логические паузы
    // Например: "Включаю... [Название трека]. Приятного прослушивания."
    let processedText = txt.replace("Включаю", "Включаю, "); 
    if (!processedText.endsWith('.')) processedText += '.';
 
    const ut = new SpeechSynthesisUtterance(processedText);
 
    // 2. Настройка языка и поиск "человеческого" голоса
    ut.lang = 'ru-RU';
 
    // Получаем все голоса системы
    const voices = synth.getVoices();
 
    // Ищем наиболее качественные голоса (Microsoft или Google)
    // Эти голоса обычно звучат гораздо мягче и естественнее
    const premiumVoice = voices.find(v => 
        (v.name.includes('Google')) 
        && v.lang.includes('ru')
    );
 
    if (premiumVoice) {
        ut.voice = premiumVoice;
    }
 
    // 3. Тонкая настройка тембра (делаем голос менее "писклявым")
    ut.rate = 1.0;   // Чуть замедляем (1.0 - быстро, 0.9 - размеренно и естественно)
    ut.pitch = 1.1;  // Оставляем стандартную высоту (или 0.9 для более глубокого голоса)
    ut.volume = 1.0; // Громкость на максимум
 
    // 4. Запуск озвучки
    synth.speak(ut);
}
 
 
// Функция загрузки и запуска трека
function loadTrack(idx) {
    if (!tracks[idx] || !tracks[idx].src) return;
 
    currentIdx = idx;
    audio.src = tracks[idx].src;
    title.innerText = tracks[idx].title;
 
    document.querySelectorAll('#playlistView li').forEach((li, i) => {
        li.className = (i === idx) ? 'active' : '';
    });
 
    // Запускаем музыку
    audio.play().then(() => {
        // Запускаем голос ПОСЛЕ начала музыки (или одновременно)
        speak("Включаю " + tracks[idx].title);
    }).catch(err => {
        console.log("Браузер заблокировал звук до первого клика");
    });
}
 
// Главная функция обработки команд (кнопок и голоса)
function runCmd(cmd) {
    debug.innerText = "Вы сказали: " + cmd; // Вывод распознанного текста для проверки
    const text = cmd.toLowerCase(); // Приведение к нижнему регистру
 
    // Логика 1: Поиск песни по названию во всем плейлисте
    let foundIdx = -1;
    tracks.forEach((track, index) => {
        const trackTitle = track.title.toLowerCase();
        // Если в команде есть название трека или название содержит часть команды
        if (text.includes(trackTitle) || (text.includes('включи') && trackTitle.includes(text.replace('включи', '').trim()))) {
            foundIdx = index;
        }
    });
 
    if (foundIdx !== -1) { // Если песня найдена
        loadTrack(foundIdx);
        return;
    }
 
    // Логика 2: Стандартные команды управления
    if (text.includes('играть') || text.includes('пуск')) { audio.play(); speak("Запускаю"); }
    else if (text.includes('стоп') || text.includes('пауза')) { audio.pause(); speak("Пауза"); }
    else if (text.includes('дальше')) loadTrack((currentIdx + 1) % tracks.length);
    else if (text.includes('назад')) loadTrack((currentIdx - 1 + tracks.length) % tracks.length);
    else if (text.includes('громче')) { audio.volume = Math.min(1, audio.volume + 0.2); speak("Громче"); }
    else if (text.includes('тише')) { audio.volume = Math.max(0, audio.volume - 0.2); speak("Тише"); }
}
 
// Переключение видимости плейлиста
function togglePlaylist() { document.getElementById('playlistOverlay').classList.toggle('open'); }
 
// Инициализация распознавания речи
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (SpeechRecognition) {
    const recognition = new SpeechRecognition();
    recognition.lang = 'ru-RU';
    recognition.continuous = false; // Отключаем постоянный режим для лучшей точности
 
    // Когда браузер распознал фразу
    recognition.onresult = (e) => {
        const transcript = e.results[0][0].transcript; // Получаем текст
        runCmd(transcript); // Отправляем в обработчик
    };
 
    // Когда микрофон отключается (после фразы или тишины)
    recognition.onend = () => { 
        // Если плеер в этот момент не "разговаривает", включаем микрофон снова
        if (!window.speechSynthesis.speaking) {
            recognition.start();
        } else {
            // Если плеер говорит, ждем секунду и пробуем снова
            setTimeout(() => recognition.start(), 1000);
        }
    };
 
    // Обработка ошибок микрофона
    recognition.onerror = (e) => {
        if (e.error === 'no-speech') console.log("Тишина...");
    };
 
    // Активация микрофона по клику (требование безопасности браузеров)
    document.body.onclick = () => {
        try { 
            recognition.start(); 
            statusBox.classList.add('active-mic'); // Визуальный эффект
            statusLabel.innerText = "Слушаю название..."; 
            if(!audio.src) loadTrack(0); // Загружаем первый трек при первом клике
        } catch(err) { /* Игнорируем ошибку, если уже запущен */ }
    };
}
 
// Формируем список песен в HTML из данных плейлиста
const list = document.getElementById('playlistView');
list.innerHTML = tracks.map((t, i) => `<li onclick="loadTrack(${i})">${t.title}</li>`).join('');
</script>
</body>
</html>
Только авторизованные участники могут оставлять комментарии.
software/development/demo/cms/ucms/appendix/appendix_js_speech_audio_search_v2_comment.txt · Последнее изменение: VladPolskiy

Если не указано иное, содержимое этой вики предоставляется на условиях следующей лицензии: Public Domain
Public Domain Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki