audio playback fixes
This commit is contained in:
@@ -16,9 +16,6 @@ type PlaybackControlsProps = {
|
||||
|
||||
const BACKEND_LABELS: Record<BackendName, string> = {
|
||||
mpv: "mpv",
|
||||
ffplay: "ffplay",
|
||||
afplay: "afplay",
|
||||
system: "system",
|
||||
none: "none",
|
||||
};
|
||||
|
||||
@@ -60,10 +57,12 @@ export function PlaybackControls(props: PlaybackControlsProps) {
|
||||
<box flexDirection="row" gap={1} marginLeft={2}>
|
||||
<text fg={theme.textMuted}>Vol</text>
|
||||
<text fg={theme.text}>{Math.round(props.volume * 100)}%</text>
|
||||
<text fg={theme.textMuted}>↑↓</text>
|
||||
</box>
|
||||
<box flexDirection="row" gap={1} marginLeft={2}>
|
||||
<text fg={theme.textMuted}>Speed</text>
|
||||
<text fg={theme.text}>{props.speed}x</text>
|
||||
<text fg={theme.textMuted}>s</text>
|
||||
</box>
|
||||
{props.backendName && props.backendName !== "none" && (
|
||||
<box flexDirection="row" gap={1} marginLeft={2}>
|
||||
|
||||
@@ -82,8 +82,9 @@ export function PlayerPage() {
|
||||
<RealtimeWaveform
|
||||
visualizerConfig={(() => {
|
||||
const viz = useAppStore().state().settings.visualizer;
|
||||
// bars is width-derived in RealtimeWaveform; pass only the
|
||||
// audio-processing params here.
|
||||
return {
|
||||
bars: viz.bars,
|
||||
noiseReduction: viz.noiseReduction,
|
||||
lowCutOff: viz.lowCutOff,
|
||||
highCutOff: viz.highCutOff,
|
||||
@@ -109,7 +110,7 @@ export function PlayerPage() {
|
||||
|
||||
<box height={1} />
|
||||
<text fg={muted()}>
|
||||
{"P play/pause N next B prev </ seek · h back"}
|
||||
{"P play/pause N next B prev ◀▶ seek h back"}
|
||||
</text>
|
||||
</box>
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import { createSignal, createEffect, onCleanup, on, untrack } from "solid-js";
|
||||
import { useTerminalDimensions } from "@opentui/solid";
|
||||
import {
|
||||
loadCavaCore,
|
||||
type CavaCore,
|
||||
@@ -16,6 +17,7 @@ import {
|
||||
import { AudioStreamReader } from "@/utils/audio-stream-reader";
|
||||
import { useAudio } from "@/hooks/useAudio";
|
||||
import { useTheme } from "@/context/ThemeContext";
|
||||
import { PANE_RATIO } from "@/utils/navigation";
|
||||
|
||||
// ── Types ────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -51,17 +53,27 @@ export function RealtimeWaveform(props: RealtimeWaveformProps) {
|
||||
// Frequency bar values (0.0–1.0 per bar)
|
||||
const [barData, setBarData] = createSignal<number[]>([]);
|
||||
|
||||
// Track whether cavacore is available
|
||||
const [available, setAvailable] = createSignal(false);
|
||||
|
||||
let cava: CavaCore | null = null;
|
||||
let reader: AudioStreamReader | null = null;
|
||||
let frameTimer: ReturnType<typeof setInterval> | null = null;
|
||||
let sampleBuffer: Float64Array | null = null;
|
||||
|
||||
// Bar count comes from the visualizer config (set in Settings); default 64.
|
||||
// Single source of truth used for cavacore init, rendering, and seek clicks.
|
||||
const numBars = () => props.visualizerConfig?.bars ?? 64;
|
||||
// Bar count scales with terminal width so the waveform fills its pane.
|
||||
// The player is a 2-pane row: current column = (current+preview) of
|
||||
// (parent+current+preview) of the terminal width. Subtract ~8 chars of
|
||||
// chrome (scrollbox border + box padding + waveform border + padding).
|
||||
// Falls back to 64 before the renderer reports a real size.
|
||||
const dimensions = useTerminalDimensions();
|
||||
const numBars = () => {
|
||||
const total = PANE_RATIO.parent + PANE_RATIO.current + PANE_RATIO.preview;
|
||||
const current = PANE_RATIO.current + PANE_RATIO.preview; // 2-pane grows current
|
||||
const width = dimensions().width;
|
||||
if (!width) return 64;
|
||||
return Math.max(
|
||||
8,
|
||||
Math.min(256, Math.floor((width * current) / total) - 8),
|
||||
);
|
||||
};
|
||||
|
||||
// ── Lifecycle: init cavacore once ──────────────────────────────────
|
||||
|
||||
@@ -70,11 +82,9 @@ export function RealtimeWaveform(props: RealtimeWaveformProps) {
|
||||
|
||||
cava = loadCavaCore();
|
||||
if (!cava) {
|
||||
setAvailable(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
setAvailable(true);
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -85,7 +95,9 @@ export function RealtimeWaveform(props: RealtimeWaveformProps) {
|
||||
|
||||
if (!url || !initCava() || !cava) return;
|
||||
|
||||
// Initialize cavacore with current resolution + any overrides
|
||||
// Initialize cavacore with current resolution + any overrides.
|
||||
// bars is width-derived (see numBars); visualizerConfig supplies the
|
||||
// audio-processing params (noise reduction, cutoffs, etc.).
|
||||
const config: CavaCoreConfig = {
|
||||
bars: numBars(),
|
||||
sampleRate: 44100,
|
||||
@@ -140,7 +152,7 @@ export function RealtimeWaveform(props: RealtimeWaveformProps) {
|
||||
const output = cava.execute(input);
|
||||
|
||||
// Copy bar values to a new array for the signal
|
||||
setBarData(Array.from(output));
|
||||
setBarData(Array.from(output as Float64Array));
|
||||
};
|
||||
|
||||
createEffect(
|
||||
|
||||
Reference in New Issue
Block a user