implementing cava for real time visualization
This commit is contained in:
15
build.ts
15
build.ts
@@ -44,4 +44,19 @@ if (platformPkg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy cavacore native library to dist
|
||||||
|
const cavacoreLib = platform === "darwin"
|
||||||
|
? "libcavacore.dylib"
|
||||||
|
: platform === "win32"
|
||||||
|
? "cavacore.dll"
|
||||||
|
: "libcavacore.so"
|
||||||
|
const cavacoreSrc = join("src", "native", cavacoreLib)
|
||||||
|
|
||||||
|
if (existsSync(cavacoreSrc)) {
|
||||||
|
copyFileSync(cavacoreSrc, join("dist", cavacoreLib))
|
||||||
|
console.log(`Copied cavacore library: ${cavacoreLib}`)
|
||||||
|
} else {
|
||||||
|
console.warn(`Warning: ${cavacoreSrc} not found — run scripts/build-cavacore.sh first`)
|
||||||
|
}
|
||||||
|
|
||||||
console.log("Build complete")
|
console.log("Build complete")
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "bun src/index.tsx",
|
"start": "bun src/index.tsx",
|
||||||
"dev": "bun --watch src/index.tsx",
|
"dev": "bun --watch src/index.tsx",
|
||||||
|
"build:native": "bash scripts/build-cavacore.sh",
|
||||||
"build": "bun run build.ts",
|
"build": "bun run build.ts",
|
||||||
"dist": "bun dist/index.js",
|
"dist": "bun dist/index.js",
|
||||||
"test": "bun test",
|
"test": "bun test",
|
||||||
|
|||||||
79
scripts/build-cavacore.sh
Executable file
79
scripts/build-cavacore.sh
Executable file
@@ -0,0 +1,79 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Build cavacore as a shared library with fftw3 statically linked.
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# macOS: brew install fftw
|
||||||
|
# Linux: apt install libfftw3-dev (or equivalent)
|
||||||
|
#
|
||||||
|
# Output: src/native/libcavacore.{dylib,so}
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
SRC="$ROOT/cava/cavacore.c"
|
||||||
|
OUT_DIR="$ROOT/src/native"
|
||||||
|
|
||||||
|
mkdir -p "$OUT_DIR"
|
||||||
|
|
||||||
|
OS="$(uname -s)"
|
||||||
|
ARCH="$(uname -m)"
|
||||||
|
|
||||||
|
# Resolve fftw3 paths
|
||||||
|
if [ "$OS" = "Darwin" ]; then
|
||||||
|
if [ "$ARCH" = "arm64" ]; then
|
||||||
|
FFTW_PREFIX="${FFTW_PREFIX:-/opt/homebrew}"
|
||||||
|
else
|
||||||
|
FFTW_PREFIX="${FFTW_PREFIX:-/usr/local}"
|
||||||
|
fi
|
||||||
|
LIB_EXT="dylib"
|
||||||
|
SHARED_FLAG="-dynamiclib"
|
||||||
|
INSTALL_NAME="-install_name @rpath/libcavacore.dylib"
|
||||||
|
else
|
||||||
|
FFTW_PREFIX="${FFTW_PREFIX:-/usr}"
|
||||||
|
LIB_EXT="so"
|
||||||
|
SHARED_FLAG="-shared"
|
||||||
|
INSTALL_NAME=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
FFTW_INCLUDE="$FFTW_PREFIX/include"
|
||||||
|
FFTW_STATIC="$FFTW_PREFIX/lib/libfftw3.a"
|
||||||
|
|
||||||
|
if [ ! -f "$FFTW_STATIC" ]; then
|
||||||
|
echo "Error: libfftw3.a not found at $FFTW_STATIC"
|
||||||
|
echo "Install fftw3: brew install fftw (macOS) or apt install libfftw3-dev (Linux)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$SRC" ]; then
|
||||||
|
echo "Error: cavacore.c not found at $SRC"
|
||||||
|
echo "Ensure the cava submodule is initialized: git submodule update --init"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
OUT="$OUT_DIR/libcavacore.$LIB_EXT"
|
||||||
|
|
||||||
|
echo "Building libcavacore.$LIB_EXT ($OS $ARCH)"
|
||||||
|
echo " Source: $SRC"
|
||||||
|
echo " FFTW3: $FFTW_STATIC"
|
||||||
|
echo " Output: $OUT"
|
||||||
|
|
||||||
|
cc -O2 \
|
||||||
|
$SHARED_FLAG \
|
||||||
|
$INSTALL_NAME \
|
||||||
|
-fPIC \
|
||||||
|
-I"$FFTW_INCLUDE" \
|
||||||
|
-I"$ROOT/cava" \
|
||||||
|
-o "$OUT" \
|
||||||
|
"$SRC" \
|
||||||
|
"$FFTW_STATIC" \
|
||||||
|
-lm
|
||||||
|
|
||||||
|
echo "Built: $OUT"
|
||||||
|
|
||||||
|
# Verify exported symbols
|
||||||
|
if [ "$OS" = "Darwin" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "Exported symbols:"
|
||||||
|
nm -gU "$OUT" | grep "cava_"
|
||||||
|
fi
|
||||||
BIN
src/native/libcavacore.dylib
Executable file
BIN
src/native/libcavacore.dylib
Executable file
Binary file not shown.
@@ -0,0 +1,57 @@
|
|||||||
|
# 01. Copy cavacore library files to project
|
||||||
|
|
||||||
|
meta:
|
||||||
|
id: real-time-audio-visualization-01
|
||||||
|
feature: real-time-audio-visualization
|
||||||
|
priority: P0
|
||||||
|
depends_on: []
|
||||||
|
tags: [setup, build]
|
||||||
|
|
||||||
|
objective:
|
||||||
|
- Copy necessary cava library files from cava/ directory to src/utils/ for integration
|
||||||
|
|
||||||
|
deliverables:
|
||||||
|
- src/utils/cavacore.h - Header file with cavacore API
|
||||||
|
- src/utils/cavacore.c - Implementation of cavacore library
|
||||||
|
- src/utils/audio-stream.h - Audio stream reader header
|
||||||
|
- src/utils/audio-stream.c - Audio stream reader implementation
|
||||||
|
- src/utils/audio-input.h - Common audio input types
|
||||||
|
- src/utils/audio-input.c - Audio input buffer management
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- Identify necessary files from cava/ directory:
|
||||||
|
- cavacore.h (API definition)
|
||||||
|
- cavacore.c (FFT processing implementation)
|
||||||
|
- input/common.h (common audio data structures)
|
||||||
|
- input/common.c (input buffer handling)
|
||||||
|
- input/fifo.h (FIFO input support - optional, for testing)
|
||||||
|
- input/fifo.c (FIFO input implementation - optional)
|
||||||
|
- Copy cavacore.h to src/utils/
|
||||||
|
- Copy cavacore.c to src/utils/
|
||||||
|
- Copy input/common.h to src/utils/
|
||||||
|
- Copy input/common.c to src/utils/
|
||||||
|
- Copy input/fifo.h to src/utils/ (optional)
|
||||||
|
- Copy input/fifo.c to src/utils/ (optional)
|
||||||
|
- Update file headers to indicate origin and licensing
|
||||||
|
- Note: Files from cava/ directory will be removed after integration
|
||||||
|
|
||||||
|
tests:
|
||||||
|
- Unit: Verify all files compile successfully
|
||||||
|
- Integration: Ensure no import errors in TypeScript/JavaScript files
|
||||||
|
- Manual: Check that files are accessible from src/utils/
|
||||||
|
|
||||||
|
acceptance_criteria:
|
||||||
|
- All required cava files are copied to src/utils/
|
||||||
|
- File headers include proper copyright and license information
|
||||||
|
- No compilation errors from missing dependencies
|
||||||
|
- Files are properly formatted for TypeScript/JavaScript integration
|
||||||
|
|
||||||
|
validation:
|
||||||
|
- Run: `bun run build` to verify compilation
|
||||||
|
- Check: `ls src/utils/*.c src/utils/*.h` to confirm file presence
|
||||||
|
|
||||||
|
notes:
|
||||||
|
- Only need cavacore.c, cavacore.h, and common.c/common.h for basic functionality
|
||||||
|
- input/fifo.c is optional - can be added later if needed
|
||||||
|
- FFTW library will need to be installed and linked separately
|
||||||
|
- The files will be integrated into the audio-waveform utility
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
# 02. Integrate cavacore library for audio analysis
|
||||||
|
|
||||||
|
meta:
|
||||||
|
id: real-time-audio-visualization-02
|
||||||
|
feature: real-time-audio-visualization
|
||||||
|
priority: P0
|
||||||
|
depends_on: [real-time-audio-visualization-01]
|
||||||
|
tags: [integration, audio-processing]
|
||||||
|
|
||||||
|
objective:
|
||||||
|
- Create a TypeScript binding for the cavacore C library
|
||||||
|
- Provide async API for real-time audio frequency analysis
|
||||||
|
|
||||||
|
deliverables:
|
||||||
|
- src/utils/cavacore.ts - TypeScript bindings for cavacore API
|
||||||
|
- src/utils/audio-visualizer.ts - High-level audio visualizer class
|
||||||
|
- Updated package.json with FFTW dependency
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- Review cavacore.h API and understand the interface:
|
||||||
|
- cava_init() - Initialize with parameters
|
||||||
|
- cava_execute() - Process samples and return frequencies
|
||||||
|
- cava_destroy() - Clean up
|
||||||
|
- Create cavacore.ts wrapper with TypeScript types:
|
||||||
|
- Define C-style structs as TypeScript interfaces
|
||||||
|
- Create bind() function to load shared library
|
||||||
|
- Implement async wrappers for init, execute, destroy
|
||||||
|
- Create audio-visualizer.ts class:
|
||||||
|
- Handle initialization with configurable parameters (bars, sensitivity, noise reduction)
|
||||||
|
- Provide execute() method that accepts audio samples and returns frequency data
|
||||||
|
- Manage cleanup and error handling
|
||||||
|
- Update package.json:
|
||||||
|
- Add @types/fftw3 dependency (if available) or document manual installation
|
||||||
|
- Add build instructions for linking FFTW library
|
||||||
|
- Test basic initialization and execution with dummy data
|
||||||
|
|
||||||
|
tests:
|
||||||
|
- Unit: Test cavacore initialization with valid parameters
|
||||||
|
- Unit: Test cavacore execution with sample audio data
|
||||||
|
- Unit: Test cleanup and memory management
|
||||||
|
- Integration: Verify no memory leaks after multiple init/destroy cycles
|
||||||
|
- Integration: Test with actual audio data from ffmpeg
|
||||||
|
|
||||||
|
acceptance_criteria:
|
||||||
|
- cavacore.ts compiles without TypeScript errors
|
||||||
|
- audio-visualizer.ts can be imported and initialized
|
||||||
|
- execute() method returns frequency data array
|
||||||
|
- Proper error handling for missing FFTW library
|
||||||
|
- No memory leaks in long-running tests
|
||||||
|
|
||||||
|
validation:
|
||||||
|
- Run: `bun run build` to verify TypeScript compilation
|
||||||
|
- Run: `bun test` for unit tests
|
||||||
|
- Manual: Test with sample audio file and verify output
|
||||||
|
|
||||||
|
notes:
|
||||||
|
- FFTW library needs to be installed separately on the system
|
||||||
|
- On macOS: brew install fftw
|
||||||
|
- On Linux: apt install libfftw3-dev
|
||||||
|
- The C code will need to be compiled into a shared library (.so/.dylib/.dll)
|
||||||
|
- For Bun, we can use `Bun.native()` or `Bun.ffi` to call C functions
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# 03. Create audio stream reader for real-time data
|
||||||
|
|
||||||
|
meta:
|
||||||
|
id: real-time-audio-visualization-03
|
||||||
|
feature: real-time-audio-visualization
|
||||||
|
priority: P1
|
||||||
|
depends_on: [real-time-audio-visualization-02]
|
||||||
|
tags: [audio-stream, real-time]
|
||||||
|
|
||||||
|
objective:
|
||||||
|
- Create a mechanism to read audio stream from mpv backend
|
||||||
|
- Convert audio data to format suitable for cavacore processing
|
||||||
|
- Implement efficient buffer management
|
||||||
|
|
||||||
|
deliverables:
|
||||||
|
- src/utils/audio-stream-reader.ts - Audio stream reader class
|
||||||
|
- src/utils/audio-stream-reader.test.ts - Unit tests
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- Design audio stream reader interface:
|
||||||
|
- Constructor accepts audio URL and backend (mpv)
|
||||||
|
- Start() method initiates audio playback and stream capture
|
||||||
|
- readSamples() method returns next batch of audio samples
|
||||||
|
- stop() method terminates stream capture
|
||||||
|
- Implement stream reading for mpv backend:
|
||||||
|
- Use mpv IPC to query audio device parameters (sample rate, channels)
|
||||||
|
- Use ffmpeg or similar to pipe audio output to stdin
|
||||||
|
- Read PCM samples from the stream
|
||||||
|
- Convert audio samples to appropriate format:
|
||||||
|
- Handle different bit depths (16-bit, 32-bit)
|
||||||
|
- Handle different sample rates (44100, 48000, etc.)
|
||||||
|
- Interleave stereo channels if needed
|
||||||
|
- Implement buffer management:
|
||||||
|
- Circular buffer for efficient sample storage
|
||||||
|
- Non-blocking read with timeout
|
||||||
|
- Sample rate conversion if needed
|
||||||
|
- Handle errors:
|
||||||
|
- Invalid audio URL
|
||||||
|
- Backend connection failure
|
||||||
|
- Sample format mismatch
|
||||||
|
- Create unit tests:
|
||||||
|
- Mock mpv backend
|
||||||
|
- Test sample reading
|
||||||
|
- Test buffer management
|
||||||
|
- Test error conditions
|
||||||
|
|
||||||
|
tests:
|
||||||
|
- Unit: Test sample rate detection
|
||||||
|
- Unit: Test channel detection
|
||||||
|
- Unit: Test sample reading with valid data
|
||||||
|
- Unit: Test buffer overflow handling
|
||||||
|
- Unit: Test error handling for invalid audio
|
||||||
|
- Integration: Test with actual audio file and mpv
|
||||||
|
- Integration: Test with ffplay backend
|
||||||
|
|
||||||
|
acceptance_criteria:
|
||||||
|
- Audio stream reader successfully reads audio data from mpv
|
||||||
|
- Samples are converted to 16-bit PCM format
|
||||||
|
- Buffer management prevents overflow
|
||||||
|
- Error handling works for invalid audio
|
||||||
|
- No memory leaks in long-running tests
|
||||||
|
|
||||||
|
validation:
|
||||||
|
- Run: `bun test` for unit tests
|
||||||
|
- Manual: Play audio and verify stream reader captures data
|
||||||
|
- Manual: Test with different audio formats (mp3, wav, m4a)
|
||||||
|
|
||||||
|
notes:
|
||||||
|
- mpv can output audio via pipe to stdin using --audio-file-pipe
|
||||||
|
- Alternative: Use ffmpeg to re-encode audio to standard format
|
||||||
|
- Sample rate conversion may be needed for cavacore compatibility
|
||||||
|
- For simplicity, start with 16-bit PCM, single channel (mono)
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# 04. Create realtime waveform component
|
||||||
|
|
||||||
|
meta:
|
||||||
|
id: real-time-audio-visualization-04
|
||||||
|
feature: real-time-audio-visualization
|
||||||
|
priority: P1
|
||||||
|
depends_on: [real-time-audio-visualization-03]
|
||||||
|
tags: [component, ui]
|
||||||
|
|
||||||
|
objective:
|
||||||
|
- Create a SolidJS component that displays real-time audio visualization
|
||||||
|
- Integrate audio-visualizer and audio-stream-reader
|
||||||
|
- Display frequency data as visual waveform bars
|
||||||
|
|
||||||
|
deliverables:
|
||||||
|
- src/components/RealtimeWaveform.tsx - Real-time waveform component
|
||||||
|
- src/components/RealtimeWaveform.test.tsx - Component tests
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- Create RealtimeWaveform component:
|
||||||
|
- Accept props: audioUrl, position, duration, isPlaying, onSeek, resolution
|
||||||
|
- Initialize audio-visualizer with cavacore
|
||||||
|
- Initialize audio-stream-reader for mpv backend
|
||||||
|
- Create render loop that:
|
||||||
|
- Reads audio samples from stream reader
|
||||||
|
- Passes samples to cavacore execute()
|
||||||
|
- Gets frequency data back
|
||||||
|
- Maps frequency data to visual bars
|
||||||
|
- Renders bars with appropriate colors
|
||||||
|
- Implement rendering logic:
|
||||||
|
- Map frequency values to bar heights
|
||||||
|
- Color-code bars based on intensity
|
||||||
|
- Handle played vs unplayed portions
|
||||||
|
- Support click-to-seek
|
||||||
|
- Create visual style:
|
||||||
|
- Use terminal block characters for bars
|
||||||
|
- Apply colors based on frequency bands (bass, mid, treble)
|
||||||
|
- Add visual flair (gradients, glow effects if possible)
|
||||||
|
- Implement state management:
|
||||||
|
- Track current frequency data
|
||||||
|
- Track playback position
|
||||||
|
- Handle component lifecycle (cleanup)
|
||||||
|
- Create unit tests:
|
||||||
|
- Test component initialization
|
||||||
|
- Test render loop
|
||||||
|
- Test click-to-seek
|
||||||
|
- Test cleanup
|
||||||
|
|
||||||
|
tests:
|
||||||
|
- Unit: Test component props
|
||||||
|
- Unit: Test frequency data mapping
|
||||||
|
- Unit: Test visual bar rendering
|
||||||
|
- Integration: Test with mock audio data
|
||||||
|
- Integration: Test with actual audio playback
|
||||||
|
|
||||||
|
acceptance_criteria:
|
||||||
|
- Component renders without errors
|
||||||
|
- Visual bars update in real-time during playback
|
||||||
|
- Frequency data is correctly calculated from audio samples
|
||||||
|
- Click-to-seek works
|
||||||
|
- Component cleans up resources properly
|
||||||
|
- Visual style matches design requirements
|
||||||
|
|
||||||
|
validation:
|
||||||
|
- Run: `bun test` for unit tests
|
||||||
|
- Manual: Play audio and verify visualization updates
|
||||||
|
- Manual: Test seeking and verify visualization follows
|
||||||
|
- Performance: Monitor frame rate and CPU usage
|
||||||
|
|
||||||
|
notes:
|
||||||
|
- Use SolidJS createEffect for reactive updates
|
||||||
|
- Keep render loop efficient to maintain 60fps
|
||||||
|
- Consider debouncing if processing is too heavy
|
||||||
|
- May need to adjust sample rate for performance
|
||||||
|
- Visual style should complement existing MergedWaveform design
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# 05. Update Player component to use realtime visualization
|
||||||
|
|
||||||
|
meta:
|
||||||
|
id: real-time-audio-visualization-05
|
||||||
|
feature: real-time-audio-visualization
|
||||||
|
priority: P1
|
||||||
|
depends_on: [real-time-audio-visualization-04]
|
||||||
|
tags: [integration, player]
|
||||||
|
|
||||||
|
objective:
|
||||||
|
- Replace static waveform display with real-time visualization
|
||||||
|
- Update Player.tsx to use RealtimeWaveform component
|
||||||
|
- Ensure seamless transition and proper state management
|
||||||
|
|
||||||
|
deliverables:
|
||||||
|
- Updated src/components/Player.tsx
|
||||||
|
- Updated src/components/MergedWaveform.tsx (optional, for fallback)
|
||||||
|
- Documentation of changes
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- Update Player.tsx:
|
||||||
|
- Import RealtimeWaveform component
|
||||||
|
- Replace MergedWaveform with RealtimeWaveform
|
||||||
|
- Pass same props (audioUrl, position, duration, isPlaying, onSeek)
|
||||||
|
- Remove audioUrl from props if no longer needed
|
||||||
|
- Test with different audio formats
|
||||||
|
- Add fallback handling:
|
||||||
|
- If realtime visualization fails, show static waveform
|
||||||
|
- Graceful degradation for systems without FFTW
|
||||||
|
- Update component documentation
|
||||||
|
- Test all player controls work with new visualization
|
||||||
|
- Verify keyboard shortcuts still work
|
||||||
|
- Test seek, pause, resume, volume, speed controls
|
||||||
|
|
||||||
|
tests:
|
||||||
|
- Unit: Test Player with RealtimeWaveform
|
||||||
|
- Integration: Test complete playback flow
|
||||||
|
- Integration: Test seek functionality
|
||||||
|
- Integration: Test pause/resume
|
||||||
|
- Integration: Test volume and speed changes
|
||||||
|
- Integration: Test with different audio formats
|
||||||
|
- Manual: Verify all player features work correctly
|
||||||
|
|
||||||
|
acceptance_criteria:
|
||||||
|
- Player displays real-time visualization during playback
|
||||||
|
- All player controls work correctly
|
||||||
|
- Seek functionality works with visualization
|
||||||
|
- Graceful fallback for systems without FFTW
|
||||||
|
- No regression in existing functionality
|
||||||
|
- Visual style matches design requirements
|
||||||
|
|
||||||
|
validation:
|
||||||
|
- Run: `bun run build` to verify compilation
|
||||||
|
- Run: `bun test` for integration tests
|
||||||
|
- Manual: Play various audio files
|
||||||
|
- Manual: Test all keyboard shortcuts
|
||||||
|
- Performance: Monitor frame rate and CPU usage
|
||||||
|
|
||||||
|
notes:
|
||||||
|
- Keep MergedWaveform as fallback option
|
||||||
|
- Consider showing a loading state while visualizer initializes
|
||||||
|
- May need to handle the case where mpv doesn't support audio pipe
|
||||||
|
- The visualizer should integrate smoothly with existing Player layout
|
||||||
|
- Consider adding a toggle to switch between static and realtime visualization
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# 06. Add visualizer controls and settings
|
||||||
|
|
||||||
|
meta:
|
||||||
|
id: real-time-audio-visualization-06
|
||||||
|
feature: real-time-audio-visualization
|
||||||
|
priority: P2
|
||||||
|
depends_on: [real-time-audio-visualization-05]
|
||||||
|
tags: [ui, controls, settings]
|
||||||
|
|
||||||
|
objective:
|
||||||
|
- Add user controls for visualizer settings
|
||||||
|
- Create settings panel for customization
|
||||||
|
- Allow users to adjust visualizer parameters
|
||||||
|
|
||||||
|
deliverables:
|
||||||
|
- src/components/VisualizerSettings.tsx - Settings component
|
||||||
|
- Updated src/components/Player.tsx - Settings panel integration
|
||||||
|
- src/types/settings.ts - Visualizer settings type definition
|
||||||
|
- src/stores/settings.ts - Settings state management
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- Define visualizer settings types:
|
||||||
|
- Number of bars (resolution)
|
||||||
|
- Sensitivity (autosens toggle + manual value)
|
||||||
|
- Noise reduction level
|
||||||
|
- Frequency cutoffs (low/high)
|
||||||
|
- Bar width and spacing
|
||||||
|
- Color scheme options
|
||||||
|
- Create VisualizerSettings component:
|
||||||
|
- Display current settings
|
||||||
|
- Allow adjusting each parameter
|
||||||
|
- Show real-time feedback
|
||||||
|
- Save settings to app store
|
||||||
|
- Integrate with Player component:
|
||||||
|
- Add settings button
|
||||||
|
- Show settings panel when toggled
|
||||||
|
- Apply settings to RealtimeWaveform component
|
||||||
|
- Update settings state management:
|
||||||
|
- Load saved settings from app store
|
||||||
|
- Save settings on change
|
||||||
|
- Provide default values
|
||||||
|
- Create UI for settings:
|
||||||
|
- Keyboard shortcuts for quick adjustment
|
||||||
|
- Visual feedback for changes
|
||||||
|
- Help text for each setting
|
||||||
|
- Add settings persistence
|
||||||
|
- Create tests for settings component
|
||||||
|
|
||||||
|
tests:
|
||||||
|
- Unit: Test settings type definitions
|
||||||
|
- Unit: Test settings state management
|
||||||
|
- Unit: Test VisualizerSettings component
|
||||||
|
- Integration: Test settings apply to visualization
|
||||||
|
- Integration: Test settings persistence
|
||||||
|
- Manual: Test all settings controls
|
||||||
|
|
||||||
|
acceptance_criteria:
|
||||||
|
- VisualizerSettings component renders correctly
|
||||||
|
- All settings can be adjusted
|
||||||
|
- Changes apply in real-time
|
||||||
|
- Settings persist between sessions
|
||||||
|
- Keyboard shortcuts work
|
||||||
|
- Component handles invalid settings gracefully
|
||||||
|
|
||||||
|
validation:
|
||||||
|
- Run: `bun test` for unit tests
|
||||||
|
- Run: `bun test` for integration tests
|
||||||
|
- Manual: Test all settings
|
||||||
|
- Manual: Test keyboard shortcuts
|
||||||
|
- Manual: Test settings persistence
|
||||||
|
|
||||||
|
notes:
|
||||||
|
- Settings should have sensible defaults
|
||||||
|
- Some settings may require visualizer re-initialization
|
||||||
|
- Consider limiting certain settings to avoid performance issues
|
||||||
|
- Add tooltips or help text for complex settings
|
||||||
|
- Settings should be optional - users can start without them
|
||||||
|
- Keep UI simple and intuitive
|
||||||
30
tasks/real-time-audio-visualization/README.md
Normal file
30
tasks/real-time-audio-visualization/README.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Real-time Audio Visualization
|
||||||
|
|
||||||
|
Objective: Integrate cava library for real-time audio visualization in Player component
|
||||||
|
|
||||||
|
Status legend: [ ] todo, [~] in-progress, [x] done
|
||||||
|
|
||||||
|
Tasks
|
||||||
|
- [x] 01 — Copy cavacore library files to project → `01-copy-cavacore-files.md`
|
||||||
|
- [ ] 02 — Integrate cavacore library for audio analysis → `02-integrate-cavacore-library.md`
|
||||||
|
- [ ] 03 — Create audio stream reader for real-time data → `03-create-audio-stream-reader.md`
|
||||||
|
- [ ] 04 — Create realtime waveform component → `04-create-realtime-waveform-component.md`
|
||||||
|
- [ ] 05 — Update Player component to use realtime visualization → `05-update-player-visualization.md`
|
||||||
|
- [ ] 06 — Add visualizer controls and settings → `06-add-visualizer-controls.md`
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
- 01 depends on (none)
|
||||||
|
- 02 depends on 01
|
||||||
|
- 03 depends on 02
|
||||||
|
- 04 depends on 03
|
||||||
|
- 05 depends on 04
|
||||||
|
- 06 depends on 05
|
||||||
|
|
||||||
|
Exit criteria
|
||||||
|
- Audio visualization updates in real-time during playback
|
||||||
|
- Waveform bars respond to actual audio frequencies
|
||||||
|
- Visualizer controls (sensitivity, bar count) work
|
||||||
|
- Performance is smooth with 60fps updates
|
||||||
|
- All necessary cava files are integrated into project
|
||||||
|
|
||||||
|
Note: Files from cava/ directory will be removed after integration
|
||||||
58
tests/cavacore-smoke.ts
Normal file
58
tests/cavacore-smoke.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Smoke test: load libcavacore.dylib via bun:ffi, init → execute → destroy.
|
||||||
|
* Run: bun tests/cavacore-smoke.ts
|
||||||
|
*/
|
||||||
|
import { dlopen, FFIType, ptr } from "bun:ffi"
|
||||||
|
import { join } from "path"
|
||||||
|
|
||||||
|
const libPath = join(import.meta.dir, "..", "src", "native", "libcavacore.dylib")
|
||||||
|
|
||||||
|
const lib = dlopen(libPath, {
|
||||||
|
cava_init: {
|
||||||
|
args: [FFIType.i32, FFIType.u32, FFIType.i32, FFIType.i32, FFIType.double, FFIType.i32, FFIType.i32],
|
||||||
|
returns: FFIType.ptr,
|
||||||
|
},
|
||||||
|
cava_execute: {
|
||||||
|
args: [FFIType.ptr, FFIType.i32, FFIType.ptr, FFIType.ptr],
|
||||||
|
returns: FFIType.void,
|
||||||
|
},
|
||||||
|
cava_destroy: {
|
||||||
|
args: [FFIType.ptr],
|
||||||
|
returns: FFIType.void,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const bars = 10
|
||||||
|
const rate = 44100
|
||||||
|
const channels = 1
|
||||||
|
|
||||||
|
// Init
|
||||||
|
const plan = lib.symbols.cava_init(bars, rate, channels, 1, 0.77, 50, 10000)
|
||||||
|
if (!plan) {
|
||||||
|
console.error("FAIL: cava_init returned null")
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
console.log("cava_init OK, plan pointer:", plan)
|
||||||
|
|
||||||
|
// Generate a 200Hz sine wave test signal
|
||||||
|
const bufferSize = 512
|
||||||
|
const cavaIn = new Float64Array(bufferSize)
|
||||||
|
const cavaOut = new Float64Array(bars * channels)
|
||||||
|
|
||||||
|
for (let k = 0; k < 100; k++) {
|
||||||
|
for (let n = 0; n < bufferSize; n++) {
|
||||||
|
cavaIn[n] = Math.sin(2 * Math.PI * 200 / rate * (n + k * bufferSize)) * 20000
|
||||||
|
}
|
||||||
|
lib.symbols.cava_execute(ptr(cavaIn), bufferSize, ptr(cavaOut), plan)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("cava_execute OK, output:", Array.from(cavaOut).map(v => v.toFixed(3)))
|
||||||
|
|
||||||
|
// Check that bar 2 (200Hz) has the peak
|
||||||
|
const maxIdx = cavaOut.indexOf(Math.max(...cavaOut))
|
||||||
|
console.log(`Peak at bar ${maxIdx} (expected ~2 for 200Hz)`)
|
||||||
|
|
||||||
|
// Destroy
|
||||||
|
lib.symbols.cava_destroy(plan)
|
||||||
|
console.log("cava_destroy OK")
|
||||||
|
console.log("\nSMOKE TEST PASSED")
|
||||||
Reference in New Issue
Block a user