Skip to content

AudioService Recording Flow

tomchoi edited this page Mar 24, 2026 · 1 revision

🎙️ AudioService Recording Flow

현재 AudioServiceAVAudioEngine이 아니라 AVAudioRecorder + metering 기반입니다. 그래서 이전보다 구조가 훨씬 단순합니다.

핵심은 딱 두 가지입니다.

  • AVAudioRecorder가 실제 파일을 저장한다
  • 별도 Task가 meter 값을 읽어 거친 Waveform을 만들어 UI로 보낸다

1. 한눈에 보기

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 반환]
Loading

2. 내부 구성

classDiagram
    class AudioService {
        -recorder
        -recordingFilePath
        -recordingCreatedAt
        -waveformContinuation
        -waveformTask
        -isPaused
        -isFinishing
        +startRecording()
        +pauseRecording()
        +resumeRecording()
        +finishRecording()
    }
Loading

예전처럼 파일 저장용 상태와 버퍼 처리 파이프라인이 따로 있지 않습니다. 지금은 actor 안의 몇 개 상태 변수만 보면 됩니다.


3. 시작 흐름

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>
Loading

4. 녹음 중 흐름

녹음 중에는 AVAudioRecorder가 파일을 알아서 쓰고, AudioService는 meter 값만 읽어서 대충 파형을 만듭니다.

flowchart LR
    REC[AVAudioRecorder recording] --> FILE[m4a 파일 저장]
    REC --> METER[updateMeters]
    METER --> LEVEL[averagePower 읽기]
    LEVEL --> WF[Waveform 생성]
    WF --> UI[AsyncStream으로 UI 전달]
Loading

즉:

  • 실제 오디오는 AVAudioRecorder가 저장
  • 파형은 averagePower 기반의 단순한 시각화용 값

5. 파형 생성 루프

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
Loading

포인트는 이겁니다.

  • 파형은 실시간 오디오 샘플이 아니라 100ms마다 읽는 meter 값입니다.
  • 그래서 정밀한 파형은 아니지만 구조는 매우 단순합니다.

6. 상태 전이

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: 파일 삭제 완료
Loading

7. 종료 흐름

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[반환]
Loading

정상 종료에서는 파일을 삭제하지 않습니다.


8. 폐기 흐름

아래 경우엔 현재 녹음을 폐기합니다.

  • 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 비활성화]
Loading

9. 파일 저장 방식

현재 저장 방식은 다음과 같습니다.

  • 디렉터리: URL.applicationSupportDirectory
  • 파일명: Date.now 기반 밀리초 timestamp
  • 확장자: .m4a
  • 인코딩: AAC, 44.1kHz, mono, 64kbps

예시:

  • 1742801485123.m4a

10. 메서드별 책임

메서드 역할
startRecording() 세션 활성화, recorder 생성, recording 시작, waveform task 시작
pauseRecording() recorder pause
resumeRecording() 세션 재활성화 후 recorder resume
finishRecording() 정상 종료 후 RecordedAudio 반환
streamWaveform() meter 값을 읽어 Waveform 생성
discardCurrentRecording() 현재 파일을 버리고 정리

11. 정말 짧게 요약

flowchart LR
    MIC[Microphone] --> REC[AVAudioRecorder]
    REC --> FILE[m4a file]
    REC --> METER[meter value]
    METER --> WAVE[rough waveform]
    WAVE --> UI
Loading

이제 AudioService는 "파일 저장기 + meter 폴링기" 정도로 이해하면 거의 맞습니다.

Clone this wiki locally