Интеграция OpenAI TTS для генерации речи в мобильном приложении

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.
Разработка и поддержка любых видов мобильных приложений:
Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Предлагаемые услуги
Показано 1 из 1 услугВсе 1735 услуг
Интеграция OpenAI TTS для генерации речи в мобильном приложении
Простая
от 1 рабочего дня до 3 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1054
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    445

Интеграция OpenAI TTS для генерации речи в мобильном приложении

OpenAI TTS — самый простой в интеграции провайдер синтеза речи с хорошим качеством. Один эндпоинт, шесть голосов, два формата запроса (REST и streaming), поддержка 57 языков. Главный нюанс — правильно организовать кэширование и стриминговое воспроизведение, иначе задержка 1–3 секунды перед началом речи будет раздражать пользователей.

API и параметры

POST https://api.openai.com/v1/audio/speech
Authorization: Bearer {api_key}
Content-Type: application/json

{
  "model": "tts-1-hd",
  "input": "Ваш текст здесь",
  "voice": "nova",
  "response_format": "mp3",
  "speed": 1.0
}

Модели:

  • tts-1 — быстрее, чуть хуже качество, дешевле ($15/млн символов)
  • tts-1-hd — выше качество, медленнее на ~30%, дороже ($30/млн символов)

Голоса: alloy (нейтральный), echo (мужской мягкий), fable (британский), onyx (мужской глубокий), nova (женский живой), shimmer (женский спокойный).

Для русского языка nova и shimmer звучат наиболее естественно.

speed: 0.25–4.0. Дефолт 1.0. Значение выше 1.3 начинает ломать просодию.

Реализация без стриминга (для коротких текстов)

// iOS: загрузка и воспроизведение
func speak(text: String, voice: String = "nova") async throws {
    var request = URLRequest(url: URL(string: "https://api.openai.com/v1/audio/speech")!)
    request.httpMethod = "POST"
    request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")

    let body = TTSSpeechRequest(model: "tts-1", input: text, voice: voice, responseFormat: "mp3")
    request.httpBody = try JSONEncoder().encode(body)

    let (data, _) = try await URLSession.shared.data(for: request)
    audioPlayer = try AVAudioPlayer(data: data)
    audioPlayer?.play()
}

Для коротких фраз (до 100 символов) на tts-1 задержка ~300–500 мс — приемлемо без стриминга. Для длинных текстов нужен стриминг.

Стриминговое воспроизведение на Android

class OpenAITTSStreamer(private val apiKey: String, private val context: Context) {
    private val exoPlayer = ExoPlayer.Builder(context).build()

    fun speak(text: String, voice: String = "nova") {
        val requestBody = JSONObject().apply {
            put("model", "tts-1")
            put("input", text)
            put("voice", voice)
            put("response_format", "mp3")
        }.toString().toRequestBody("application/json".toMediaType())

        // Используем OkHttp как DataSource через кастомный MediaSource
        val call = OkHttpClient().newCall(
            Request.Builder()
                .url("https://api.openai.com/v1/audio/speech")
                .header("Authorization", "Bearer $apiKey")
                .post(requestBody)
                .build()
        )

        call.enqueue(object : Callback {
            override fun onResponse(call: Call, response: Response) {
                // Пишем поток во временный файл, одновременно начинаем воспроизведение
                val tempFile = File(context.cacheDir, "tts_${System.currentTimeMillis()}.mp3")
                response.body!!.byteStream().use { input ->
                    tempFile.outputStream().use { output ->
                        val buffer = ByteArray(8192)
                        var bytes: Int
                        var firstChunk = true
                        while (input.read(buffer).also { bytes = it } != -1) {
                            output.write(buffer, 0, bytes)
                            if (firstChunk && tempFile.length() > 32768) {
                                firstChunk = false
                                // Начинаем воспроизведение после первых 32 KB
                                Handler(Looper.getMainLooper()).post {
                                    exoPlayer.setMediaItem(MediaItem.fromUri(tempFile.toUri()))
                                    exoPlayer.prepare()
                                    exoPlayer.play()
                                }
                            }
                        }
                    }
                }
            }
            override fun onFailure(call: Call, e: IOException) { /* обработка ошибки */ }
        })
    }
}

ExoPlayer поддерживает воспроизведение из файла, который ещё пишется — ProgressiveMediaSource читает данные по мере их поступления. Задержка до первого звука — 400–700 мс.

Кэш

// iOS: кэш синтезированного аудио
class TTSCache {
    private let cacheURL: URL

    init() {
        cacheURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
            .appendingPathComponent("tts_cache")
        try? FileManager.default.createDirectory(at: cacheURL, withIntermediateDirectories: true)
    }

    func key(text: String, voice: String) -> String {
        let input = "\(text)|\(voice)"
        return SHA256.hash(data: Data(input.utf8)).hexString
    }

    func get(_ key: String) -> Data? {
        let url = cacheURL.appendingPathComponent(key + ".mp3")
        return try? Data(contentsOf: url)
    }

    func set(_ key: String, data: Data) {
        let url = cacheURL.appendingPathComponent(key + ".mp3")
        try? data.write(to: url)
    }
}

Перед каждым TTS-запросом — проверка кэша. Попадание в кэш = мгновенное воспроизведение. Для UI-фраз приложения (приветствие, подсказки) — предгенерируй аудио при первом запуске и кэшируй навсегда.

Обработка длинных текстов

OpenAI TTS принимает до 4096 символов за запрос. Для длинных текстов — разбивка по предложениям:

func splitBySentences(_ text: String, maxLength: Int = 1000) -> [String] {
    var chunks: [String] = []
    var current = ""
    for sentence in text.components(separatedBy: CharacterSet(charactersIn: ".!?\n")) {
        let trimmed = sentence.trimmingCharacters(in: .whitespaces)
        if trimmed.isEmpty { continue }
        if current.count + trimmed.count > maxLength {
            if !current.isEmpty { chunks.append(current) }
            current = trimmed
        } else {
            current += (current.isEmpty ? "" : ". ") + trimmed
        }
    }
    if !current.isEmpty { chunks.append(current) }
    return chunks
}

Куски синтезируем параллельно через TaskGroup, воспроизводим последовательно — так общая задержка меньше, чем при последовательной обработке.

Сроки

REST-интеграция с кэшем на одной платформе — 3–4 дня. Стриминговое воспроизведение + разбивка длинных текстов + UI управления голосом — 7–10 дней.