diff --git a/src/pwa-audio-recorder/app.mjs b/src/pwa-audio-recorder/app.mjs index b3b8134..35f0106 100644 --- a/src/pwa-audio-recorder/app.mjs +++ b/src/pwa-audio-recorder/app.mjs @@ -2,8 +2,10 @@ import {IndexedDBStorage} from './indexeddb-storage.mjs'; import * as visualize from './visualize.mjs'; +import { audioBufferToWav } from './wav-utils.mjs'; const DELETE_BUTTON_SELECTOR = '.delete-button'; +const DOWNLOAD_BUTTON_SELECTOR = '.download-button'; const RECORDING_DESCRIPTION_SELECTOR = '.recording-description'; const recordButton = document.querySelector('#record'); @@ -54,6 +56,7 @@ const RECORDING_CONFIGS = [ function populateRecordingConfigurations() { const configRadioTemplate = document.querySelector(CONFIG_RADIO_TEMPLATE_SELECTOR); const configRadios = document.querySelector(CONFIG_RADIOS_SELECTOR); + const configParam = new URLSearchParams(window.location.search).get('config'); for (const [i, {name, param}] of RECORDING_CONFIGS.entries()) { const radio = configRadioTemplate.content.firstElementChild.cloneNode(true); @@ -63,7 +66,16 @@ function populateRecordingConfigurations() { label.textContent = `${name} (${JSON.stringify(param)})`; label.setAttribute('for', input.id); label.recordingParam = param; - if (i === 0) input.checked = true; + + // Check if this config matches the query param, otherwise default to the first one + if (configParam) { + if (name === configParam) { + input.checked = true; + } + } else if (i === 0) { + input.checked = true; + } + configRadios.appendChild(radio); } } @@ -114,6 +126,28 @@ function finalizeClip({clipContainer, blob, id, recordingDescription, storage}) clipContainer.parentNode.removeChild(clipContainer); storage.delete(parseInt(id)); }; + clipContainer.querySelector(DOWNLOAD_BUTTON_SELECTOR).onclick = async () => { + const arrayBuffer = await blob.arrayBuffer(); + const audioCtx = new AudioContext(); + try { + const audioBuffer = await audioCtx.decodeAudioData(arrayBuffer); + const wavView = audioBufferToWav(audioBuffer); + const wavBlob = new Blob([wavView], { type: 'audio/wav' }); + const url = URL.createObjectURL(wavBlob); + const a = document.createElement('a'); + a.style.display = 'none'; + a.href = url; + a.download = `${recordingDescription.replace(/[:\/\s]/g, '_')}.wav`; + document.body.appendChild(a); + a.click(); + window.URL.revokeObjectURL(url); + } catch (e) { + console.error('Error converting to WAV:', e); + alert('Failed to convert audio to WAV'); + } finally { + await audioCtx.close(); + } + }; clipContainer.querySelector('audio').src = URL.createObjectURL(blob); clipContainer.classList.remove('clip-recording'); } @@ -215,26 +249,46 @@ function visualizeRecording({stream, outlineIndicator, waveformIndicator}) { } async function runTest() { - // 1. Start recording + // 1. Start recording and playback recordButton.click(); - - // 2. After 10 seconds, start playback - await new Promise((resolve) => setTimeout(resolve, 10000)); const playbackSource = document.querySelector('#playback-source'); playbackSource.play(); - // 3. After 10 seconds, stop recording and stop playback - await new Promise((resolve) => setTimeout(resolve, 10000)); - recordButton.click(); + // 2. After 15 seconds, stop playback + await new Promise((resolve) => setTimeout(resolve, 15000)); playbackSource.pause(); playbackSource.currentTime = 0; + // 3. After another 25 seconds (total 40s), stop recording + await new Promise((resolve) => setTimeout(resolve, 25000)); + recordButton.click(); + // 4. Download the recorded audio await new Promise((resolve) => setTimeout(resolve, 1000)); const clip = soundClips.firstElementChild; const audio = clip.querySelector('audio'); - const a = document.createElement('a'); - a.href = audio.src; - a.download = 'recorded_audio.webm'; - a.click(); + + try { + const response = await fetch(audio.src); + const blob = await response.blob(); + const arrayBuffer = await blob.arrayBuffer(); + const audioCtx = new AudioContext(); + const audioBuffer = await audioCtx.decodeAudioData(arrayBuffer); + const wavView = audioBufferToWav(audioBuffer); + const wavBlob = new Blob([wavView], { type: 'audio/wav' }); + const url = URL.createObjectURL(wavBlob); + + const a = document.createElement('a'); + a.href = url; + a.download = 'recorded_audio.wav'; + document.body.appendChild(a); + a.click(); + + // Clean up + await audioCtx.close(); + window.URL.revokeObjectURL(url); + document.body.removeChild(a); + } catch (e) { + console.error('Test failed to download WAV:', e); + } } diff --git a/src/pwa-audio-recorder/index.html b/src/pwa-audio-recorder/index.html index bde8427..3cb6f0c 100644 --- a/src/pwa-audio-recorder/index.html +++ b/src/pwa-audio-recorder/index.html @@ -88,7 +88,10 @@