############################################################## ### Tools for creating preference tests (MUSHRA, ABX, etc) ### ############################################################## import copy import csv import random import sys import traceback from collections import defaultdict from pathlib import Path from typing import List import gradio as gr from audiotools.core.util import find_audio ################################################################ ### Logic for audio player, and adding audio / play buttons. ### ################################################################ WAVESURFER = """
""" CUSTOM_CSS = """ .gradio-container { max-width: 840px !important; } region.wavesurfer-region:before { content: attr(data-region-label); } block { min-width: 0 !important; } #wave-timeline { background-color: rgba(0, 0, 0, 0.8); } .head.svelte-1cl284s { display: none; } """ load_wavesurfer_js = """ function load_wavesurfer() { function load_script(url) { const script = document.createElement('script'); script.src = url; document.body.appendChild(script); return new Promise((res, rej) => { script.onload = function() { res(); } script.onerror = function () { rej(); } }); } function create_wavesurfer() { var options = { container: '#waveform', waveColor: '#F2F2F2', // Set a darker wave color progressColor: 'white', // Set a slightly lighter progress color loaderColor: 'white', // Set a slightly lighter loader color cursorColor: 'black', // Set a slightly lighter cursor color backgroundColor: '#00AAFF', // Set a black background color barWidth: 4, barRadius: 3, barHeight: 1, // the height of the wave plugins: [ WaveSurfer.regions.create({ regionsMinLength: 0.0, dragSelection: { slop: 5 }, color: 'hsla(200, 50%, 70%, 0.4)', }), WaveSurfer.timeline.create({ container: "#wave-timeline", primaryLabelInterval: 5.0, secondaryLabelInterval: 1.0, primaryFontColor: '#F2F2F2', secondaryFontColor: '#F2F2F2', }), ] }; wavesurfer = WaveSurfer.create(options); wavesurfer.on('region-created', region => { wavesurfer.regions.clear(); }); wavesurfer.on('finish', function () { var loop = document.getElementById("loop-button").textContent.includes("ON"); if (loop) { wavesurfer.play(); } else { var button_elements = document.getElementsByClassName('playpause') var buttons = Array.from(button_elements); for (let j = 0; j < buttons.length; j++) { buttons[j].classList.remove("primary"); buttons[j].classList.add("secondary"); buttons[j].textContent = buttons[j].textContent.replace("Stop", "Play") } } }); wavesurfer.on('region-out', function () { var loop = document.getElementById("loop-button").textContent.includes("ON"); if (!loop) { var button_elements = document.getElementsByClassName('playpause') var buttons = Array.from(button_elements); for (let j = 0; j < buttons.length; j++) { buttons[j].classList.remove("primary"); buttons[j].classList.add("secondary"); buttons[j].textContent = buttons[j].textContent.replace("Stop", "Play") } wavesurfer.pause(); } }); console.log("Created WaveSurfer object.") } load_script('https://unpkg.com/wavesurfer.js@6.6.4') .then(() => { load_script("https://unpkg.com/wavesurfer.js@6.6.4/dist/plugin/wavesurfer.timeline.min.js") .then(() => { load_script('https://unpkg.com/wavesurfer.js@6.6.4/dist/plugin/wavesurfer.regions.min.js') .then(() => { console.log("Loaded regions"); create_wavesurfer(); document.getElementById("start-survey").click(); }) }) }); } """ play = lambda i: """ function play() { var audio_elements = document.getElementsByTagName('audio'); var button_elements = document.getElementsByClassName('playpause') var audio_array = Array.from(audio_elements); var buttons = Array.from(button_elements); var src_link = audio_array[{i}].getAttribute("src"); console.log(src_link); var loop = document.getElementById("loop-button").textContent.includes("ON"); var playing = buttons[{i}].textContent.includes("Stop"); for (let j = 0; j < buttons.length; j++) { if (j != {i} || playing) { buttons[j].classList.remove("primary"); buttons[j].classList.add("secondary"); buttons[j].textContent = buttons[j].textContent.replace("Stop", "Play") } else { buttons[j].classList.remove("secondary"); buttons[j].classList.add("primary"); buttons[j].textContent = buttons[j].textContent.replace("Play", "Stop") } } if (playing) { wavesurfer.pause(); wavesurfer.seekTo(0.0); } else { wavesurfer.load(src_link); wavesurfer.on('ready', function () { var region = Object.values(wavesurfer.regions.list)[0]; if (region != null) { region.loop = loop; region.play(); } else { wavesurfer.play(); } }); } } """.replace( "{i}", str(i) ) clear_regions = """ function clear_regions() { wavesurfer.clearRegions(); } """ reset_player = """ function reset_player() { wavesurfer.clearRegions(); wavesurfer.pause(); wavesurfer.seekTo(0.0); var button_elements = document.getElementsByClassName('playpause') var buttons = Array.from(button_elements); for (let j = 0; j < buttons.length; j++) { buttons[j].classList.remove("primary"); buttons[j].classList.add("secondary"); buttons[j].textContent = buttons[j].textContent.replace("Stop", "Play") } } """ loop_region = """ function loop_region() { var element = document.getElementById("loop-button"); var loop = element.textContent.includes("OFF"); console.log(loop); try { var region = Object.values(wavesurfer.regions.list)[0]; region.loop = loop; } catch {} if (loop) { element.classList.remove("secondary"); element.classList.add("primary"); element.textContent = "Looping ON"; } else { element.classList.remove("primary"); element.classList.add("secondary"); element.textContent = "Looping OFF"; } } """ class Player: def __init__(self, app): self.app = app self.app.load(_js=load_wavesurfer_js) self.app.css = CUSTOM_CSS self.wavs = [] self.position = 0 def create(self): gr.HTML(WAVESURFER) gr.Markdown( "Click and drag on the waveform above to select a region for playback. " "Once created, the region can be moved around and resized. " "Clear the regions using the button below. Hit play on one of the buttons below to start!" ) with gr.Row(): clear = gr.Button("Clear region") loop = gr.Button("Looping OFF", elem_id="loop-button") loop.click(None, _js=loop_region) clear.click(None, _js=clear_regions) gr.HTML("