Реалізація мультимодального AI-введення (текст + аудіо) у мобільному додатку
Голосові повідомлення в месенджерах—звичайна річ. Але коли користувач хоче не просто розшифровку, а смисловой ответ на сказане—потрібна з'язка: захоплення аудіо → транскрипція (або нативний аудіо-вводу в модель) → LLM з контекстом. Три різних технічних шари, у кожного свої підводні каміння.
Два архітектурні шляхи
Шлях 1: STT → LLM. Whisper API або аналоги перетворюють аудіо на текст, текст йде в messages[]. Працює з будь-якою LLM, дешево, передбачувано. Проблема—подвійна затримка: чекаємо транскрипцію (1–3 с для 30-секундного фрагмента), потім ответ моделі. Користувач дивиться 5–10 секунд.
Шлях 2: нативний аудіо-вводу. GPT-4o Audio Preview, Gemini 1.5 Pro приймають input_audio прямо у content[]. Менша затримка, модель «чує» інтонацію, паузи, акцент. Обмеження—формат: OpenAI вимагає PCM16 або MP3, Gemini—FLAC, MP3, WAV, OGG. На пристрої потрібна конвертація.
Вибір залежить від завдання. Голосовой асистент для спілкування—шлях 2. Транскрипція совещань з подальшим аналізом—шлях 1 з батч-обробкою.
Запис аудіо: звідки приходять баги
На Android захоплення через MediaRecorder простою, але AudioRecord потрібна, коли потрібен PCM у реальному часі (потік до Whisper через WebSocket). MediaRecorder зберігає у файл—зручно для коротких голосових, незручно для живого потока. Типичний краш: IllegalStateException: start called in invalid state—виклик start() до prepare() або повторний start() без reset(). Не забувайте звільняти у onPause(), інакше інші додатки втратять мікрофон.
На iOS: AVAudioEngine для PCM-потока, AVAudioRecorder для файлів. Проблема, яку всі зустрічають—AVAudioSession конфігурація. Якщо не виставити .record категорію до старту, запис або тиха, або йде через динамік замість мікрофона. А з iOS 17 потрібна NSMicrophoneUsageDescription навіть для симулятора.
Формат за замовчуванням у AVAudioRecorder—CAF. Whisper його не приймає. Потрібно либо конвертувати через AVAssetExportSession (асинхронно, додає затримку), либо одразу настроїти AVAudioRecorder на M4A/FLAC.
Реалізація потокового STT
Для живої розшифровки (користувач говорить—текст з'являється на екрані), використовуємо WebSocket до Whisper Streaming або Deepgram. На Android:
val audioRecord = AudioRecord(
MediaRecorder.AudioSource.MIC,
16000, // 16kHz—оптимум для Whisper
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize
)
// чанки по 100мс → WebSocket → partial transcripts
Частота дискретизації 16 кГц достатня для мови і вдвічі менше даних, ніж 44.1 кГц. На iOS еквівалент—AVAudioEngine з installTap(onBus:).
Важливо: WebSocket потрібно переоткривати при втраті мережі. OkHttp WebSocket на Android має коллбек onFailure—реалізуйте exponential backoff з максимум 3 спробами, інакше користувач не зрозуміє, що з'єднання оборвалось.
Передача аудіо-файлу у мультимодальну модель
// iOS—відправка аудіо у GPT-4o Audio
let audioData = try Data(contentsOf: recordingURL)
let b64 = audioData.base64EncodedString()
let payload: [String: Any] = [
"model": "gpt-4o-audio-preview",
"messages": [[
"role": "user",
"content": [
["type": "text", "text": userText],
["type": "input_audio", "input_audio": [
"data": b64,
"format": "mp3"
]]
]
]]
]
Ліміт на розмір аудіо у OpenAI—25 МБ. 30-хвилинна запис у MP3 128kbps займає ~28 МБ—не влізає. Для довгого контенту потрібна нарізка на чанки по 10–15 хвилин або передопрацювання Whisper.
Етапи та сроки
Аудит вимог (потік vs файл, провайдер, цільові платформи) → вибір архітектури → реалізація захопу та конвертації → інтеграція STT/мультимодального API → потоковой UI → тестування на реальних пристроях (різні мікрофони, фоновий шум, навушники) → публікація.
MVP з записом та Whisper—1–2 тижні. Повна реалізація з потоком, нативним аудіо-вводом та обробкою довгих записів—3–5 тижнів.







