feat: implement Phase 8.1 - audio recording infrastructure
Added recording capabilities to the multi-track editor: - useRecording hook with MediaRecorder API integration - Audio input device enumeration and selection - Microphone permission handling - Input level monitoring with RMS calculation - InputLevelMeter component with visual feedback - Record-enable button per track with pulsing indicator - Real-time input level display when recording Recording infrastructure is complete. Next: integrate into AudioEditor for global recording control and buffer storage. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import * as React from 'react';
|
||||
import { Volume2, VolumeX, Headphones, Trash2, ChevronDown, ChevronRight, CircleArrowOutUpRight, Upload, Plus } from 'lucide-react';
|
||||
import { Volume2, VolumeX, Headphones, Trash2, ChevronDown, ChevronRight, CircleArrowOutUpRight, Upload, Plus, Mic } from 'lucide-react';
|
||||
import type { Track as TrackType } from '@/types/track';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Slider } from '@/components/ui/Slider';
|
||||
@@ -9,6 +9,7 @@ import { cn } from '@/lib/utils/cn';
|
||||
import { EffectBrowser } from '@/components/effects/EffectBrowser';
|
||||
import { EffectDevice } from '@/components/effects/EffectDevice';
|
||||
import { createEffect, type EffectType } from '@/lib/audio/effects/chain';
|
||||
import { InputLevelMeter } from '@/components/recording/InputLevelMeter';
|
||||
|
||||
export interface TrackProps {
|
||||
track: TrackType;
|
||||
@@ -31,6 +32,9 @@ export interface TrackProps {
|
||||
onUpdateEffect?: (effectId: string, parameters: any) => void;
|
||||
onAddEffect?: (effectType: EffectType) => void;
|
||||
onSelectionChange?: (selection: { start: number; end: number } | null) => void;
|
||||
onToggleRecordEnable?: () => void;
|
||||
isRecording?: boolean;
|
||||
recordingLevel?: number;
|
||||
}
|
||||
|
||||
export function Track({
|
||||
@@ -54,6 +58,9 @@ export function Track({
|
||||
onUpdateEffect,
|
||||
onAddEffect,
|
||||
onSelectionChange,
|
||||
onToggleRecordEnable,
|
||||
isRecording = false,
|
||||
recordingLevel = 0,
|
||||
}: TrackProps) {
|
||||
const canvasRef = React.useRef<HTMLCanvasElement>(null);
|
||||
const containerRef = React.useRef<HTMLDivElement>(null);
|
||||
@@ -439,6 +446,22 @@ export function Track({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Record Enable Button */}
|
||||
{onToggleRecordEnable && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
onClick={onToggleRecordEnable}
|
||||
title="Arm track for recording"
|
||||
className={cn(
|
||||
track.recordEnabled && 'bg-red-500/20 hover:bg-red-500/30',
|
||||
isRecording && 'animate-pulse'
|
||||
)}
|
||||
>
|
||||
<Mic className={cn('h-4 w-4', track.recordEnabled && 'text-red-500')} />
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* Solo Button */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -523,6 +546,22 @@ export function Track({
|
||||
: `R${Math.round(track.pan * 100)}`}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Input Level Meter (shown when recording or record-enabled) */}
|
||||
{(track.recordEnabled || isRecording) && (
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-xs text-muted-foreground flex items-center gap-1 w-16 flex-shrink-0">
|
||||
<Mic className="h-3 w-3" />
|
||||
Input
|
||||
</label>
|
||||
<div className="flex-1">
|
||||
<InputLevelMeter level={recordingLevel} orientation="horizontal" />
|
||||
</div>
|
||||
<span className="text-xs text-muted-foreground w-10 text-right flex-shrink-0">
|
||||
{Math.round(recordingLevel * 100)}%
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user