-
Notifications
You must be signed in to change notification settings - Fork 0
AudioService Recording Flow
tomchoi edited this page Mar 24, 2026
·
1 revision
현재 AudioService는 AVAudioEngine이 아니라 AVAudioRecorder + metering 기반입니다.
그래서 이전보다 구조가 훨씬 단순합니다.
핵심은 딱 두 가지입니다.
-
AVAudioRecorder가 실제 파일을 저장한다 - 별도
Task가 meter 값을 읽어 거친Waveform을 만들어 UI로 보낸다
flowchart TD
A[startRecording 호출] --> B{이미 녹음 중?}
B -- Yes --> E1[alreadyRecording]
B -- No --> C{권한 granted?}
C -- No --> E2[startFailed]
C -- Yes --> D[AVAudioSession 활성화]
D --> F[AVAudioRecorder 생성]
F --> G[waveformStream 생성]
G --> H[recorder.record]
H --> I[actor 내부 상태 저장]
I --> J[waveform polling Task 시작]
J --> K[waveformStream 반환]
classDiagram
class AudioService {
-recorder
-recordingFilePath
-recordingCreatedAt
-waveformContinuation
-waveformTask
-isPaused
-isFinishing
+startRecording()
+pauseRecording()
+resumeRecording()
+finishRecording()
}
예전처럼 파일 저장용 상태와 버퍼 처리 파이프라인이 따로 있지 않습니다. 지금은 actor 안의 몇 개 상태 변수만 보면 됩니다.
sequenceDiagram
participant Caller
participant AudioService
participant Session as AVAudioSession
participant Recorder as AVAudioRecorder
participant Task as Waveform Task
Caller->>AudioService: startRecording()
AudioService->>Session: activateSession()
AudioService->>Recorder: makeRecorder()
AudioService->>Recorder: record()
AudioService->>AudioService: recorder / filePath / createdAt 저장
AudioService->>Task: streamWaveform() 시작
AudioService-->>Caller: AsyncStream<Waveform>
녹음 중에는 AVAudioRecorder가 파일을 알아서 쓰고,
AudioService는 meter 값만 읽어서 대충 파형을 만듭니다.
flowchart LR
REC[AVAudioRecorder recording] --> FILE[m4a 파일 저장]
REC --> METER[updateMeters]
METER --> LEVEL[averagePower 읽기]
LEVEL --> WF[Waveform 생성]
WF --> UI[AsyncStream으로 UI 전달]
즉:
- 실제 오디오는
AVAudioRecorder가 저장 - 파형은
averagePower기반의 단순한 시각화용 값
flowchart TD
A[waveformTask loop] --> B{취소됨?}
B -- Yes --> Z[종료]
B -- No --> C{isFinishing?}
C -- Yes --> Z
C -- No --> D{isPaused?}
D -- Yes --> S[sleep 100ms]
S --> A
D -- No --> E{recorder.isRecording?}
E -- No --> F[예상치 못한 중단 처리]
E -- Yes --> G[updateMeters]
G --> H[averagePower -> 0...1 정규화]
H --> I[Waveform yield]
I --> J[sleep 100ms]
J --> A
포인트는 이겁니다.
- 파형은 실시간 오디오 샘플이 아니라
100ms마다 읽는 meter 값입니다. - 그래서 정밀한 파형은 아니지만 구조는 매우 단순합니다.
stateDiagram-v2
[*] --> Idle
Idle --> Recording: startRecording
Recording --> Paused: pauseRecording
Paused --> Recording: resumeRecording
Recording --> Finishing: finishRecording
Paused --> Finishing: finishRecording
Recording --> Discarded: waveform stream 종료 / 예기치 않은 중단
Finishing --> Idle: 종료 완료
Discarded --> Idle: 파일 삭제 완료
flowchart TD
A[finishRecording 호출] --> B{recorder / filePath / createdAt 존재?}
B -- No --> E1[notRecording]
B -- Yes --> C[isFinishing = true]
C --> D[waveformStream finish]
D --> E[recorder.stop]
E --> F[waveformTask 종료 대기]
F --> G[파일 duration 계산]
G --> H[RecordedAudio 생성]
H --> I[session clear]
I --> J[AVAudioSession 비활성화]
J --> K[반환]
정상 종료에서는 파일을 삭제하지 않습니다.
아래 경우엔 현재 녹음을 폐기합니다.
-
waveformStream이 외부에서 종료된 경우 -
AVAudioRecorder가 예상치 못하게 멈춘 경우
flowchart TD
A[discardCurrentRecording] --> B[waveformStream finish]
B --> C[recorder.stop]
C --> D[waveformTask 종료]
D --> E[filePath 보관]
E --> F[내부 상태 clear]
F --> G[파일 삭제]
G --> H[AVAudioSession 비활성화]
현재 저장 방식은 다음과 같습니다.
- 디렉터리:
URL.applicationSupportDirectory - 파일명:
Date.now기반 밀리초 timestamp - 확장자:
.m4a - 인코딩:
AAC,44.1kHz,mono,64kbps
예시:
1742801485123.m4a
| 메서드 | 역할 |
|---|---|
startRecording() |
세션 활성화, recorder 생성, recording 시작, waveform task 시작 |
pauseRecording() |
recorder pause |
resumeRecording() |
세션 재활성화 후 recorder resume |
finishRecording() |
정상 종료 후 RecordedAudio 반환 |
streamWaveform() |
meter 값을 읽어 Waveform 생성 |
discardCurrentRecording() |
현재 파일을 버리고 정리 |
flowchart LR
MIC[Microphone] --> REC[AVAudioRecorder]
REC --> FILE[m4a file]
REC --> METER[meter value]
METER --> WAVE[rough waveform]
WAVE --> UI
이제 AudioService는 "파일 저장기 + meter 폴링기" 정도로 이해하면 거의 맞습니다.
- 도메인-레이어-파라미터-설계:-URL-vs-Entity
- 도메인-에러-설계:-유스케이스별-vs-도메인(기능)별
- Swift-6.0-Typed-Throws-Guideline
- CoreData-loadPersistentStores-비동기-처리-전략
- stateless-Infrastructure-서비스의-struct-전환
- STT-Repository-동시-요청-큐잉-전환
- Coordinator-클래스-기반에서-프로토콜-기반으로-마이그레이션