feat: use sexier audio waveform (#265)
* use sexier audio waveform * format
This commit was merged in pull request #265.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Paperclip } from 'lucide-react';
|
||||
import type { RecordingMode } from '@/hooks/use-recording-mode';
|
||||
import { AudioLevelBars } from '@/components/audio/audio-level-bars';
|
||||
import type { RecordingMode } from '@/stores/media-settings-store';
|
||||
import { CenteredWaveform } from '@/components/audio/centered-waveform';
|
||||
import { useAudioSource } from '@/components/audio/use-audio-source';
|
||||
import { useObjectUrl } from '@/hooks/use-object-url';
|
||||
import { AttachmentStrip } from '@/features/compose/attachment-strip';
|
||||
@@ -67,7 +67,6 @@ function ReviewPlayback({
|
||||
objectFit?: 'cover' | 'contain';
|
||||
}) {
|
||||
const objectUrl = useObjectUrl(blob);
|
||||
const audioElRef = useRef<HTMLAudioElement | null>(null);
|
||||
const [audioEl, setAudioEl] = useState<HTMLAudioElement | null>(null);
|
||||
const audioSource = useAudioSource(isVideo ? null : audioEl);
|
||||
|
||||
@@ -87,17 +86,12 @@ function ReviewPlayback({
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<audio
|
||||
ref={(el) => {
|
||||
audioElRef.current = el;
|
||||
setAudioEl(el);
|
||||
}}
|
||||
src={objectUrl}
|
||||
autoPlay
|
||||
loop
|
||||
/>
|
||||
<audio ref={setAudioEl} src={objectUrl} autoPlay loop />
|
||||
{audioSource ? (
|
||||
<AudioLevelBars sourceNode={audioSource.sourceNode} />
|
||||
<CenteredWaveform
|
||||
sourceNode={audioSource.sourceNode}
|
||||
className="text-white"
|
||||
/>
|
||||
) : (
|
||||
<span className="text-sm text-white/60">Playing back audio...</span>
|
||||
)}
|
||||
@@ -198,10 +192,13 @@ export function RecordingOverlay({
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{/* Bottom center: audio level bars (recording with active stream) */}
|
||||
{/* Bottom center: live waveform (recording with active stream) */}
|
||||
{isRecording && recordingAudioSource && (
|
||||
<div className="z-10 absolute bottom-15">
|
||||
<AudioLevelBars sourceNode={recordingAudioSource.sourceNode} />
|
||||
<CenteredWaveform
|
||||
sourceNode={recordingAudioSource.sourceNode}
|
||||
className="text-white"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import type { RecordingMode } from '@/hooks/use-recording-mode';
|
||||
import type { RecordingMode } from '@/stores/media-settings-store';
|
||||
|
||||
const VIDEO_PREFERRED_MIME = 'video/webm;codecs=vp9,opus';
|
||||
const VIDEO_FALLBACK_MIME = 'video/webm';
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useDownloadUrl } from '@/hooks/use-download-url';
|
||||
import { useTranscriptPlayback } from '@/hooks/use-transcript-playback';
|
||||
import { TranscriptOverlay } from '@/features/particles/transcript-overlay';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { AudioLevelBars } from '@/components/audio/audio-level-bars';
|
||||
import { CenteredWaveform } from '@/components/audio/centered-waveform';
|
||||
import { useAudioSource } from '@/components/audio/use-audio-source';
|
||||
import { useParticleAttachments } from '@/hooks/use-particle-attachments';
|
||||
import { ParticleAttachments } from '@/features/particles/particle-attachments';
|
||||
@@ -89,7 +89,7 @@ export const MediaParticleView = forwardRef<
|
||||
currentTime,
|
||||
);
|
||||
|
||||
// WORKAROUND: useAudioSource needs an audio element, but audioRef is only set after mount
|
||||
// useAudioSource needs the audio element, but audioRef is only set after mount
|
||||
const [audioEl, setAudioEl] = useState<HTMLAudioElement | null>(null);
|
||||
const audioSource = useAudioSource(audioEl);
|
||||
|
||||
@@ -156,7 +156,10 @@ export const MediaParticleView = forwardRef<
|
||||
|
||||
{audioSource && (
|
||||
<div className="z-10 absolute bottom-20">
|
||||
<AudioLevelBars sourceNode={audioSource.sourceNode} />
|
||||
<CenteredWaveform
|
||||
sourceNode={audioSource.sourceNode}
|
||||
className="text-white"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { AudioLevelBars } from '@/components/audio/audio-level-bars';
|
||||
import { CenteredWaveform } from '@/components/audio/centered-waveform';
|
||||
import { useAudioSource } from '@/components/audio/use-audio-source';
|
||||
import { useMediaDevices } from '@/hooks/use-media-devices';
|
||||
import {
|
||||
@@ -105,20 +105,17 @@ function FieldLabel({ children }: { children: React.ReactNode }) {
|
||||
function InlineLevelMeter({ stream }: { stream: MediaStream | null }) {
|
||||
const audioSource = useAudioSource(stream);
|
||||
if (!audioSource) {
|
||||
return (
|
||||
<div className="flex h-3 items-end gap-1">
|
||||
{[0, 1, 2].map((i) => (
|
||||
<div key={i} className="bg-muted h-1 w-1 rounded-full" />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
return <div className="bg-muted h-3 w-20 rounded-sm opacity-50" />;
|
||||
}
|
||||
return (
|
||||
<div className="flex h-3 items-end">
|
||||
<div className="scale-[0.55] origin-right">
|
||||
<AudioLevelBars sourceNode={audioSource.sourceNode} />
|
||||
</div>
|
||||
</div>
|
||||
<CenteredWaveform
|
||||
sourceNode={audioSource.sourceNode}
|
||||
width={80}
|
||||
height={12}
|
||||
barWidth={2}
|
||||
gap={3}
|
||||
className="text-primary"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user