Skip to content

Commit

Permalink
use uuid
Browse files Browse the repository at this point in the history
  • Loading branch information
pengzhendong committed Jan 14, 2025
1 parent 5083cc4 commit a19c1a7
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 64 deletions.
6 changes: 3 additions & 3 deletions wavesurfer/js/pcm-player.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
(function() {
if (typeof PCMPlayer === 'undefined') {
class PCMPlayer {
constructor(id, option) {
this.id = id
constructor(uuid, option) {
this.uuid = uuid
this.is_done = false // 是否传输完毕
this.is_playing = true
this.button = document.getElementById(`play_button_${id}`)
this.button = document.getElementById(`play_button_${uuid}`)
this.button.addEventListener('click', function() {
this.is_playing = !this.is_playing
if (!this.is_playing) {
Expand Down
22 changes: 15 additions & 7 deletions wavesurfer/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,21 @@
# limitations under the License.

import base64
import time
from typing import Optional
from uuid import uuid4

import numpy as np
from IPython.display import HTML, Audio, display


class Player:
def __init__(self, index):
self.player = f"player_{index}"
self.wavesurfer = f"wavesurfer_{index}"
def __init__(self, verbose: bool = False):
self.uuid = str(uuid4().hex)
self.display_id = None
self.has_started = False
self.created_at = time.time()
self.verbose = verbose

@staticmethod
def encode(data, rate: Optional[int] = None, with_header: bool = True):
Expand All @@ -42,12 +46,16 @@ def render(self, script):
self.display_id.update(html)

def feed(self, chunk):
if not self.has_started and self.verbose:
latency = (time.time() - self.created_at) * 1000
print(f"First chunk latency: {latency:.2f} ms")
self.has_started = True
base64_pcm = Player.encode(chunk, with_header=False)
self.render(f"{self.player}.feed('{base64_pcm}')")
self.render(f"{self.wavesurfer}.load({self.player}.url)")
self.render(f"player_{self.uuid}.feed('{base64_pcm}')")
self.render(f"wavesurfer_{self.uuid}.load(player_{self.uuid}.url)")

def set_done(self):
self.render(f"{self.player}.set_done()")
self.render(f"player_{self.uuid}.set_done()")

def destroy(self):
self.render(f"{self.player}.destroy()")
self.render(f"player_{self.uuid}.destroy()")
65 changes: 32 additions & 33 deletions wavesurfer/templates/wavesurfer.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<script>{{ script }}</script>
<style>
{{ style }}
#waveform_{{ idx }} {
#waveform_{{ uuid }} {
cursor: pointer;
position: relative;
}
#time_{{ idx }}, #duration_{{ idx }} {
#time_{{ uuid }}, #duration_{{ uuid }} {
position: absolute;
z-index: 11;
top: 95%;
Expand All @@ -15,23 +15,22 @@
background: rgba(0, 0, 0, 0.75);
color: #DDDD;
}
#time_{{ idx }} { left: 0; }
#duration_{{ idx }} { right: 0; }
#time_{{ uuid }} { left: 0; }
#duration_{{ uuid }} { right: 0; }
</style>
<div id="waveform_{{ idx }}" style="background-color: black; width: {{ width }}px;">
<div id="time_{{ idx }}">0:00</div>
<div id="duration_{{ idx }}">0:00</div>
<div id="waveform_{{ uuid }}" style="background-color: black; width: {{ width }}px;">
<div id="time_{{ uuid }}">0:00</div>
<div id="duration_{{ uuid }}">0:00</div>
</div>
{% if is_streaming %}
<button class="btn btn-primary me-3 my-3" id="play_button_{{ idx }}"> 暂停 <i class="fas fa-pause"></i></button>
<button class="btn btn-primary me-3 my-3" id="play_button_{{ uuid }}"> 暂停 <i class="fas fa-pause"></i></button>
<script>
play_button_{{ idx }} = document.getElementById('play_button_{{ idx }}')
player_{{ idx }} = new PCMPlayer({{ idx }}, { sampleRate: '{{ rate }}' });
player_{{ uuid }} = new PCMPlayer('{{ uuid }}', { sampleRate: '{{ rate }}' });
</script>
{% endif %}
<script>
wavesurfer_{{ idx }} = WaveSurfer.create({
container: '#waveform_{{ idx }}',
wavesurfer_{{ uuid }} = WaveSurfer.create({
container: '#waveform_{{ uuid }}',
autoScroll: true,
waveColor: '#4BF2A7',
cursorColor: '#FF0000',
Expand All @@ -45,54 +44,54 @@
minPxPerSec: 100,
backend: 'WebAudio',
});
wavesurfer_{{ idx }}.on('interaction', () => { wavesurfer_{{ idx }}.playPause() });
wavesurfer_{{ idx }}.on('decode', (duration) => (document.querySelector('#duration_{{ idx }}').textContent = formatTime(duration)));
wavesurfer_{{ idx }}.on('timeupdate', (currentTime) => (document.querySelector('#time_{{ idx }}').textContent = formatTime(currentTime)));
wavesurfer_{{ uuid }}.on('interaction', () => { wavesurfer_{{ uuid }}.playPause() });
wavesurfer_{{ uuid }}.on('decode', (duration) => (document.querySelector('#duration_{{ uuid }}').textContent = formatTime(duration)));
wavesurfer_{{ uuid }}.on('timeupdate', (currentTime) => (document.querySelector('#time_{{ uuid }}').textContent = formatTime(currentTime)));
{% if enable_hover %}
wavesurfer_{{ idx }}.registerPlugin(create_hover());
wavesurfer_{{ uuid }}.registerPlugin(create_hover());
{% endif %}
{% if enable_timeline %}
wavesurfer_{{ idx }}.registerPlugin(create_timeline());
wavesurfer_{{ uuid }}.registerPlugin(create_timeline());
{% endif %}
{% if enable_minimap %}
wavesurfer_{{ idx }}.registerPlugin(create_minimap());
wavesurfer_{{ uuid }}.registerPlugin(create_minimap());
{% endif %}
{% if enable_spectrogram %}
wavesurfer_{{ idx }}.registerPlugin(create_spectrogram());
wavesurfer_{{ uuid }}.registerPlugin(create_spectrogram());
{% endif %}
{% if enable_zoom %}
wavesurfer_{{ idx }}.registerPlugin(create_zoom());
wavesurfer_{{ uuid }}.registerPlugin(create_zoom());
{% endif %}
{% if enable_regions %}
wavesurfer_{{ idx }}.registerPlugin(create_regions());
activeRegion_{{ idx }} = null;
regions_{{ idx }}.on('region-clicked', (region, e) => {
wavesurfer_{{ uuid }}.registerPlugin(create_regions());
activeRegion_{{ uuid }} = null;
regions_{{ uuid }}.on('region-clicked', (region, e) => {
e.stopPropagation();
activeRegion_{{ idx }} = region;
activeRegion_{{ uuid }} = region;
region.play();
});
regions_{{ idx }}.on('region-out', (region) => {
wavesurfer_{{ idx }}.pause();
regions_{{ uuid }}.on('region-out', (region) => {
wavesurfer_{{ uuid }}.pause();
});
document.addEventListener('keydown', function(event) {
if (event.key == 'Backspace' || event.key == 'Delete') {
console.log(event.key)
if (activeRegion_{{ idx }}) {
activeRegion_{{ idx }}.remove();
if (activeRegion_{{ uuid }}) {
activeRegion_{{ uuid }}.remove();
}
}
});
{% endif %}
</script>
<button class="btn btn-success my-3", id="download_button_{{ idx }}"> 下载 <i class="fas fa-download"></i></button>
<button class="btn btn-success my-3", id="download_button_{{ uuid }}"> 下载 <i class="fas fa-download"></i></button>
<script>
download_button_{{ idx }} = document.getElementById('download_button_{{ idx }}');
download_button_{{ idx }}.addEventListener('click', function() {
download_button_{{ uuid }} = document.getElementById('download_button_{{ uuid }}');
download_button_{{ uuid }}.addEventListener('click', function() {
const link = document.createElement('a');
{% if is_streaming %}
link.href = player_{{ idx }}.url;
link.href = player_{{ uuid }}.url;
{% else %}
link.href = wavesurfer_{{ idx }}.options.url;
link.href = wavesurfer_{{ uuid }}.options.url;
{% endif %}
link.download = 'audio.wav';
document.body.appendChild(link);
Expand Down
33 changes: 12 additions & 21 deletions wavesurfer/wavesurfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@

import asyncio
import os
import time
from functools import partial
from inspect import isasyncgen, isgenerator
from pathlib import Path
from typing import Optional
from uuid import uuid4

import soundfile as sf
from IPython.display import HTML, display
Expand Down Expand Up @@ -52,30 +52,23 @@ def __init__(self):
enable_regions=False,
width=1000,
)
self.idx = -1

def render(self, audio, rate: int, is_streaming: bool = False, **kwargs):
self.idx += 1
def render(self, audio, rate: int, uuid: str = None, **kwargs):
html_code = self.template_render(
idx=self.idx,
uuid=uuid or str(uuid4().hex),
audio=audio,
rate=rate,
is_streaming=is_streaming,
is_streaming=uuid is not None,
**kwargs,
)
display(HTML(html_code))
if is_streaming:
return Player(self.idx)

def play(self, player, start, audio, rate: Optional[int] = None, verbose: bool = False, **kwargs):
def play(self, player, audio, rate: Optional[int] = None, **kwargs):
if isinstance(audio, tuple):
audio, rate = audio
if player is None:
player = self.render(None, rate, True, **kwargs)
if verbose:
print(f"First chunk latency: {(time.time() - start) * 1000:.2f} ms")
if not player.has_started:
self.render(None, rate, player.uuid, **kwargs)
player.feed(audio)
return player

def display_audio(self, audio, rate: Optional[int] = None, verbose: bool = False, **kwargs):
"""
Expand All @@ -90,24 +83,22 @@ def display_audio(self, audio, rate: Optional[int] = None, verbose: bool = False
if isinstance(audio, (str, Path)) and rate is None:
rate = sf.info(audio).samplerate
audio = Player.encode(audio, rate)
self.render(audio, rate, False, **kwargs)
self.render(audio, rate, **kwargs)

if is_streaming:
if isasyncgen(audio):

async def process_async_gen():
player = None
start = time.time()
player = Player(verbose)
async for chunk in audio:
player = self.play(player, start, chunk, rate, verbose, **kwargs)
self.play(player, chunk, rate, **kwargs)
player.set_done()

asyncio.create_task(process_async_gen())
else:
player = None
start = time.time()
player = Player(verbose)
for chunk in audio:
player = self.play(player, start, chunk, rate, verbose, **kwargs)
self.play(player, chunk, rate, **kwargs)
player.set_done()


Expand Down

0 comments on commit a19c1a7

Please sign in to comment.