(function () { // -- docu elements let clearButton = document.getElementById('recordingClearButton'); let recordButton = document.getElementById('recordingRecordButton'); let playbackButton = document.getElementById('recordingPlaybackButton'); let saveButton = document.getElementById('recordingSaveButton'); let recordingTime = document.getElementById('recordingTime'); let recordingIcon = document.getElementById('recordingIcon'); let recordingName = document.getElementById('recordingTitle'); let recordingPoemID = globalPoemId; // это определено в вызывающем скрипте // -- init they clearButton.disabled = true; //recordButton.disabled = false; // always enabled playbackButton.disabled = true; saveButton.disabled = true; // -- Audio Player let audioPlayer = document.getElementById('recordingAudioPlayer'); // -- Recrding Data let mediaRecorder; let audioChunks = []; // let lastAudioUrl; // -- Отображение времени записи let recordingInterval; // -- Сохранение Длительности Записи let recordingStartTime; let recordingDuration; // -- Таймер для автоостановки let recordingTimeout; const formatDataSchema = { day: '2-digit', month: '2-digit', // 'long', //'2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false }; // -- Проверка поддержки микрофона в браузере function checkMicrophoneSupport() { if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) { // API поддерживается // navigator.mediaDevices.getUserMedia({ audio: true }) // .then(stream => { // console.log("Микрофон доступен."); // // Закрываем поток, так как он нам больше не нужен // stream.getTracks().forEach(track => track.stop()); // }) // .catch(err => { // // Микрофон недоступен или пользователь не дал разрешения // console.error("Микрофон недоступен: ", err); // }); } else { console.log("API для работы с медиа-устройствами не поддерживается в этом браузере."); document.getElementById('recordingMessage').innerHTML = "API для работы с медиа-устройствами не поддерживается в этом браузере."; document.getElementById('recordingMessage').hidden = false; } } checkMicrophoneSupport(); // -- Запись recordButton.addEventListener('click', startOrStopRecording); function startOrStopRecording() { if (mediaRecorder && mediaRecorder.state === "recording") { // -- Остановить запись stopRecording(); if (recordingName.value == null || recordingName.value == '') recordingName.value = 'Новая запись'; // 'Запись ' + new Date().toLocaleString('ru-RU', formatDataSchema); } else { // -- Начать запись navigator.mediaDevices.getUserMedia({ audio: true }) .then(stream => { mediaRecorder = new MediaRecorder(stream); mediaRecorder.ondataavailable = event => { audioChunks.push(event.data); }; mediaRecorder.start(); recordButton.innerHTML = ''; clearButton.disabled = true; playbackButton.disabled = true; saveButton.disabled = true; // -- Сохранение Длительности Записи recordingStartTime = new Date(); // Сохраняем время начала записи // -- Отображение времени записи recordingTime.textContent = '00:00.0'; startRecordingTimer(); // Установка таймера для автоостановки через 15 минут clearTimeout(recordingTimeout); recordingTimeout = setTimeout(() => { if (mediaRecorder.state === "recording") { stopRecording(); // Автоматическая остановка } }, 15 * 60 * 1000); // 15 минут mediaRecorder.onstop = () => { const audioBlob = new Blob(audioChunks, { type: 'audio/wav' }); lastAudioUrl = URL.createObjectURL(audioBlob); audioChunks = []; // Очищаем массив для следующей записи // ... }; // -- Создания Визуализатора Уровня Записи startAudioProcessing(stream); }) .catch(err => console.log("Ошибка доступа к микрофону: " + err)); } //mediaRecorder.onstop = () => { // const audioBlob = new Blob(audioChunks, { type: 'audio/wav' }); // lastAudioUrl = URL.createObjectURL(audioBlob); // audioChunks = []; // Очищаем массив для следующей записи // // ... //}; } function stopRecording() { // -- Остановить запись mediaRecorder.stop(); // -- Очистить таймер автоостановки clearTimeout(recordingTimeout); recordButton.innerHTML = ''; clearButton.disabled = false; playbackButton.disabled = false; saveButton.disabled = false; // -- Отображение времени записи clearInterval(recordingInterval); //recordingTime.textContent = '00:00.0'; // -- Сохранение Длительности Записи recordingDuration = new Date() - recordingStartTime; // Сохраняем длительность // -- Остановить Визуализатора Уровня Записи stopAudioProcessing(); } playbackButton.addEventListener('click', () => { if (lastAudioUrl && isAudioStopped(audioPlayer)) { // const audioPlayer = document.getElementById('audioPlayer'); audioPlayer.src = lastAudioUrl; audioPlayer.play(); } else { stopAudioPlayback(audioPlayer); // REM: no method "stop()" audioPlayer.stop(); } }); function isAudioStopped(audioElement) { return audioElement.paused || audioElement.ended || audioElement.currentTime === 0; } audioPlayer.onpause = () => { playbackButton.innerHTML = ''; }; audioPlayer.onended = () => { playbackButton.innerHTML = ''; }; audioPlayer.onplay = () => { playbackButton.innerHTML = ''; }; function stopAudioPlayback(audioPlayer) { audioPlayer.pause(); audioPlayer.currentTime = 0; } clearButton.addEventListener('click', clearRecordingsData); function clearRecordingsData() { stopRecording(); stopAudioPlayback(audioPlayer); lastAudioUrl = null; audioChunks = []; clearButton.disabled = true; // recordButton .. always enabled playbackButton.disabled = true; saveButton.disabled = true; recordingTime.textContent = '00:00.0'; recordingName.value = ''; // clear name } // --------------------------------------- // -- Отображение времени записи function startRecordingTimer() { let seconds = 0; recordingInterval = setInterval(() => { seconds += 1/10; recordingTime.textContent = new Date(seconds * 1000).toISOString().substr(14, 7); }, 100); } // -------------------------- // -- Создания Визуализатора Уровня Записи let audioContext; let analyser; let microphone; let animationFrameRequest; // Функция для преобразования числа в цвет function numberToColor(number) { // Пример преобразования: чем выше число, тем краснее цвет //const red = Math.min(255, number * 2); //const green = 255 - red; //return `rgb(${red}, ${green}, 0)`; const var1 = Math.min(255, number/1.5); const var2 = 255 - var1; return `rgb(${var2}, ${var2}, ${var2})`; } function startAudioProcessing(stream) { audioContext = new AudioContext(); analyser = audioContext.createAnalyser(); microphone = audioContext.createMediaStreamSource(stream); microphone.connect(analyser); analyser.fftSize = 256; const bufferLength = analyser.frequencyBinCount; const dataArray = new Uint8Array(bufferLength); function draw() { animationFrameRequest = requestAnimationFrame(draw); analyser.getByteFrequencyData(dataArray); let sum = 0; for (let i = 0; i < bufferLength; i++) { sum += dataArray[i]; } let average = sum / bufferLength; // Обновляем элемент на странице в соответствии с уровнем громкости // Например, обновляем ширину элемента div // document.getElementById('volumeIndicator').style.width = `${average}%`; const color = numberToColor(average); recordingIcon.style.backgroundColor = color; } draw(); } function stopMicrophone(stream) { if (stream) { stream.getTracks().forEach(track => track.stop()); } } function stopAudioProcessing() { try { if (animationFrameRequest) { cancelAnimationFrame(animationFrameRequest); } // Останавливаем поток микрофона if (microphone && microphone.mediaStream) { stopMicrophone(microphone.mediaStream); } // Дополнительно: остановите и закройте AudioContext if (audioContext && audioContext.state !== "closed" ) { audioContext.close(); } recordingIcon.style.backgroundColor = "transparent"; } catch (error) { console.error("Ошибка stopAudioProcessing:", error); } } // --------------------------------- // -- Работа с IndexedDB // -- Создание и настройка IndexedDB let db; let maxRecordsCount = 5; let currentRecordsCount = 0; function openIndexedDB() { const request = indexedDB.open("PtryLndDB_audioRecords", 1); request.onupgradeneeded = function (event) { db = event.target.result; if (!db.objectStoreNames.contains('audioStore')) { var objectStore = db.createObjectStore('audioStore', { keyPath: 'id', autoIncrement: true }); // Создаём индекс для поля 'poemId' objectStore.createIndex('poemId', 'poemId', { unique: false }); } }; request.onsuccess = function (event) { db = event.target.result; updateRecordingsList(); // Вызываем здесь, чтобы загрузить записи при открытии страницы }; request.onerror = function (event) { console.error("Ошибка IndexedDB:", event.target.errorCode); }; } openIndexedDB(); // Сохранение записей в IndexedDB saveButton.addEventListener('click', () => { if (lastAudioUrl) { const recordingNameValue = recordingName.value; if (recordingNameValue === '') { alert('Пожалуйста, введите название для записи.'); return; } if (currentRecordsCount >= maxRecordsCount) { alert(`Для одного произведения разрешено создавать и хранить не более ${maxRecordsCount} записей. Чтобы сохранить аудиозапись, удалите некоторые другие из списка.`); return; } fetch(lastAudioUrl).then(response => response.blob()).then(blob => { const transaction = db.transaction(['audioStore'], 'readwrite'); const store = transaction.objectStore('audioStore'); const request = store.add({ poemId: recordingPoemID, name: recordingNameValue, createdDate: new Date(), duration: recordingDuration, blob: blob }); request.onsuccess = () => { console.log('Аудиозапись сохранена в IndexedDB под названием:', recordingNameValue); // Очистка поля ввода и обновление списка записей // recordingName.value = ''; updateRecordingsList(); clearRecordingsData(); }; request.onerror = (e) => { console.error('Ошибка при сохранении записи:', e.target.error); }; }); } else { alert('Нет аудиозаписи для сохранения.'); } }); // -- Обновление списка записей // version 1: function updateRecordingsList() { // version 1: const list = document.getElementById('recordingsList'); // version 1: list.innerHTML = ''; // Очищаем текущий список // version 1: // version 1: const transaction = db.transaction(['audioStore'], 'readonly'); // version 1: const store = transaction.objectStore('audioStore'); // version 1: const index = store.index('poetryId'); // version 1: const request = store.openCursor(index); // version 1: // version 1: request.onsuccess = (event) => { // version 1: const cursor = event.target.result; // version 1: if (cursor) { // version 1: const listItem = document.createElement('div'); // version 1: listItem.setAttribute("class", "record-item"); // version 1: // version 1: const audioUrl = URL.createObjectURL(cursor.value.blob); // version 1: // version 1: const formatedDate = cursor.value.createdDate.toLocaleString('ru-RU', // version 1: // version 1: //{ // version 1: //day: '2-digit', // version 1: //month: '2-digit', // 'long', //'2-digit', // version 1: //year: 'numeric', // version 1: //hour: '2-digit', // version 1: //minute: '2-digit', // version 1: //hour12: false // version 1: //} // version 1: formatDataSchema // version 1: ); // version 1: // version 1: // const duration = new Date(cursor.value.duration).toISOString().substr(14, 5); // ---- Отображение Длительности в Списке // version 1: // version 1: listItem.innerHTML = ` // version 1:
${cursor.value.name}
// version 1:${formatedDate}
// version 1:${item.name}
${formatedDate}