- AudioTapContext tracks: peak amplitude, active/silent call counts,
skip/error counters, last active timestamp
- tapProcess computes peak per buffer (every 16th sample for perf)
- writeDiagnosticReport() generates .txt report after each recording:
* Verdict: OK / FAIL / WARN with specific diagnosis
* Full tap stats, format info, skip/error breakdown
* 'How to Read' section for quick interpretation
- Report saved alongside mp4 (same name, .txt extension)
- showSaveDialog moves both mp4 and report together
- Replace single dataBuffer with double-buffer (dataBuffers tuple)
to eliminate data race between real-time tapProcess thread and
async dispatch queue reading the buffer
- Use kCMPersistentTrackID_Invalid for audioMix trackID so the tap
survives HLS variant switches (track ID changes on quality change)
MTAudioProcessingTap with AVPlayerItem.audioMix doesn't fire its process
callback for HLS streams on macOS (known limitation). This caused recorded
videos to have no audio track when recording HLS content.
Replace with CoreAudio AudioDeviceCreateIOProcID approach that captures
PCM audio directly from the system output device. This works for all
audio sources (HLS, local files, etc).
Key changes:
- Remove MTAudioProcessingTap and all tap callbacks
- Add CoreAudio device IOProc for audio capture
- Pre-allocated buffer to avoid malloc in real-time audio thread
- Serial dispatch queue for CMSampleBuffer processing off audio thread
- AudioTimeStamp → CMTime conversion for proper timestamps
- RecorderState.isFinalizing flag set during stopRecording → finishWriting
- applicationShouldTerminate returns .terminateLater if finalizing
- Shows alert '正在保存录制文件...' to inform user
- After save dialog completes, calls NSApp.reply(toApplicationShouldTerminate: true)
- All failure paths also check and resolve pendingTerminate
- Fixes: 9-min recording lost because user closed app before async finishWriting completed
- setupAudioTap: log all tracks at start, KVO count changes
- findAudioTrackID: log when assetTrack is nil (HLS not loaded)
- Polling: add async asset.load(.tracks) fallback after 5s
- Extend timeout from 20s to 30s
- Status log every 5s during polling
- Replaced text buttons with SF Symbol icon buttons (28x28)
- All features: folder/prev/play/next/volume+slider/track/repeat/fullscreen/playlist
- Removed Spacer() to prevent buttons from being clipped
- Single compact row layout, no overflow
- Video fills entire window via VideoPlayerRepresentable
- MiniPlayerIcon in top-left corner toggles ControlToolbar visibility
- ControlToolbar slides up from bottom with animation
- Removed Bricks JSON UI dependency for main view
- All toolbar features preserved: open/prev/play/next/volume/tracks/repeat/fullscreen/playlist
- Added i18n JSON files (zh.json, en.json) with auto language detection
- Updated miniplayer.ui to use i18n keys for all button labels
- Added MiniPlayerIcon overlay in top-left corner of main window
- Load i18n translations on app startup based on system locale