Реалізація запису аудіо в мобільному застосунку
Диктофон, голос-повідомлення, запис інтерв'ю в журналістському застосунку — завдання одне, вимоги різні. Голос-повідомлення потрібно в AAC/M4A, 32 кбіт/с, маленький файл. Диктофон для подкастів — WAV або FLAC, 44100 Гц, без потерь. Починаємо з аудіосесії, інакше потім не розберемося з конфліктами.
Аудіосесія та категорії
iOS. AVAudioSession — центральний об'єкт управління. Для запису:
let session = AVAudioSession.sharedInstance()
try session.setCategory(.playAndRecord,
mode: .default,
options: [.defaultToSpeaker, .allowBluetooth])
try session.setActive(true)
.playAndRecord дозволяє одночасно відтворювати та писати. .allowBluetooth включає запис з AirPods. Без .defaultToSpeaker — звук при відтворенні йде в ухо, не в динамік.
Проблема, яка трапляється в production: інший застосунок (навігатор, музичний плеєр) перехоплює сесію. Підписуємось на AVAudioSession.interruptionNotification, при .began паузуємо запис, при .ended з .shouldResume — відновлюємо.
Android. AudioRecord для прямого доступу до PCM-даних. MediaRecorder — простіше, але менше контролю над форматом. Для більшості завдань MediaRecorder достатньо:
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC)
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
mediaRecorder.setAudioSamplingRate(44100)
mediaRecorder.setAudioEncodingBitRate(128000)
VOICE_COMMUNICATION замість MIC включає еховідлучення та шумоподавлення на рівні системи — корисно для голос-повідомлень.
Візуалізація рівня звука
Амплітудний хвильовий індикатор — користувач видит, що запис іде. На iOS: AVAudioRecorder.averagePower(forChannel: 0) повертає дБ (від -160 до 0). Нормалізуємо в 0..1:
let power = recorder.averagePower(forChannel: 0)
let level = pow(10, power / 20) // dBFS → linear
Опитуємо через Timer.scheduledTimer(withTimeInterval: 0.05) — 20 fps для плавної анімації.
На Android: MediaRecorder.getMaxAmplitude() — максимальна амплітуда з останнього виклику (0–32767). Робимо Handler.postDelayed кожні 50 мс.
Для Flutter: record (pub.dev) надає onAmplitudeChanged stream.
Waveform при відтворенні
Записаний файл аналізуємо офлайн: читаємо PCM-сэмплі, розбиваємо на чанки по N сэмплів, беремо RMS кожного чанка. Отримуємо масив float-значень — малюємо через Canvas або Path. На iOS потрібен AVAssetReader + AVAssetReaderTrackOutput з kAudioFormatLinearPCM.
Формати та сумісність
| Формат | Розмір 1 хв | Сумісність | Застосування |
|---|---|---|---|
| AAC (M4A) | ~480 КБ | iOS, Android, Web | голос-повідомлення |
| MP3 | ~960 КБ | везде | загальний випадок |
| WAV (PCM) | ~10 МБ | везде | без потерь, диктофон |
| FLAC | ~3–5 МБ | Android native, iOS 11+ | без потерь, компактніше |
| OGG/Opus | ~300 КБ | Android, Web | оптимальний для VoIP |
Фоновий запис
Якщо користувач сворачує застосунок — запис повинен продовжуватися. iOS: UIBackgroundModes: audio в Info.plist, AVAudioSession залишається активною. Android: ForegroundService з типом microphone (android:foregroundServiceType="microphone" у маніфесті, обов'язково з Android 10). Сповіщення з кнопкою «Зупинити» — стандарт.
Без ForegroundService на Android 9+ система убьє процес через кілька хвилин у фоні. На iOS без UIBackgroundModes запис зупиниться через 3 секунди після сворачування.
Орієнтири за часом
Запис із візуалізацією рівня та збереженням файлу — 1–2 дні. Повнофункціональний диктофон із waveform, паузою, переименуванням та фоновим записом — 3–4 дні.







