diff --git a/CHANGELOG.md b/CHANGELOG.md index f7ac03a..cb66222 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to DirectPipe will be documented in this file. --- +## [Unreleased] + +### Changed +- **Auto-start label (cross-platform UI consistency)**: Unified tray menu and Settings toggle to use a single platform label source via `Platform::getAutoStartLabel()` (`Open at Login` on macOS, `Start with System` on others), removing Windows-specific hardcoded wording from the Settings toggle path. + +--- + ## [4.0.2] - 2026-03-19 ### Added diff --git a/README.md b/README.md index 7a15bef..c386f59 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,11 @@ **스트리머, 팟캐스터, 게이머, 보이스챗 사용자를 위한** 크로스 플랫폼 실시간 마이크 프로세서. -USB 마이크에 노이즈 제거, EQ, 컴프레서 등 VST 플러그인을 걸어 실시간으로 처리하고, Discord · Zoom · OBS 등에 깨끗한 음성을 바로 전달한다. **VST 플러그인을 모르셔도 [Auto] 버튼 하나로 AI 노이즈 제거 + 자동 볼륨 조절 + 저주파 필터가 즉시 적용됩니다.** 방송 중에도 Stream Deck 버튼 하나로 이펙트 전환, 볼륨 조절, 뮤트가 가능하며, 게임 중에는 단축키로, MIDI 컨트롤러로도 조작할 수 있다. DAW 없이도 전문적인 마이크 세팅을 간편하게 구성하고, 상황별 프리셋(A~E)으로 즉시 전환할 수 있다. +USB 마이크에 노이즈 제거, EQ, 컴프레서 등 VST 플러그인을 걸어 실시간으로 처리하고, Discord · Zoom · OBS 등에 깨끗한 음성을 바로 전달한다. **VST 플러그인을 모르셔도 [Auto] 버튼 하나로 AI 노이즈 제거 + 자동 볼륨 조절 + 저주파 필터를 한 번에 추가할 수 있습니다.** 방송 중에도 Stream Deck 버튼 하나로 이펙트 전환, 볼륨 조절, 뮤트가 가능하며, 게임 중에는 단축키로, MIDI 컨트롤러로도 조작할 수 있다. DAW 없이도 전문적인 마이크 세팅을 간편하게 구성하고, 상황별 프리셋(A~E)으로 빠르게 전환할 수 있다. **Cross-platform real-time microphone processor for streamers, podcasters, gamers, and voice chat users.** -Apply VST plugins (noise removal, EQ, compressor, etc.) to your USB mic and deliver clean audio directly to Discord, Zoom, or OBS. **Don't know VST plugins? Just click the [Auto] button — AI noise removal + auto volume leveling + low-cut filter are applied instantly.** Switch effects, adjust volume, and mute with a single Stream Deck button while live — or use hotkeys during gameplay, MIDI controllers for hands-on mixing. Set up a professional mic chain without a DAW, and instantly switch between situation presets (A-E). +Apply VST plugins (noise removal, EQ, compressor, etc.) to your USB mic and deliver clean audio directly to Discord, Zoom, or OBS. **Don't know VST plugins? Just click the [Auto] button — AI noise removal + auto volume leveling + low-cut filter can be added in one step.** Switch effects, adjust volume, and mute with a single Stream Deck button while live — or use hotkeys during gameplay, MIDI controllers for hands-on mixing. Set up a professional mic chain without a DAW, and switch quickly between situation presets (A-E). > **Platform support**: Windows (WASAPI/ASIO), macOS (CoreAudio), Linux (ALSA/JACK). Windows는 안정 빌드, macOS는 베타, Linux는 실험적 지원. > Windows is the stable release. macOS is in beta. Linux support is experimental. @@ -73,7 +73,7 @@ Mute/preset switch with hotkeys (Ctrl+Shift) during gameplay. **Leave [Auto] on 1. **다운로드 & 실행** — [최신 버전 다운로드](https://github.com/LiveTrack-X/DirectPipe/releases/latest) → 압축 해제 → 실행 / [Download latest](https://github.com/LiveTrack-X/DirectPipe/releases/latest) → Extract → Run 2. **마이크 선택** — Audio 탭 → Input에서 USB 마이크 선택 / Audio tab → Select your USB mic as Input -3. **[Auto] 클릭** — 기본적인 마이크 보정(노이즈 제거 + 자동 볼륨 + 저주파 필터)이 즉시 적용! / Click [Auto] — Essential mic correction (noise removal + auto volume + low-cut filter) applied instantly! +3. **[Auto] 클릭** — 기본적인 마이크 보정(노이즈 제거 + 자동 볼륨 + 저주파 필터)을 한 번에 추가 / Click [Auto] — Add essential mic correction (noise removal + auto volume + low-cut filter) in one step 4. **끝!** 더 자세한 설정은 [Quick Start 가이드](docs/QUICKSTART.md) 참조 / **Done!** For detailed setup, see the [Quick Start guide](docs/QUICKSTART.md) --- @@ -88,7 +88,7 @@ Mute/preset switch with hotkeys (Ctrl+Shift) during gameplay. **Leave [Auto] on - **설치 불필요** — Windows: 단일 .exe, macOS: .app 번들, Linux: 단일 바이너리. 인스톨러 없음 — Windows: single .exe, macOS: .app bundle, Linux: single binary. No installer needed - **5종 외부 제어** — 핫키 · MIDI · Stream Deck · HTTP · WebSocket을 한 프로그램에서 — All 5 control methods in one app -- **프리셋 즉시 전환** — A-E 슬롯, 이름 지정, 끊김 없는 교체 — Named preset slots (A-E) with seamless switching +- **빠른 프리셋 전환** — A-E 슬롯, 이름 지정, 같은 체인은 즉시, 다른 체인은 비동기 로딩으로 짧은 갭 최소화 — Named preset slots (A-E) with fast switching: instant for same-chain changes, async loading for different chains - **VST 출력 (DirectPipe Receiver, VST2/VST3/AU)** — 가상 케이블 없이 OBS/DAW 직접 연결 — Direct OBS/DAW connection without virtual cables - **오픈소스** — GPL v3, 누구나 기여 가능 — Open source, community-driven @@ -168,7 +168,7 @@ External Control: - **실시간 레벨 미터** — 입력(좌) / 출력(우) RMS 미터, dB 로그 스케일 — Input/output RMS meters with dB log scale - **Safety Limiter** — VST 체인 이후 전역 피드포워드 리미터. 기본 ceiling -0.3 dBFS, 예기치 않은 클리핑 방지 — Global feed-forward limiter after VST chain. Default ceiling -0.3 dBFS, prevents unexpected clipping - **Built-in Processors** — Filter (HPF+LPF), Noise Removal (RNNoise AI), Auto Gain (LUFS AGC) — VST 플러그인과 함께 체인에 삽입 가능. [Auto] 버튼(입력 게인 옆 특수 프리셋 슬롯)으로 3개 모두 한 번에 추가 — Filter, Noise Removal (RNNoise AI), Auto Gain (LUFS AGC) insertable alongside VST plugins. [Auto] button (special preset slot next to input gain) adds all 3 at once -- **Clock Drift Compensation** — Bidirectional IPC drift handling with hysteresis dead-band for stable long-duration streaming (auto buffer management prevents clicks/pops) / 히스테리시스 데드 밴드를 포함한 양방향 IPC 클록 드리프트 보상으로 장시간 스트리밍 안정성 보장 (자동 버퍼 관리로 끊김/팝 방지) +- **Clock Drift Compensation** — Bidirectional IPC drift handling with hysteresis dead-band to improve long-duration streaming stability (auto buffer management helps reduce clicks/pops) / 히스테리시스 데드 밴드를 포함한 양방향 IPC 클록 드리프트 보상으로 장시간 스트리밍 안정성을 높이도록 설계됨 (자동 버퍼 관리로 끊김/팝 완화) ### 외부 제어 / External Control @@ -701,7 +701,7 @@ Professional low-latency driver. Requires native ASIO driver from your audio int --- -Plugin scanning runs in a **separate process**, so DirectPipe itself will never freeze or crash. Some plugins may take a while to scan (up to 5 minutes). +Plugin scanning runs in a **separate process**, so a plugin scan crash should not take down the main DirectPipe app. Some plugins may take a while to scan (up to 5 minutes). - Plugins that cause crashes are automatically **blacklisted** and skipped in future scans - Scan log: Windows `%AppData%/DirectPipe/scanner-log.txt`, macOS `~/Library/Application Support/DirectPipe/scanner-log.txt`, Linux `~/.config/DirectPipe/scanner-log.txt` @@ -724,7 +724,7 @@ Plugin scanning runs in a **separate process**, so DirectPipe itself will never **Quick Preset Slots (A–E):** - Save your current plugin chain and settings to slots **A through E** - Click a slot button **(A/B/C/D/E)** → saves current state if empty, loads slot if occupied -- If the plugins are the same, only parameters change (**instant switch**); different plugins use **async loading** (preloading cache enables seamless instant switching) +- If the plugins are the same, only parameters change (**near-instant switch**); different plugins use **async loading** (preloading cache can shorten the transition) - **Right-click** slot → **Rename** (e.g., `A|Game`), **Copy**, **Delete**, **Export/Import** (`.dppreset`) - Use **Save/Load** buttons to save/load presets as .dppreset files @@ -740,7 +740,7 @@ Example: Slot **A|Game** for gaming (noise removal only), Slot **B|Karaoke** for - Main Output과는 별도의 오디오 장치를 사용하므로 **독립적으로 동작** (Windows: WASAPI, macOS: CoreAudio) - **MON** 버튼으로 켜기/끄기 -> **지연(레이턴시) 참고**: 모니터 출력은 메인 오디오와 별도의 오디오 장치를 사용하기 때문에 **~15-20ms의 추가 지연**이 발생합니다. 이 지연은 듀얼 디바이스 구조의 한계입니다. 자기 목소리를 지연 없이 듣고 싶다면 **ASIO 드라이버 사용** (Windows, 입출력이 하나의 디바이스로 처리됨) 또는 오디오 인터페이스의 **하드웨어 다이렉트 모니터링** 기능을 권장합니다. +> **지연(레이턴시) 참고**: 모니터 출력은 메인 오디오와 별도의 오디오 장치를 사용하므로, 장치/드라이버/버퍼 설정에 따라 보통 **추가 지연이 느껴질 수 있습니다**. 많은 환경에서 대략 `~15-20ms` 수준이지만 시스템에 따라 달라질 수 있습니다. 가장 낮은 모니터 지연이 필요하다면 **ASIO 드라이버 사용** (Windows, 입출력이 하나의 디바이스로 처리됨) 또는 오디오 인터페이스의 **하드웨어 다이렉트 모니터링** 기능을 권장합니다. --- @@ -750,7 +750,7 @@ Example: Slot **A|Game** for gaming (noise removal only), Slot **B|Karaoke** for - Uses a separate audio device from the Main Output, so it **works independently** (Windows: WASAPI, macOS: CoreAudio) - Toggle on/off with the **MON** button -> **Latency note**: Monitor output uses a separate audio device, which adds **~15-20ms of extra latency** due to the dual-device architecture. For zero-latency monitoring, use an **ASIO driver** (Windows only, single device handles both input and output) or your audio interface's **hardware direct monitoring** feature. +> **Latency note**: Monitor output uses a separate audio device, so you will usually hear some extra latency depending on the device, driver, and buffer settings. In many setups this is roughly `~15-20ms`, but it can vary by system. For the lowest monitor latency, use an **ASIO driver** (Windows only, single device handles both input and output) or your audio interface's **hardware direct monitoring** feature.
@@ -823,7 +823,7 @@ Yes! Multiple control methods are available: **DirectPipe Receiver** is a plugin that lets **OBS, DAWs, and other hosts** receive DirectPipe's processed mic audio directly. Available in VST2, VST3, and AU formats (OBS only supports VST2). -Normally, to route DirectPipe's processed audio to OBS, you need a **virtual audio cable** (VB-Cable, BlackHole, etc.). With the DirectPipe Receiver, you can receive audio directly via shared memory (IPC) — **no virtual cable needed**, simpler setup, lower latency. +Normally, to route DirectPipe's processed audio to OBS, you need a **virtual audio cable** (VB-Cable, BlackHole, etc.). With the DirectPipe Receiver, you can receive audio directly via shared memory (IPC) — **no virtual cable needed**, usually simpler to set up, and often lower-latency or more predictable than virtual-cable routing depending on the host and driver setup. **DirectPipe Receiver vs. Virtual Cable 비교 / Comparison:** @@ -1090,15 +1090,15 @@ DirectPipe only supports **64-bit VST2/VST3 plugins**. 32-bit plugins won't appe
프리셋 전환할 때 소리가 잠깐 끊겨요 / Brief audio gap when switching presets -프리셋 전환 시 **~10-50ms의 매우 짧은 오디오 갭**이 발생할 수 있습니다. 이것은 **Keep-Old-Until-Ready** 메커니즘의 정상 동작입니다 — 새 플러그인 체인이 백그라운드에서 완전히 로드될 때까지 이전 체인이 계속 오디오를 처리하고, 준비가 되면 원자적으로 교체합니다. +프리셋 전환 시 **매우 짧은 오디오 갭**이 발생할 수 있습니다. 같은 체인 전환이나 캐시 히트 상황에서는 흔히 `~10-50ms` 수준이지만, 플러그인 구성과 시스템 상태에 따라 더 길어질 수 있습니다. 이것은 **Keep-Old-Until-Ready** 메커니즘의 정상 동작입니다 — 새 플러그인 체인이 백그라운드에서 완전히 로드될 때까지 이전 체인이 계속 오디오를 처리하고, 준비가 되면 원자적으로 교체합니다. -이 10-50ms 갭은 v3의 1-3초 무음 갭에서 크게 개선된 것입니다. 프리로드 캐시가 활성화되어 있으면 (다른 슬롯 플러그인을 미리 로드) 더 짧아질 수 있습니다. +이 짧은 갭은 v3의 1-3초 무음 갭에서 크게 개선된 것입니다. 프리로드 캐시가 활성화되어 있으면 (다른 슬롯 플러그인을 미리 로드) 더 짧아질 수 있습니다. --- -A **very brief audio gap (~10-50ms)** may occur during preset switches. This is normal **Keep-Old-Until-Ready** behavior — the old plugin chain continues processing audio while the new chain loads in the background, then swaps atomically when ready. +A **very brief audio gap** may occur during preset switches. In same-chain or cache-hit cases this is often around `~10-50ms`, but it can be longer depending on the plugin set and system state. This is normal **Keep-Old-Until-Ready** behavior — the old plugin chain continues processing audio while the new chain loads in the background, then swaps atomically when ready. -This 10-50ms gap is a major improvement over v3's 1-3 second mute gap. With the preload cache active (pre-loads other slots' plugins), the gap can be even shorter. +This short gap is a major improvement over v3's 1-3 second mute gap. With the preload cache active (pre-loads other slots' plugins), the gap can be even shorter.
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 71e1ca7..0ab5b13 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -48,11 +48,11 @@ JUCE 7.0.12 기반 데스크톱 앱. 메인 오디오 처리 엔진. #### Audio Module (`host/Source/Audio/`) / 오디오 모듈 -- **AudioEngine** — **Windows**: 5 driver types — DirectSound (legacy), Windows Audio (WASAPI Shared, recommended), Windows Audio (Low Latency) (IAudioClient3), Windows Audio (Exclusive Mode), ASIO. **macOS**: CoreAudio. **Linux**: ALSA, JACK. Manages the audio device callback. Pre-allocated work buffers (8ch). Mono mixing or stereo passthrough. Runtime device type switching, sample rate/buffer size queries. Input gain (atomic), master mute. Audio optimizations: `ScopedNoDenormals` (prevents CPU spikes from denormals in VST plugins), muted fast-path (skips VST chain when muted), RMS decimation (every 4th callback). Rolling 60-second XRun monitoring with atomic reset flag (`xrunResetRequested_`) for thread-safe device→message thread communication. XRun history persists through device restarts — display shows full 60s window regardless of device state changes. `setBufferSize` auto-fallback to closest device-supported size with notification. **Device auto-reconnection**: Dual mechanism — `ChangeListener` on `deviceManager_` for immediate detection + 3s timer polling fallback. Tracks `desiredInputDevice_`/`desiredOutputDevice_`. Preserves SR/BS/channel routing on reconnect. Per-direction loss: `inputDeviceLost_` zeroes input in audio callback, `outputAutoMuted_` auto-mutes/unmutes output. `reconnectMissCount_` accepts current devices after 5 failed attempts only for cross-driver stale name scenarios; when `outputAutoMuted_` is true (genuine device loss / physical unplug), the counter resets and keeps waiting indefinitely for the desired device. `setInputDevice`/`setOutputDevice` clear `deviceLost_`, `inputDeviceLost_`, `outputAutoMuted_`, and reconnection counters — allows users to manually select a different device during device loss without waiting for reconnection. **Driver type snapshot**: `DriverTypeSnapshot` saves per-driver settings (input/output device, SR, BS, `outputNone`) before type switch, restores when switching back. `outputNone_` cleared on driver type switch (prevents OUT mute lock after WASAPI "None" -> ASIO), restored from snapshot if the target driver had it saved. `ipcAllowed_` blocks IPC in audio-only multi-instance mode. Audio optimizations (`timeBeginPeriod`, Power Throttling disable, MMCSS "Pro Audio" thread registration at AVRT_PRIORITY_HIGH) are Windows-specific; macOS/Linux rely on JUCE defaults. **Output "None" mode**: `setOutputNone(bool)` / `isOutputNone()` — `outputNone_` atomic flag mutes output and locks OUT button (intentional "no output device" state, similar to panic mute lockout but for deliberate use). Cleared on driver type switch to prevent OUT button lock persisting across drivers. `DriverTypeSnapshot` saves/restores `outputNone` per driver type. **ASIO SR/BS policy**: ASIO devices own SR/BS globally (affects all apps sharing the device). On startup, DirectPipe does NOT force saved SR/BS on ASIO — instead accepts whatever the device currently reports via `syncDesiredFromDevice()`. Reason: forcing SR/BS would restart the ASIO driver, disrupting audio in DAWs, media players, and other apps. When the user changes BS from the ASIO control panel, `audioDeviceAboutToStart` syncs `desiredSR`/`desiredBS` from the device, and the new values are automatically saved to settings. WASAPI/CoreAudio/ALSA use per-app SR/BS, so saved values are safely forced on startup (no impact on other apps). **Startup flow**: Always opens WASAPI first (safe fallback), then loads saved driver type from settings and switches to ASIO if configured. The WASAPI→ASIO transition is invisible (~100ms, before window shown). Falls back to WASAPI if ASIO driver is unavailable. / Windows 5종 드라이버, macOS CoreAudio, Linux ALSA/JACK. 오디오 콜백 관리. 사전 할당 버퍼. Mono/Stereo 처리. 입력 게인, 마스터 뮤트, RMS 레벨 측정. **장치 자동 재연결**: 듀얼 감지 + 방향별 감지 (입력/출력 분리). `reconnectMissCount_`는 교차 드라이버 이름 불일치에만 폴백 적용; `outputAutoMuted_` true(물리적 분리)시 원하는 장치를 무기한 대기. `setInputDevice`/`setOutputDevice`는 장치 손실 중 수동 선택을 허용하기 위해 `deviceLost_` 및 재연결 카운터를 초기화. **드라이버 타입 스냅샷**: 타입 전환 시 설정 저장/복원 (`outputNone` 포함). `outputNone_`는 드라이버 전환 시 초기화, 스냅샷에서 복원. `ipcAllowed_`로 audio-only 모드에서 IPC 차단. **Output "None" 모드**: `setOutputNone(bool)` / `isOutputNone()` — `outputNone_` atomic 플래그로 출력 뮤트 + OUT 버튼 잠금 (의도적 "출력 장치 없음" 상태). 드라이버 전환 시 초기화, `DriverTypeSnapshot`으로 드라이버별 저장/복원. **ASIO SR/BS 정책**: ASIO 장치는 SR/BS를 전역으로 소유 (장치를 공유하는 모든 앱에 영향). 시작 시 저장된 SR/BS를 ASIO에 강제하지 않고, `syncDesiredFromDevice()`를 통해 장치가 보고하는 현재 값을 수용. 이유: SR/BS 강제 시 ASIO 드라이버 재시작 → DAW, 미디어 플레이어 등 다른 앱의 오디오 끊김. ASIO 컨트롤 패널에서 BS 변경 시 `audioDeviceAboutToStart`가 `desiredSR`/`desiredBS`를 장치에서 동기화하여 설정에 자동 반영. WASAPI/CoreAudio/ALSA는 앱별 SR/BS이므로 시작 시 저장된 값을 안전하게 강제 적용 (다른 앱에 영향 없음). **시작 흐름**: WASAPI로 먼저 시작 (안전한 폴백) → 설정 파일에서 저장된 드라이버 타입 로드 → ASIO 설정 시 전환 시도. WASAPI→ASIO 전환은 ~100ms, 창 표시 전에 완료. ASIO 드라이버 사용 불가 시 WASAPI에 남아있음. -- **VSTChain** — `AudioProcessorGraph`-based VST2/VST3 plugin chain. `rebuildGraph(bool suspend = true)` rebuilds connections — `suspend=true` (default) for node add/remove, `suspend=false` for bypass toggle (connection-only change, no audio gap). Bypassed plugins are disconnected from the signal chain in `rebuildGraph` (audio routes around them). `setPluginBypassed` syncs both `node->setBypassed()` and `getBypassParameter()->setValueNotifyingHost()` for plugins with internal bypass parameter (VST2 canDo("bypass"), VST3), then calls `rebuildGraph(false)`. Async chain replacement (`replaceChainAsync`) loads plugins on background thread with `alive_` flag (`shared_ptr>`) to guard `callAsync` completion callbacks against object destruction. **Keep-Old-Until-Ready**: old chain continues processing audio during background plugin loading; new chain swapped atomically on message thread when ready (~10-50ms suspend vs previous 1-3s mute gap). `asyncGeneration_` counter discards stale callAsync callbacks from superseded loads. Batch graph rebuild via `UpdateKind::async` for intermediate addNode/removeNode calls (N² → O(1) rebuild count). Editor windows tracked per-plugin. Pre-allocated MidiBuffer. `chainLock_` (mutable `CriticalSection`) protects ALL reader methods (`getPluginSlot`, `getPluginCount`, `setPluginBypassed`, parameter access, editor open/close) — not just writers. `prepared_` is `std::atomic` for RT-safe access. `processBlock` uses capacity guard instead of misleading buffer size check. `movePlugin` resizes `editorWindows_` before move to prevent out-of-bounds access. / VST2/VST3 플러그인 체인. **Keep-Old-Until-Ready**: 백그라운드 플러그인 로딩 중 이전 체인이 오디오 처리를 유지, 메시지 스레드에서 원자적 스왑 (~10-50ms 일시정지 vs 이전 1-3초 무음). `asyncGeneration_` 카운터로 대체된 로드의 stale callAsync 콜백 폐기. `UpdateKind::async`로 배치 그래프 리빌드. `alive_` 플래그(`shared_ptr>`)로 callAsync 콜백의 수명 안전 보장. MidiBuffer 사전 할당. `chainLock_` (mutable `CriticalSection`)이 모든 리더 메서드도 보호. `prepared_`는 `std::atomic`. `processBlock`은 용량 가드 사용. `movePlugin`은 이동 전 `editorWindows_` 크기 조정. Known limitation: bypassing a reverb/delay plugin immediately cuts its tail (graph disconnection). Future: consider dry-input routing while continuing processBlock for natural tail decay. / 알려진 제한사항: 리버브/딜레이 플러그인 바이패스 시 잔향 테일 즉시 절단 (그래프 연결 해제). 향후: processBlock 유지하면서 dry 입력 라우팅 검토. +- **AudioEngine** — **Windows**: 5 driver types — DirectSound (legacy), Windows Audio (WASAPI Shared, recommended), Windows Audio (Low Latency) (IAudioClient3), Windows Audio (Exclusive Mode), ASIO. **macOS**: CoreAudio. **Linux**: ALSA, JACK. Manages the audio device callback. Pre-allocated work buffers (8ch). Mono mixing or stereo passthrough. Runtime device type switching, sample rate/buffer size queries. Input gain (atomic), master mute. Audio optimizations: `ScopedNoDenormals` (prevents CPU spikes from denormals in VST plugins), muted fast-path (skips VST chain when muted), RMS decimation (every 4th callback). Rolling 60-second XRun monitoring with atomic reset flag (`xrunResetRequested_`) for thread-safe device→message thread communication. XRun history persists through device restarts — display shows full 60s window regardless of device state changes. `setBufferSize` auto-fallback to closest device-supported size with notification. **Device auto-reconnection**: Dual mechanism — `ChangeListener` on `deviceManager_` for immediate detection + 3s timer polling fallback. Tracks `desiredInputDevice_`/`desiredOutputDevice_`. Preserves SR/BS/channel routing on reconnect. Per-direction loss: `inputDeviceLost_` zeroes input in audio callback, `outputAutoMuted_` auto-mutes/unmutes output. `reconnectMissCount_` accepts current devices after 5 failed attempts only for cross-driver stale name scenarios; when `outputAutoMuted_` is true (genuine device loss / physical unplug), the counter resets and keeps waiting indefinitely for the desired device. `setInputDevice`/`setOutputDevice` clear `deviceLost_`, `inputDeviceLost_`, `outputAutoMuted_`, and reconnection counters — allows users to manually select a different device during device loss without waiting for reconnection. **Driver type snapshot**: `DriverTypeSnapshot` saves per-driver settings (input/output device, SR, BS, `outputNone`) before type switch, restores when switching back. `outputNone_` cleared on driver type switch (prevents OUT mute lock after WASAPI "None" -> ASIO), restored from snapshot if the target driver had it saved. `ipcAllowed_` blocks IPC in audio-only multi-instance mode. Audio optimizations (`timeBeginPeriod`, Power Throttling disable, MMCSS "Pro Audio" thread registration at AVRT_PRIORITY_HIGH) are Windows-specific; macOS/Linux rely on JUCE defaults. **Output "None" mode**: `setOutputNone(bool)` / `isOutputNone()` — `outputNone_` atomic flag mutes output and locks OUT button (intentional "no output device" state, similar to panic mute lockout but for deliberate use). Cleared on driver type switch to prevent OUT button lock persisting across drivers. `DriverTypeSnapshot` saves/restores `outputNone` per driver type. **ASIO SR/BS policy**: ASIO devices own SR/BS globally (affects all apps sharing the device). On startup, DirectPipe does NOT force saved SR/BS on ASIO — instead accepts whatever the device currently reports via `syncDesiredFromDevice()`. Reason: forcing SR/BS would restart the ASIO driver, disrupting audio in DAWs, media players, and other apps. When the user changes BS from the ASIO control panel, `audioDeviceAboutToStart` syncs `desiredSR`/`desiredBS` from the device, and the new values are automatically saved to settings. WASAPI/CoreAudio/ALSA use per-app SR/BS, so saved values are safely forced on startup (no impact on other apps). **Startup flow**: Always opens WASAPI first (safe fallback), then loads saved driver type from settings and switches to ASIO if configured. The WASAPI→ASIO transition typically completes before the window is shown (~100ms in common cases). Falls back to WASAPI if ASIO driver is unavailable. / Windows 5종 드라이버, macOS CoreAudio, Linux ALSA/JACK. 오디오 콜백 관리. 사전 할당 버퍼. Mono/Stereo 처리. 입력 게인, 마스터 뮤트, RMS 레벨 측정. **장치 자동 재연결**: 듀얼 감지 + 방향별 감지 (입력/출력 분리). `reconnectMissCount_`는 교차 드라이버 이름 불일치에만 폴백 적용; `outputAutoMuted_` true(물리적 분리)시 원하는 장치를 무기한 대기. `setInputDevice`/`setOutputDevice`는 장치 손실 중 수동 선택을 허용하기 위해 `deviceLost_` 및 재연결 카운터를 초기화. **드라이버 타입 스냅샷**: 타입 전환 시 설정 저장/복원 (`outputNone` 포함). `outputNone_`는 드라이버 전환 시 초기화, 스냅샷에서 복원. `ipcAllowed_`로 audio-only 모드에서 IPC 차단. **Output "None" 모드**: `setOutputNone(bool)` / `isOutputNone()` — `outputNone_` atomic 플래그로 출력 뮤트 + OUT 버튼 잠금 (의도적 "출력 장치 없음" 상태). 드라이버 전환 시 초기화, `DriverTypeSnapshot`으로 드라이버별 저장/복원. **ASIO SR/BS 정책**: ASIO 장치는 SR/BS를 전역으로 소유 (장치를 공유하는 모든 앱에 영향). 시작 시 저장된 SR/BS를 ASIO에 강제하지 않고, `syncDesiredFromDevice()`를 통해 장치가 보고하는 현재 값을 수용. 이유: SR/BS 강제 시 ASIO 드라이버 재시작 → DAW, 미디어 플레이어 등 다른 앱의 오디오 끊김. ASIO 컨트롤 패널에서 BS 변경 시 `audioDeviceAboutToStart`가 `desiredSR`/`desiredBS`를 장치에서 동기화하여 설정에 자동 반영. WASAPI/CoreAudio/ALSA는 앱별 SR/BS이므로 시작 시 저장된 값을 안전하게 강제 적용 (다른 앱에 영향 없음). **시작 흐름**: WASAPI로 먼저 시작 (안전한 폴백) → 설정 파일에서 저장된 드라이버 타입 로드 → ASIO 설정 시 전환 시도. WASAPI→ASIO 전환은 일반적으로 창 표시 전에 끝나지만, 시스템 환경에 따라 달라질 수 있음. ASIO 드라이버 사용 불가 시 WASAPI에 남아있음. +- **VSTChain** — `AudioProcessorGraph`-based VST2/VST3 plugin chain. `rebuildGraph(bool suspend = true)` rebuilds connections — `suspend=true` (default) for node add/remove, `suspend=false` for bypass toggle (connection-only change, avoids a full chain reload). Bypassed plugins are disconnected from the signal chain in `rebuildGraph` (audio routes around them). `setPluginBypassed` syncs both `node->setBypassed()` and `getBypassParameter()->setValueNotifyingHost()` for plugins with internal bypass parameter (VST2 canDo("bypass"), VST3), then calls `rebuildGraph(false)`. Async chain replacement (`replaceChainAsync`) loads plugins on background thread with `alive_` flag (`shared_ptr>`) to guard `callAsync` completion callbacks against object destruction. **Keep-Old-Until-Ready**: old chain continues processing audio during background plugin loading; new chain swapped atomically on message thread when ready (often around ~10-50ms under typical cache-hit or light-load conditions, vs previous 1-3s mute gap). `asyncGeneration_` counter discards stale callAsync callbacks from superseded loads. Batch graph rebuild via `UpdateKind::async` for intermediate addNode/removeNode calls (N² → O(1) rebuild count). Editor windows tracked per-plugin. Pre-allocated MidiBuffer. `chainLock_` (mutable `CriticalSection`) protects ALL reader methods (`getPluginSlot`, `getPluginCount`, `setPluginBypassed`, parameter access, editor open/close) — not just writers. `prepared_` is `std::atomic` for RT-safe access. `processBlock` uses capacity guard instead of misleading buffer size check. `movePlugin` resizes `editorWindows_` before move to prevent out-of-bounds access. / VST2/VST3 플러그인 체인. **Keep-Old-Until-Ready**: 백그라운드 플러그인 로딩 중 이전 체인이 오디오 처리를 유지, 메시지 스레드에서 원자적 스왑 (캐시 히트나 가벼운 로드 조건에서는 흔히 ~10-50ms 수준이지만 상황에 따라 달라질 수 있으며, 이전 1-3초 무음 대비 크게 개선). `asyncGeneration_` 카운터로 대체된 로드의 stale callAsync 콜백 폐기. `UpdateKind::async`로 배치 그래프 리빌드. `alive_` 플래그(`shared_ptr>`)로 callAsync 콜백의 수명 안전 보장. MidiBuffer 사전 할당. `chainLock_` (mutable `CriticalSection`)이 모든 리더 메서드도 보호. `prepared_`는 `std::atomic`. `processBlock`은 용량 가드 사용. `movePlugin`은 이동 전 `editorWindows_` 크기 조정. Known limitation: bypassing a reverb/delay plugin immediately cuts its tail (graph disconnection). Future: consider dry-input routing while continuing processBlock for natural tail decay. / 알려진 제한사항: 리버브/딜레이 플러그인 바이패스 시 잔향 테일 즉시 절단 (그래프 연결 해제). 향후: processBlock 유지하면서 dry 입력 라우팅 검토. - **OutputRouter** — Routes processed audio to the monitor output (separate audio device). Independent atomic volume and enable controls. Pre-allocated scaled buffer. `routeAudio()` clamps `numSamples` to `scaledBuffer_` capacity (prevents buffer overrun). Main output goes directly through outputChannelData. / 모니터 출력(별도 오디오 장치)으로 오디오 라우팅. `routeAudio()`가 `numSamples`를 `scaledBuffer_` 용량에 클램프 (버퍼 오버런 방지). 메인 출력은 outputChannelData로 직접 전송. - **MonitorOutput** — Second AudioDeviceManager used for the monitor output (WASAPI on Windows, CoreAudio on macOS, ALSA/JACK on Linux). Lock-free `AudioRingBuffer` bridge between two audio callback threads. Configured in Output tab. Status tracking (Active/Error/NotConfigured/SampleRateMismatch). Independent auto-reconnection via `monitorLost_` atomic + 3s timer polling. / 모니터 출력용 별도 AudioDeviceManager (Windows: WASAPI, macOS: CoreAudio, Linux: ALSA). 락프리 링버퍼 브리지. Output 탭에서 구성. 상태 추적. `monitorLost_` + 3초 타이머로 독립 자동 재연결. -- **PluginPreloadCache** — Background pre-loads other slots' plugin instances after slot switch. Cache hit = instant swap (~10-50ms vs 200-500ms DLL loading). Invalidated on SR/BS change, slot structure change (plugin names/paths/order via `isCachedWithStructure`), slot delete/copy. Per-slot version counter (`slotVersions_`) prevents stale preload: version captured at file-read time, checked before cache store — discards results if `invalidateSlot` was called mid-preload. Max 5 slots × ~4 plugins cached. / 슬롯 전환 후 다른 슬롯의 플러그인 인스턴스를 백그라운드 프리로드. 캐시 hit = 즉시 스왑. SR/BS 변경, 슬롯 구조 변경(플러그인 이름/경로/순서, `isCachedWithStructure`), 슬롯 삭제/복사 시 무효화. Per-slot 버전 카운터(`slotVersions_`)로 stale 프리로드 방지: 파일 읽기 시점에 버전 캡처, 캐시 저장 전 확인 — 프리로드 중 `invalidateSlot` 호출되면 결과 폐기. +- **PluginPreloadCache** — Background pre-loads other slots' plugin instances after slot switch. Cache hit = fast swap (often around ~10-50ms in typical cases, vs 200-500ms class DLL loading on cache miss). Invalidated on SR/BS change, slot structure change (plugin names/paths/order via `isCachedWithStructure`), slot delete/copy. Per-slot version counter (`slotVersions_`) prevents stale preload: version captured at file-read time, checked before cache store — discards results if `invalidateSlot` was called mid-preload. Max 5 slots × ~4 plugins cached. / 슬롯 전환 후 다른 슬롯의 플러그인 인스턴스를 백그라운드 프리로드. 캐시 hit = 빠른 스왑 (일반적인 경우 흔히 ~10-50ms 수준이지만, 캐시 미스나 플러그인 상태에 따라 더 길어질 수 있음). SR/BS 변경, 슬롯 구조 변경(플러그인 이름/경로/순서, `isCachedWithStructure`), 슬롯 삭제/복사 시 무효화. Per-slot 버전 카운터(`slotVersions_`)로 stale 프리로드 방지: 파일 읽기 시점에 버전 캡처, 캐시 저장 전 확인 — 프리로드 중 `invalidateSlot` 호출되면 결과 폐기. - **AudioRingBuffer** — Header-only SPSC lock-free ring buffer for inter-device audio transfer. `reset()` zeroes all channel data. / 디바이스 간 오디오 전송용 헤더 전용 SPSC 락프리 링 버퍼. `reset()`은 모든 채널 데이터를 0으로 초기화. - **LatencyMonitor** — High-resolution timer-based latency measurement. Callback overrun detection (`getCallbackOverrunCount()`) — processing time exceeding buffer period guarantees an audio glitch. / 고해상도 타이머 기반 레이턴시 측정. 콜백 오버런 감지 (`getCallbackOverrunCount()`) — 처리 시간이 버퍼 주기를 초과하면 오디오 글리치 발생. - **AudioRecorder** — Lock-free audio recording to WAV via `AudioFormatWriter::ThreadedWriter`. SpinLock-protected writer teardown for RT-safety. Timer-based duration tracking. Auto-stop on device change. `outputStream` properly deleted on writer creation failure (leak fix). / 락프리 WAV 녹음. SpinLock으로 RT 안전한 writer 해제. 장치 변경 시 자동 중지. writer 생성 실패 시 `outputStream` 올바르게 삭제 (누수 수정). @@ -218,8 +218,8 @@ Elgato Stream Deck plugin (Node.js, `@elgato/streamdeck` SDKVersion 3). / Stream - Chain-only: plugins, order, bypass state, plugin internal parameters / 체인 전용 - Audio/output settings are NOT affected by slot switching / 오디오/출력 설정 영향 없음 -- Same-chain fast path: bypass + state update only (instant) / 동일 체인: 즉시 전환 -- Different-chain: async background loading with **Keep-Old-Until-Ready** (old chain processes audio during load, atomic swap when ready — ~10-50ms suspend vs 1-3s mute gap). `asyncGeneration_` counter discards stale load callbacks. / 다른 체인: 비동기 로딩 + **Keep-Old-Until-Ready** (로딩 중 이전 체인이 오디오 처리 유지, 완료 시 원자적 스왑) +- Same-chain fast path: bypass + state update only (typically near-instant) / 동일 체인: 보통 거의 즉시 전환 +- Different-chain: async background loading with **Keep-Old-Until-Ready** (old chain processes audio during load, atomic swap when ready — often ~10-50ms in lighter/cache-hit cases, but varies by plugin set and system load). `asyncGeneration_` counter discards stale load callbacks. / 다른 체인: 비동기 로딩 + **Keep-Old-Until-Ready** (로딩 중 이전 체인이 오디오 처리 유지, 완료 시 원자적 스왑. 가벼운 로드나 캐시 히트 상황에서는 흔히 ~10-50ms 수준이지만, 플러그인 구성과 시스템 부하에 따라 달라짐) - Auto-save on any change (dirty-flag + 1s debounce), editor close, slot switch. Cache invalidated only on structure change (plugin names/paths/order via `isCachedWithStructure`), not on parameter-only saves / 변경 시 자동 저장. 구조 변경 시에만 캐시 무효화 (파라미터만 변경 시 유지) ### Plugin State Serialization / 플러그인 상태 직렬화 diff --git a/host/Source/Main.cpp b/host/Source/Main.cpp index f135bf9..f1bef4a 100644 --- a/host/Source/Main.cpp +++ b/host/Source/Main.cpp @@ -438,12 +438,7 @@ class DirectPipeApplication : public juce::JUCEApplication { menu.addItem(1, "Show Window"); menu.addSeparator(); if (directpipe::Platform::isAutoStartSupported()) { -#if JUCE_MAC - const char* autoStartLabel = "Open at Login"; -#else - const char* autoStartLabel = "Start with System"; -#endif - menu.addItem(3, autoStartLabel, + menu.addItem(3, directpipe::Platform::getAutoStartLabel(), true, directpipe::Platform::isAutoStartEnabled()); } menu.addSeparator(); diff --git a/host/Source/Platform/AutoStart.h b/host/Source/Platform/AutoStart.h index 771d82e..080b250 100644 --- a/host/Source/Platform/AutoStart.h +++ b/host/Source/Platform/AutoStart.h @@ -23,5 +23,15 @@ bool setAutoStartEnabled(bool enable); /** Whether this platform supports auto-start configuration. */ bool isAutoStartSupported(); +/** Platform-appropriate UI label for auto-start toggle/menu item. */ +inline const char* getAutoStartLabel() +{ +#if JUCE_MAC + return "Open at Login"; +#else + return "Start with System"; +#endif +} + } // namespace Platform } // namespace directpipe diff --git a/host/Source/UI/LogPanel.cpp b/host/Source/UI/LogPanel.cpp index 08b225c..61943ca 100644 --- a/host/Source/UI/LogPanel.cpp +++ b/host/Source/UI/LogPanel.cpp @@ -136,6 +136,7 @@ LogPanel::LogPanel() appHeaderLabel_.setColour(juce::Label::textColourId, juce::Colour(kDimTextColour)); addAndMakeVisible(appHeaderLabel_); + startupToggle_.setButtonText(Platform::getAutoStartLabel()); startupToggle_.setColour(juce::ToggleButton::textColourId, juce::Colour(kTextColour)); startupToggle_.setColour(juce::ToggleButton::tickColourId, juce::Colour(kAccentColour)); if (Platform::isAutoStartSupported()) { diff --git a/host/Source/UI/LogPanel.h b/host/Source/UI/LogPanel.h index 4ded17c..53dccef 100644 --- a/host/Source/UI/LogPanel.h +++ b/host/Source/UI/LogPanel.h @@ -91,13 +91,7 @@ class LogPanel : public juce::Component { private: // Application section juce::Label appHeaderLabel_{"", "Application"}; -#if JUCE_MAC - juce::ToggleButton startupToggle_{"Start at Login"}; -#elif JUCE_LINUX - juce::ToggleButton startupToggle_{"Start on Login"}; -#else - juce::ToggleButton startupToggle_{"Start with Windows"}; -#endif + juce::ToggleButton startupToggle_{"Start with System"}; juce::TextButton quitBtn_{"Quit"}; // Settings Export/Import section